Design Patterns_Structural Patterns-《Adapter Patterns》

Design Patterns_Structural Patterns-《Adapter Patterns》

The notes are organized from the detailed explanation of Java design patterns by dark horse programmers, 23 Java design patterns (diagram + framework source code analysis + actual combat)

overview

If you travel to European countries, their sockets are the far left of the picture below, which is the European standard. And the plug we use is the one on the far right in the picture below. Therefore, our laptops and mobile phones cannot be directly charged locally. So we need a socket converter, the first side of the converter is plugged into the local socket, and the second side is for us to charge, so that our plug can be used locally. There are many such examples in life, such as mobile phone chargers (converting 220v to 5v voltage), card readers, etc., are actually using the adapter mode.

image-20230110124752914

definition

  • Convert the interface of a class to another interface that the client wants, so that those classes that could not work together due to incompatible interfaces can work together.
  • The adapter mode is divided into 类适配器模式and 对象适配器模式, the coupling between the former class is higher than the latter, and requires the programmer to understand the internal structure of the relevant components in the existing component library, so the application is relatively less.

structure

The Adapter Pattern includes the following main roles:

  • Target (Target) interface: the interface expected by the current system business, which can be an abstract class or an interface.
  • Adaptee class: It is the component interface in the existing component library to be accessed and adapted.
  • Adapter (Adapter) class: It is a converter that converts the adapter interface into the target interface by inheriting or referencing the adapter object, allowing customers to access the adapter in the format of the target interface.

class adapter pattern

Implementation method: define an adapter class to realize the business interface of the current system, and at the same time inherit the existing components in the existing component library.

[Example] card reader

An existing computer can only read the SD card, but if you want to read the content in the TF card, you need to use the adapter mode. Create a card reader to read the contents of the TF card.

The class diagram is as follows:

TFCard and TFCardImpl are adapter classes, SDCard and SDCardImpl are target interfaces, and SDAdapterTF is the adapter class.

We let the adapter class SDAdapterTF implement the SDCard interface, and inherit the adapter class TFCardImpl, so that it can be compatible with TF operations.

code show as below:

  • Target interface - interface for SD card

    public interface SDCard {
          
          
        // 读取SD卡方法
        String readSD();
        // 写入SD卡功能
        void writeSD(String msg);
    }
    
  • Specific SD card implementation class

    public class SDCardImpl implements SDCard {
          
          
        public String readSD() {
          
          
            String msg = "sd card read a msg :hello world SD";
            return msg;
        }
    
        public void writeSD(String msg) {
          
          
            System.out.println("sd card write msg : " + msg);
        }
    }
    
  • computer class

    public class Computer {
          
          
        
        // 从SD卡中读取数据
        public String readSD(SDCard sdCard) {
          
          
            if (sdCard == null) {
          
          
                throw new NullPointerException("sd card null");
            }
            return sdCard.readSD();
        }
    }
    
  • Adapter class interface - TF card interface

    public interface TFCard {
          
          
        // 读取TF卡方法
        String readTF();
        // 写入TF卡功能
        void writeTF(String msg);
    }
    
  • Adapter class - TF card implementation class

    public class TFCardImpl implements TFCard {
          
          
    
        public String readTF() {
          
          
            String msg ="tf card read msg : hello world tf card";
            return msg;
        }
    
        public void writeTF(String msg) {
          
          
            System.out.println("tf card write a msg : " + msg);
        }
    }
    
  • Define adapter class (SD compatible TF)

    public class SDAdapterTF extends TFCardImpl implements SDCard {
          
          
    
        public String readSD() {
          
          
            System.out.println("adapter read tf card ");
            return readTF();
        }
    
        public void writeSD(String msg) {
          
          
            System.out.println("adapter write tf card");
            writeTF(msg);
        }
    }
    
  • test class

    public class Client {
          
          
        public static void main(String[] args) {
          
          
            // 创建计算机对象
            Computer computer = new Computer();
            // 读取SD卡中的数据
            String msg = computer.readSD(new SDCardImpl());
            System.out.println(msg);
    
            System.out.println("===============");
            // 使用该电脑读取TF卡中的数据
            // 创建适配器类对象
            SDAdapterTF sdAdapterTF = new SDAdapterTF(new TFCardImpl());
            String msg1 = computer.readSD(sdAdapterTF);
            System.out.println(msg1);
        }
    }
    

    output

    SDCard read msg : hello world SD
    ===============
    adapter read tf card
    TFCard read msg : hello world TFcard
    

The Class Adapter pattern violates the principle of compositional reuse. A class adapter is available if the client class has an interface specification, and not otherwise.

object adapter pattern

Implementation method: The object adapter mode can be used to introduce the components already implemented in the existing component library into the adapter class, and this class realizes the business interface of the current system at the same time.

The construction method in FutureTask uses the object adapter pattern to convert Runnable to Callable;
for details, please refer to: Construction method of FutureTask source code analysis .

[Example] card reader

We use the object adapter pattern to rewrite the case of the card reader.

The class diagram is as follows:

TFCard and TFCardImpl are adapter classes, SDCard and SDCardImpl are target interfaces, and SDAdapterTF is the adapter class.

We let the adapter class SDAdapterTF implement the SDCard interface, but this time we aggregate the adapter class TFCard instead of directly inheriting it. This satisfies the principle of composite reuse. If there is no interface specification SDCard, we can directly inherit the specific target implementation class SDCardImpl, which solves the two shortcomings of the above class adapter mode.

For the code of the class adapter pattern, we only need to modify the adapter class (SDAdapterTF) and the test class.

code show as below:

  • Create adapter object (SD compatible TF)

    public class SDAdapterTF implements SDCard {
          
          
    
        // 聚合-声明适配者类
        private TFCard tfCard;
    
        public SDAdapterTF(TFCard tfCard) {
          
          
            this.tfCard = tfCard;
        }
    
        public String readSD() {
          
          
            System.out.println("adapter read tf card ");
            return tfCard.readTF();
        }
    
        public void writeSD(String msg) {
          
          
            System.out.println("adapter write tf card");
            tfCard.writeTF(msg);
        }
    }
    
  • test class

    public class Client {
          
          
        public static void main(String[] args) {
          
          
            Computer computer = new Computer();
            SDCard sdCard = new SDCardImpl();
            System.out.println(computer.readSD(sdCard));
    
            System.out.println("------------");
    
            // 创建适配者类对象
            TFCard tfCard = new TFCardImpl();
            // 创建适配器类对象
            SDAdapterTF adapter = new SDAdapterTF(tfCard);
            System.out.println(computer.readSD(adapter));
        }
    }
    

    output

    SDCard read msg : hello world SD
    ===============
    adapter read tf card
    TFCard read msg : hello world TFcard
    

Note: Another adapter pattern is the interface adapter pattern. When you don't want to implement all the methods in an interface, you can create an abstract class Adapter to implement all the methods. At this time, we only need to inherit the abstract class.

Application Scenario

  • The previously developed system has classes that meet the functional requirements of the new system, but its interface is inconsistent with the interface of the new system.
  • Use the components provided by the third party, but the component interface definition is different from the interface definition required by yourself.

JDK source code analysis - InputStreamReader

The adaptation of Reader (character stream) and InputStream (byte stream) uses InputStreamReader (converts byte data into character data).

InputStreamReader inherits from Reader in the java.io package, and implements the abstract unimplemented methods in it. like:

public int read() throws IOException {
    
    
    return sd.read();
}

public int read(char cbuf[], int offset, int length) throws IOException {
    
    
    return sd.read(cbuf, offset, length);
}

As for sd (StreamDecoder class object) in the above code, in Sun's JDK implementation, the actual method implementation is the call encapsulation of the method with the same name in the sun.nio.cs.StreamDecoder class.

The class structure diagram is as follows:

image-20230107144804733

The three classes Reader, InputStream, and StreamDecoder use the standard object adapter pattern. StreamDecoder aggregates InputStream, and it also provides two read methods. In fact, these two methods are methods for rewriting the parent class Reader, so StreamDecoder is An adapter class, and its parent abstract class Reader is the target interface, and InputStream is the adapter class.

  • InputStreamReader is an encapsulation of StreamDecoder which also implements Reader.
  • StreamDecoder is not part of the Java SE API, but its own implementation provided by Sun JDK. But we know that they encapsulate the byte stream class (InputStream) in the construction method, and use this class to convert between byte streams and character streams 解码.

in conclusion:

From the superficial point of view, InputStreamReader does the conversion between InputStream byte stream class and Reader character stream. As can be seen from the implementation class relationship structure in Sun JDK above, the design and implementation of StreamDecoder actually adopts the adapter mode.

Guess you like

Origin blog.csdn.net/weixin_53407527/article/details/128627892