[6] Design Pattern ~~~ Structural Pattern ~~~ Adapter Pattern (Java)

[Learning Difficulty: ★★☆☆☆, Frequency of Use: ★★★★☆】

1.1. Pattern Motivation

  • Using design and coding techniques similar to power adapters in software development is known as the adapter pattern.
  • Typically, clients can access the services it provides through the interface of the target class. Sometimes, the existing class can meet the functional needs of the client class, but the interface it provides is not necessarily what the client class expects. This may be because the method name in the existing class is inconsistent with the method name defined in the target class. caused by.
  • In this case, the existing interface needs to be transformed into the interface expected by the client class, which ensures the reuse of the existing class. If such conversion is not performed, the client class cannot take advantage of the functions provided by the existing class, and the adapter pattern can complete such conversion.
  • In the adapter mode, a wrapper class can be defined to wrap objects with incompatible interfaces. This wrapper class refers to the adapter (Adapter), and the object it wraps is the adapter (Adaptee), that is, the class to be adapted.
  • The adapter provides the interface required by the client class, and the implementation of the adapter is to convert the request of the client class into a call to the corresponding interface of the adapter. That is to say: when the client class calls the method of the adapter, the method of the adapter class will be called inside the adapter class, and this process is transparent to the client class, and the client class does not directly access the adapter class. Thus, adapters enable classes that cannot interact because of incompatible interfaces to work together. This is the pattern motivation of the Adapter pattern.

1.2. Schema Definition

       Adapter Pattern: Convert an interface into another interface that the customer wants. The adapter pattern enables those classes with incompatible interfaces to work together. Its alias is Wrapper. The Adapter pattern can be used both as a class-structural pattern and as an object-structural pattern.

1.3. Schema structure

       The adapter pattern includes the following roles:

  • Target: target abstract class , the target abstract class defines the interface required by the customer, which can be an abstract class or interface, or a concrete class.
  • Adapter: adapter class , the adapter can call another interface, as a converter, adapt Adaptee and Target, the adapter class is the core of the adapter mode, in the object adapter, it inherits the Target and associates an Adaptee object to make the two Make a connection.
  • Adaptee: Adapter class , the adapter is the role to be adapted, it defines an existing interface, this interface needs to be adapted, the adapter class is generally a concrete class, including the business method that the customer wants to use , and in some cases there may be no source code for the adapter class.
  • Client: client class , according to the structure diagram of the object adapter pattern, in the object adapter, the client needs to call the request() method, but the adapter class Adaptee does not have this method, but the specificRequest() method it provides is provided by the client needs. In order to enable the client to use the adapter class, it is necessary to provide a wrapper class Adapter, that is, the adapter class. This wrapper class wraps an instance of an adapter to connect the client with the adapter, and calls the adapter's specificRequest() method in the adapter's request() method. Because the adapter class and the adapter class are in an association relationship (also called a delegation relationship), this adapter pattern is called an object adapter pattern. Typical object adapter code looks like this:
class Adapter extends Target {
    
    
	private Adaptee adaptee; //维持一个对适配者对象的引用
	
	public Adapter(Adaptee adaptee) {
    
    
		this.adaptee=adaptee;
	}
	
	public void request() {
    
    
		adaptee.specificRequest(); //转发调用
	}
}

       Adapter mode has two implementations: object adapter and class adapter: in object adapter mode, there is an association relationship between the adapter and the adapter; in the class adapter mode, the relationship between the adapter and the adapter is inheritance (or implementation). In actual development, object adapters are used more frequently.

Object adapter:
insert image description here

class adapter:
insert image description here

1.4. Timing diagram

insert image description here

1.5. Code Analysis
       The developers of Sunny Software Company decided to use the adapter pattern to reuse the algorithms in the algorithm library, and its basic structure is shown in Figure 9-4:
insert image description here

1.5.1 Production

       In Figure 9-4, the ScoreOperation interface acts as the abstract target, the QuickSort and BinarySearch classes act as the adapters, and the OperationAdapter acts as the adapter. The full code is as follows:

package com.zyz.demo;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/13 10:20
 * @Description: 定义适配者:具体完成的接口方法、适配器:接口请求转化。抽象目标接口:客户端实际调用接口
 */
/**
 *  抽象成绩操作类:目标接口
 */
interface ScoreOperation {
    
    
    public int[] sort(int array[]); //成绩排序
    public int search(int array[],int key); //成绩查找
}


/**
 * 快速排序类:适配者
 */
class QuickSort {
    
    
    public int[] quickSort(int array[]) {
    
    
        sort(array,0,array.length-1);
        return array;
    }

    public void sort(int array[],int p, int r) {
    
    
        int q=0;
        if(p<r) {
    
    
            q=partition(array,p,r);
            sort(array,p,q-1);
            sort(array,q+1,r);
        }
    }

    public int partition(int[] a, int p, int r) {
    
    
        int x=a[r];
        int j=p-1;
        for (int i=p;i<=r-1;i++) {
    
    
            if (a[i]<=x) {
    
    
                j++;
                swap(a,j,i);
            }
        }
        swap(a,j+1,r);
        return j+1;
    }

    public void swap(int[] a, int i, int j) {
    
    
        int t = a[i];
        a[i] = a[j];
        a[j] = t;
    }
}

/**
 * 二分查找类:适配者
 */
class BinarySearch {
    
    
    public int binarySearch(int array[],int key) {
    
    
        int low = 0;
        int high = array.length -1;
        while(low <= high) {
    
    
            int mid = (low + high) / 2;
            int midVal = array[mid];
            if(midVal < key) {
    
    
                low = mid +1;
            }
            else if (midVal > key) {
    
    
                high = mid -1;
            }
            else {
    
    
                return 1; //找到元素返回1
            }
        }
        return -1;  //未找到元素返回-1
    }
}


/**
 * 适配器
 */
class OperationAdapter implements ScoreOperation{
    
    
    private QuickSort sortObj; //定义适配者QuickSort对象
    private  BinarySearch searchObj; //定义适配者BinarySearch对象

    public OperationAdapter(){
    
    
        sortObj = new QuickSort();
        searchObj = new BinarySearch();
    }

    @Override
    public int[] sort(int[] array) {
    
    
        return sortObj.quickSort(array);
    }

    @Override
    public int search(int[] array, int key) {
    
    
        return searchObj.binarySearch(array,key);
    }
}

1.5.2 Client

Write the following client test code:

package com.zyz.demo;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/13 10:29
 * @Description: 客户端
 */
public class Client {
    
    
    public static void main(String[] args) {
    
    
        ScoreOperation operation; //针对抽象目标接口编程
        operation = new OperationAdapter();

        int scores[] = {
    
    84, 76, 50, 69, 90, 91, 88, 96}; //定义成绩数组
        int result[];
        int score;

        System.out.println("成绩排序结果:");
        result = operation.sort(scores);
        for (int i : result) {
    
    
            System.out.print(i + ",");
        }
        System.out.println(" ");

        System.out.println("查找成绩90:");
        score = operation.search(result,90);
        if (score != -1) {
    
    
            System.out.println("找到成绩90。");
        }
        else {
    
    
            System.out.println("没有找到成绩90。");
        }

        System.out.println("查找成绩92:");
        score = operation.search(result,92);
        if (score != -1) {
    
    
            System.out.println("找到成绩92。");
        }
        else {
    
    
            System.out.println("没有找到成绩92。");
        }
    }
}

1.5.3 Results

insert image description here

1.6. Pattern Analysis

1.7. Examples

Same as 1.5 code analysis case

1.8. Advantages

  • Decouple the target class and the adapter class, and reuse the existing adapter class by introducing an adapter class without modifying the original code.
  • The transparency and reusability of the class are increased, and the specific implementation is encapsulated in the adapter class, which is transparent to the client class and improves the reusability of the adapter.
  • The flexibility and scalability are very good. By using the configuration file, the adapter can be easily replaced, and a new adapter class can be added without modifying the original code, which fully complies with the "open and close principle".

       The class adapter pattern also has the following advantages:
Since the adapter class is a subclass of the adapter class, some methods of the adapter can be replaced in the adapter class, making the adapter more flexible.

       The object adapter pattern also has the following advantages:
an object adapter can adapt multiple different adapters to the same target, that is, the same adapter can adapt both the adapter class and its subclasses to the target interface.

1.9. Disadvantages

       The disadvantages of the class adapter pattern are as follows:
       For languages ​​that do not support multiple inheritance, such as Java and C#, at most one adapter class can be adapted at a time, and the target abstract class can only be an abstract class, not a concrete class. Due to limitations, an adapter class and its subclasses cannot be adapted to the target interface.

       The disadvantages of the object adapter pattern are as follows:
       Compared with the class adapter pattern, it is not easy to replace the method of the adapter class. If you must replace one or more methods of the adapter class, you have to make a subclass of the adapter class first, replace the methods of the adapter class, and then use the subclass of the adapter class as The real adapter performs the adaptation, and the implementation process is more complicated.

1.10. Applicable environment

The Adapter pattern can be used when:

  • The system needs to use existing classes whose interfaces do not meet the needs of the system.
  • Want to create a reusable class for working with some classes that are not very related to each other, including some classes that may be introduced in the future.

1.11. Pattern application

       In 1996, Sun Corporation released the Java language database connection tool JDBC. JDBC enables Java language programs to connect to the database, and use SQL language to query and manipulate data. JDBC provides a common abstract interface for clients, and the JDBC driver software of each specific database engine (such as SQL Server, Oracle, MySQL, etc.) is an adapter software between the JDBC interface and the database engine interface. Corresponding adapter software is required between the abstract JDBC interface and each database engine API, which is the driver prepared for each different database engine.

1.12. Schema extensions

       Default Adapter Pattern or Default Adapter Pattern
When you do not need to implement all the methods provided by the interface, you can first design an abstract class to implement the interface, and provide a default implementation (empty method) for each method in the interface. Then the subclass of the abstract class can selectively override some methods of the parent class to achieve the requirements, which is suitable for situations where an interface does not want to use all its methods. It is therefore also known as the single-interface adapter pattern.

1.13. Summary

  • Structural patterns describe how classes or objects are grouped together to form larger structures.
  • The adapter pattern is used to convert an interface into another interface that the client wants. The adapter pattern enables those classes with incompatible interfaces to work together, and its alias is a wrapper. The Adapter pattern can be used both as a class-structural pattern and as an object-structural pattern.
  • The adapter pattern contains four roles: the target abstract class defines the domain-specific interface that the client will use; the adapter class can call another interface, as a converter, to adapt the adapter and the abstract target class, which is the adapter pattern Core; the adapter class is the role to be adapted, which defines an existing interface that needs to be adapted; in the client class, program against the target abstract class, and call the business method defined in the target abstract class.
  • In the class adapter mode, the adapter class implements the target abstract class interface and inherits the adapter class, and calls the method of the inherited adapter class in the implementation method of the target abstract class; in the object adapter mode, the adapter class Inherit the target abstract class and define an object instance of the adapter class, and call the corresponding business method of the adapter class in the inherited target abstract class method.
  • The main advantage of the adapter mode is to decouple the target class and the adapter class, which increases the transparency and reusability of the class. At the same time, the flexibility and scalability of the system are very good. It is very convenient to replace the adapter or add a new adapter. , in line with the "opening and closing principle"; the disadvantage of the class adapter model is that the adapter class cannot adapt to multiple adapter classes at the same time in many programming languages, and the disadvantage of the object adapter model is that it is difficult to replace the method of the adapter class.
  • The application of the adapter pattern includes: the system needs to use existing classes, and the interfaces of these classes do not meet the needs of the system; want to create a reusable class for use with some classes that are not very related to each other Work.

1.14 Extension (read xml file)

       In order to make the system have good flexibility and scalability, we introduced the tool class XMLUtil and configuration files, where the code of the XMLUtil class is as follows:

package com.zyz.demo.config;

import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.SAXException;
import java.io.*;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/13 11:04
 * @Description:
 */
public class XMLUtil {
    
    
    /**
     * 该方法用于从XML配置文件中提取具体类类名,并返回一个实例对象
     * @return
     */
    public static Object getBean() {
    
    
        try {
    
    
            String path = "F:\\java学习资料(后端)\\github管理后端学习资料\\后端学习\\设计模式\\代码\\DesignPatterns-Java-Examples\\6. 适配器模式\\src\\main\\resources\\config.xml";
            //创建文档对象
            DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = dFactory.newDocumentBuilder();
            Document doc;
            doc = builder.parse(new File(path));

            //获取包含类名的文本节点
            NodeList nl = doc.getElementsByTagName("className");
            Node classNode=nl.item(0).getFirstChild();
            String cName=classNode.getNodeValue();

            //通过类名生成实例对象并将其返回
            Class c=Class.forName("com.zyz.demo."+cName);
            Object obj=c.newInstance();
            return obj;
        }
        catch(Exception e) {
    
    
            e.printStackTrace();
            return null;
        }
    }
}

The class name of the adapter class is stored in the configuration file config.xml, and the code is as follows:

<?xml version="1.0"?>
<config>
    <className>MyOperationAdapter</className>
</config>

Write the following client test code:

package com.zyz.demo;

import com.zyz.demo.config.XMLUtil;

/**
 * @author zyz
 * @version 1.0
 * @data 2023/5/13 11:04
 * @Description:
 */
public class Client1 {
    
    
    public static void main(String[] args) {
    
    
        ScoreOperation operation; //针对抽象目标接口编程
        operation = (ScoreOperation) XMLUtil.getBean();

        int scores[] = {
    
    84, 76, 50, 69, 90, 91, 88, 96}; //定义成绩数组
        int result[];
        int score;

        System.out.println("成绩排序结果:");
        result = operation.sort(scores);
        for (int i : result) {
    
    
            System.out.print(i + ",");
        }
        System.out.println(" ");

        System.out.println("查找成绩90:");
        score = operation.search(result,90);
        if (score != -1) {
    
    
            System.out.println("找到成绩90。");
        }
        else {
    
    
            System.out.println("没有找到成绩90。");
        }

        System.out.println("查找成绩92:");
        score = operation.search(result,92);
        if (score != -1) {
    
    
            System.out.println("找到成绩92。");
        }
        else {
    
    
            System.out.println("没有找到成绩92。");
        }
    }
}

Test Results
insert image description here

1.15 class adapter

       In addition to the object adapter mode, there is another form of the adapter mode, that is, the class adapter mode. The biggest difference between the class adapter mode and the object adapter mode is that the relationship between the adapter and the adapter is different. In the object adapter mode, the adapter and the adaptation There is an association relationship between the adapters, while the adapter and the adapter in the class adapter pattern are an inheritance relationship. The structure of the class adapter pattern is shown in Figure 9-5:
insert image description here

                    图 9-5 类适配器模式结构图

       According to the structure diagram of the class adapter pattern, the adapter class implements the abstract target class interface Target, and inherits the adapter class, calls the specificRequest() method of the inherited adapter class in the request() method of the adapter class, and realizes adaptation.
Typical class adapter code looks like this:

class Adapter extends Adaptee implements Target {
    
    
	public void request() {
    
    
		specificRequest();
	}
}

       Because languages ​​such as Java and C# do not support multiple class inheritance, the use of class adapters is subject to many restrictions. For example, if the target abstract class Target is not an interface, but a class, the class adapter cannot be used; in addition, if the adapter Adaptee is the final (Final) classes, also cannot use class adapters. In object-oriented programming languages ​​such as Java, we use object adapters in most cases, and class adapters are rarely used.

1.16 Two-way adapter

       During the use of the object adapter, if the adapter contains references to both the target class and the adapter class, the adapter can call the method in the target class through it, and the target class can also call the method in the adapter class through it. method, then the adapter is a two-way adapter, and its structural diagram is shown in Figure 9-6:

insert image description here

The implementation of the bidirectional adapter is more complicated, and its typical code is as follows:

class Adapter implements Target,Adaptee {
    
    
    //同时维持对抽象目标类和适配者的引用
	private Target target;
	private Adaptee adaptee;
	
	public Adapter(Target target) {
    
    
		this.target = target;
	}
	
	public Adapter(Adaptee adaptee) {
    
    
		this.adaptee = adaptee;
	}
	
	public void request() {
    
    
		adaptee.specificRequest();
	}
	
	public void specificRequest() {
    
    
		target.request();
	}
}

In actual development, we rarely use two-way adapters.

1.17 Default Adapter

       The default adapter pattern is a variant of the adapter pattern, and its application is more extensive. The default adapter schema is defined as follows:

       Default Adapter Pattern: When you do not need to implement all the methods provided by an interface, you can first design an abstract class to implement the interface, and provide a default implementation (empty method) for each method in the interface, then The subclass of this abstract class can selectively override some methods of the parent class to achieve the requirements. It is suitable for situations where you do not want to use all the methods in an interface, also known as the single interface adapter mode.

The structure of the default adapter mode is shown in Figure 9-7:
insert image description here

       In the default adapter mode, the following three roles are included:

  • ServiceInterface (adapter interface): It is an interface in which a large number of methods are usually declared.
  • AbstractServiceClass (default adapter class): It is the core class of the default adapter mode, and implements the method declared in the ServiceInterface interface in the form of an empty method. It's usually defined as an abstract class because it doesn't make sense to instantiate it.
  • ConcreteServiceClass (concrete business class): It is a subclass of the default adapter class. Before the adapter is introduced, it needs to implement the adapter interface, so it needs to implement all the methods defined in the adapter interface, and some do not need to be used The method also had to provide an empty implementation. After you have a default adapter, you can directly inherit the adapter class, and selectively override the methods defined in the adapter class as needed.

       The default adapter mode is widely used in the event processing package java.awt.event of the JDK class library, such as WindowAdapter, KeyAdapter, MouseAdapter, etc. Let's take window event processing as an example to illustrate: In the Java language, generally we can implement window event processing classes in two ways, one is by implementing the WindowListener interface, and the other is by inheriting the WindowAdapter adapter class. If the first method is used to directly implement the WindowListener interface, the event processing class needs to implement the seven methods defined in the interface, and for most requirements, it may only need to implement one or two methods, and other methods do not need to be implemented, but due to Language features We have to provide a simple implementation (usually empty implementation) for other methods, which brings troubles to use. Using the default adapter mode can solve this problem very well. An adapter class WindowAdapter is provided in JDK to implement the WindowListener interface. This adapter class provides an empty implementation for each method in the interface. At this time, the event The processing class can inherit the WindowAdapter class without providing implementation for each method in the interface. As shown in Figure 9-8:
insert image description here

Guess you like

Origin blog.csdn.net/weixin_43304253/article/details/130926224