23 Design Patterns (6) - Adapter Patterns

Adapter mode

1. Overview 
2. Use of adapter mode 
3. Role in Patterns 
4. Realization 
5. Trade-off between class adapter and object adapter 
6. Default adaptation mode    

1. Overview

The adapter pattern transforms the interface of one class into another that the client expects, so that two classes that could not work together because of interface mismatch can work together.

2. Use of adapter mode
 
That is, the Adapter pattern enables classes that could not work together due to incompatibility of interfaces to work together.
Here are two typical examples

3. Role in Patterns

3.1 Target: The interface the customer expects. Targets can be concrete or abstract classes or interfaces.
3.2 Adaptee: A class or adapter class that needs to be adapted.
3.3 Adapter: By wrapping an object that needs to be adapted, the original interface is converted to the target interface.   

4. Realization

(1) Class adapter pattern (implemented by inheritance)

Source code:

//Target interface, or standard interface 
public interface Target {
    //General function
    public void request();
}

//Existing classes with special functions that do not meet our existing standard interfaces  
public class Adaptee {
    public void specificRequest() {
        System.out.println("Adapted class...I am a two-hole socket with special functions.");
    }
}

//The adapter class inherits the adapted class and implements the standard interface
public class Adapter extends Adaptee implements Target  {
    @Override
    public void request() {
        System.out.println("I'm an adapter class. I can fit any two-hole socket to make it work properly.");
        this.specificRequest();
    }
 }


public class Client {
    public static void main(String[] args) {
         Target adapter = new Adapter(); 
         //Calling special functions through adapters
         adapter.request();  
    }
}

Result:
I'm an adapter class. I can fit any two-hole socket to make it work properly.
The adapted class... I am a two-hole socket with special functions.

(2) Object adapter (implemented by object combination)

Source code:
//Target interface, or standard interface 
public interface Target {
    //General function
    public void request();
}

//Existing classes with special functions that do not meet our existing standard interfaces  
public class Adaptee {
    public void specificRequest() {
        System.out.println("Adapted class...I am a two-hole socket with special functions.");
    }
} 

//Adapter class, which directly associates the adapted class and implements the standard interface  
class Adapter implements Target {
    // Direct Association of adapted classes
    private Adaptee adaptee;

    // Adapted class objects that need to be adapted can be passed in through constructors
    public Adapter(Adaptee adaptee) {
        this.adaptee = adaptee;
    }

    public void request() {
        // Here's how to use delegation to accomplish special functions
        System.out.println("I'm an adapter class. I can fit any two-hole socket to make it work properly.");
        this.adaptee.specificRequest();
    }
}

public class Client {
    public static void main(String[] args) {
        //Using special function classes, i.e. adapter classes,  
        // You need to first create an object of the adapted class as a parameter  
        Target adapter = new Adapter(new Adaptee());  
        adapter.request();  
    }
}

Result:
I'm an adapter class. I can fit any two-hole socket to make it work properly.
The adapted class... I am a two-hole socket with special functions.

From the class diagram, we also know that all we need to modify is the internal structure of the adapter class, that is, the adapter itself must first have an object of the adapter class, and then delegate specific special functions to this object to implement. Using the object adapter pattern, the Adapter class (adapter class) can be adapted to different adapted classes according to the incoming adapter object. Of course, we can extract an interface or abstract class for multiple adapted classes at this time. In this way, it seems that the object adapter pattern is more flexible.

5. The trade-off between class adapter and object adapter (from Daniel Blog)

Class adapters use object inheritance as a static way of definition, while object adapters use object composition as a dynamic way of composition.
  

For object adapters, an adapter can adapt multiple different sources to the same target. In other words, the same adapter can adapt both the source class and its subclasses to the target interface. Because the object adapter uses the relation of object combination, as long as the object type is correct, it doesn't matter whether it is a subclass or not.

For object adapters, it is difficult to redefine the behavior of Adaptee. In this case, we need to define a subclass of Adaptee to redefine, and then let the adapter combine subclasses. Although it is difficult to redefine the behavior of Adaptee, it is convenient to add some new behavior, and the new behavior can be applied to all sources at the same time.
  
For class adapters, only one object is introduced, and no additional references are needed to indirectly obtain the adapter.
  
For an object adapter, additional references are needed to indirectly obtain the adaee.
It is suggested that the implementation of object adapter should be used as far as possible, using synthesis/aggregation more and inheritance less. Of course, concrete analysis of specific issues, according to the needs to choose the implementation method, the most suitable is the best.
  

Advantages of adapter mode

Better reusability
The system needs to use existing classes, and such interfaces do not meet the needs of the system. Then these functions can be better reused through the adapter mode.
Better scalability
When realizing the function of adapter, we can call the function developed by ourselves, which naturally expands the function of the system.
  
Disadvantages of adapter mode

Too many adapters will make the system very messy and difficult to grasp as a whole. For example, clearly see that the call is interface A, in fact, the internal is adapted to the implementation of interface B, a system if too many such cases, is tantamount to a disaster. Therefore, if it is not necessary, the system can be reconstructed directly instead of using adapters.

6. Default adaptation mode

Default Adapter mode provides a default implementation for an interface so that subtypes can be extended from this default implementation rather than from the original interface. As a special case of the adapter pattern, the default is that the adaptation pattern has a special application in JAVA language.

It's an abstract class that empties all functions of a functional interface and then inherits the abstract class so that some functions can be implemented or expanded.

source code

Functional interface:

/**
 * Functional interface after falling in love has the following functions
 *  Hand in hand
 *  Kiss
 *  Watch movie
 *  Couple Dinner
 *  Go home and meet your parents
 * @author Administrator
 *
 */
public interface Love {
    //Hand-in-hand is a function that often occurs after a love affair.
    public void hand();

    //Kiss
    public void kiss();

    //Watch movie
    public void lookMovie();

    //Couple Dinner
    public void have();

    //Go home and meet your parents
    public void backHome();

    //Who is your girlfriend?
    public String getGirlFriend();

}

Default adapter class

/**
 * abstract class  
 * Communication class realizes love interface
 *  Ha-ha contacts more naturally talk about love? Hee-hee!
 *  But communication is not love after all. Although it has the embryonic form of love, it is not.
 *  So the empty realization of all the functions of love is only shelf.
 *  No, but there may be Gaga in the future.
 * @author Administrator
 *
 */
public abstract class Communication implements Love {
    //All empty implementations  
    @Override
    public void hand() {}

    @Override
    public void kiss() {}

    @Override
    public void lookMovie() {}

    @Override
    public void have() {}

    @Override
    public void backHome() {}

    @Override
    public String getGirlFriend() {
        return null;
    }

}

Specific realization

/**
 * The concrete class Liang Shanbo inherits the abstract class of Communication 
 * This type of Liangshanbo has slowly fallen in love from communication, so it has a love interface.
 * All the functions but some of them he can't call every day.
 * For example, watching movies, couples coming home for dinner to meet their parents, etc. 
 * (Because they are separated from each other, they can't meet every day, but they are as happy as New Year's Day when they meet.
 * They can invoke some of the functions of love.
 * For example, they can watch movies and have dinner together.
 * )
 * 
 * @author Administrator
 *
 */

public class LiangShanbo extends Communication {
        //Watch movie
        public void lookMovie(){
            System.out.println("Let's watch a movie together.");
        }

        //Couple Dinner
        public void have(){
            System.out.println("Let's have dinner together.");
        }

        //Who is your girlfriend?
        public String getGirlFriend(){
            return "ZhuYingtai";
        }
}

Client:

public class Client {
    public static void main(String[] args) {

        Love lover = new LiangShanbo();
        lover.lookMovie();
        lover.have();

        System.out.println("Girlfriend is :"+lover.getGirlFriend());
    }
}

Result:
Let's watch a movie together.
Let's have dinner together.
Girlfriend: Zhu Yingtai

In many cases, a specific class must implement an interface, but this class does not use all the methods specified by the interface. The usual approach is to implement all the methods in this specific class, those useful methods and those useless methods in an empty and mediocre way.

These empty methods are wasteful and sometimes confusing. Unless you look at the code for these empty methods, programmers may think that they are not empty. Even if he knows that some of these methods are empty, he does not necessarily know which methods are empty and which methods are not, unless he has seen the source code or documentation of these methods.

The default adaptation pattern handles this situation well. An Abstract adapter class implementation interface can be designed, which provides an empty method for each method required by the interface. Like Tang Gao, he only realizes some functions because he only uses some of them. If he wants to use them later, he can realize other functions. But today he doesn't need them, he doesn't need them anymore! ___________

The purpose of the adapter pattern is to change the source interface so that the target interface is compatible. The purpose of default adapter is slightly different. It is a mediocre implementation provided to facilitate the creation of an unequal adapter class.

At any time, if you are not ready to implement all the methods of an interface, you can use the "default adaptation pattern" to create an abstract class, giving a mediocre concrete implementation of all the methods. In this way, subclasses that inherit from this abstract class do not have to implement all the methods.

Keywords: socket less Java

Added by jrolands on Sat, 15 Jun 2019 04:41:02 +0300