[Design Mode] Adapter Mode Say goodbye to interface problems, make your code more compatible, make your interface no longer lonely, unlock a new realm of interface compatibility, and make your code more flexible!

Table of contents

Foreword:

1. Principle and sample code

2. Structure diagram

3. Usage scenarios:

4. Advantages and Disadvantages


Foreword:

        Let’s continue to talk about one of the structural design patterns: the adapter pattern;

1. Principle and sample code

The Adapter pattern is a structural design pattern used to convert the interface of a class into another interface expected by the client. This mode is usually used to solve the adaptation problem between two incompatible interfaces. The adapter pattern includes two implementation methods: class adapter pattern and object adapter pattern.

class adapter pattern

In the class adapter pattern, the adapter class inherits the target interface and contains an instance of the class that needs to be adapted. The adapter class implements the methods of the target interface by calling the methods of the class that needs to be adapted. This pattern uses multiple inheritance so that the adapter class has both the target interface and the functions of the adapter class.

object adapter pattern

In the object adapter pattern, the adapter class contains an instance of the class to be adapted and implements the target interface. The adapter class implements the methods of the target interface by calling the methods of the class that needs to be adapted. This mode uses a combination method so that the adapter class can be associated with the class that needs to be adapted and implement the methods of the target interface.

The main roles of the adapter pattern include:

  1. Target interface (Target): Defines the interface used by the client, and the client calls the adapter method through the target interface.
  2. Adapter: implements the target interface and contains an instance of the class that needs to be adapted or inherits the class that needs to be adapted, and implements the method of the target interface by calling the method of the class that needs to be adapted.
  3. Class that needs to be adapted (Adaptee): The class that needs to be adapted has an interface that is incompatible with the target interface.

The advantages of the adapter pattern include improving code reusability and maintainability, reducing code coupling, and being able to adapt to multiple classes. However, the adapter pattern may also increase system complexity, affect performance, and introduce too many adapter classes.

The adapter pattern is often used in practical applications to adapt old interfaces to new interfaces, or to adapt the interfaces of third-party libraries to their own interfaces. It can well solve the problem of interface incompatibility and improve the reusability and maintainability of code.

Here is the C++ sample code for the class adapter:

#include <iostream>
#include <string>

// 目标接口
class Target {
public:
    virtual void request() = 0;
};

// 需要适配的类
class Adaptee {
public:
    void specificRequest() {
        std::cout << "Adaptee's specific request" << std::endl;
    }
};

// 类适配器
class Adapter : public Target, public Adaptee {
public:
    void request() override {
        specificRequest();
    }
};

int main() {
    Target *adapter = new Adapter();
    adapter->request();

    delete adapter;

    return 0;
}

In this example, the Adapter class inherits the Target and Adaptee classes, using multiple inheritance to implement the adapter model. The adapter classAdapter directly uses the method of the Adaptee class to implement the interface a> method. specificRequest()Targetrequest()

The following is a simple C++ example code that demonstrates the use of the object adapter pattern:

#include <iostream>
#include <string>

// 目标接口
class Target {
public:
    virtual void request() = 0;
};

// 需要适配的类
class Adaptee {
public:
    void specificRequest() {
        std::cout << "Adaptee's specific request" << std::endl;
    }
};

// 对象适配器
class Adapter : public Target {
private:
    Adaptee *adaptee;

public:
    Adapter(Adaptee *a) : adaptee(a) {}

    void request() override {
        adaptee->specificRequest();
    }
};

int main() {
    Adaptee *adaptee = new Adaptee();
    Target *adapter = new Adapter(adaptee);
    adapter->request();

    delete adaptee;
    delete adapter;

    return 0;
}

In this example, Target is the required interface, Adaptee is the class that needs to be adapted, Adapter is Adapter class. The adapter class Adapter uses a combination of Adaptee objects to implement the Target interface.

2. Structure diagram

The structure diagram of the adapter pattern is as follows:

┌────────────┐       ┌──────────────┐
│   Client   │──────>│    Target    │
└────────────┘       └──────────────┘
                             ▲
                             │
                    ┌──────────────┐
                    │   Adaptee    │
                    └──────────────┘
                             ▲
                             │
                    ┌────────────────┐
                    │   Adapter    │
                    └────────────────┘

In this structure diagram, Client needs to use the Target interface, but the Target interface is different from < a i=4>The interface of the class is incompatible. Therefore, you need to use the adapter pattern to adapt the class into a class that conforms to the interface. The class contains the interface and the class, and is implemented using the methods of the class Interface. Use the class to call the interface. AdapteeAdapteeTargetAdapterAdapterTargetAdapteeAdapteeTargetClientAdapterTarget

3. Usage scenarios:

Adapter pattern is typically used in the following situations:

  1. An existing class needs to be used, but its interface is incompatible with the required interface.
  2. Multiple classes need to be used, but their interfaces are incompatible.
  3. An existing class needs to be reused, but its interface does not match the required interface.

The adapter pattern allows these incompatible interfaces to work together, thereby improving code reusability and maintainability. In actual development, the adapter pattern is often used to adapt third-party libraries or legacy code into interfaces that meet your own needs.

For example, an application needs to use the interface of a third-party library to process images, but the interface of this third-party library is incompatible with the interface required by the application. At this time, you can use the adapter pattern to adapt the interface of the third-party library to an interface that meets the needs of the application, so that they can work together.

In addition, the adapter mode can also be used for system expansion. When new functions need to be added, the adapter mode can be used to expand the system's functions without modifying the existing code.

When you need to use an existing class, but its interface is incompatible with the required interface, you can use the adapter pattern. Here is a C++ example code for this situation:

#include <iostream>
#include <string>

// 目标接口
class Target {
public:
    virtual void request() = 0;
};

// 需要适配的类
class Adaptee {
public:
    void specificRequest() {
        std::cout << "Adaptee's specific request" << std::endl;
    }
};

// 类适配器
class Adapter : public Target, public Adaptee {
public:
    void request() override {
        specificRequest();
    }
};

int main() {
    Target *adapter = new Adapter();
    adapter->request();

    delete adapter;

    return 0;
}

The adapter pattern can also be used when multiple classes need to be used but their interfaces are incompatible. Here is a C++ example code for this situation:

#include <iostream>
#include <string>

// 目标接口
class Target {
public:
    virtual void request() = 0;
};

// 需要适配的类
class Adaptee1 {
public:
    void specificRequest1() {
        std::cout << "Adaptee1's specific request" << std::endl;
    }
};

class Adaptee2 {
public:
    void specificRequest2() {
        std::cout << "Adaptee2's specific request" << std::endl;
    }
};

// 类适配器
class Adapter : public Target, public Adaptee1, public Adaptee2 {
public:
    void request() override {
        specificRequest1();
        specificRequest2();
    }
};

int main() {
    Target *adapter = new Adapter();
    adapter->request();

    delete adapter;

    return 0;
}

When you need to reuse an existing class, but its interface does not meet the required interface, you can also use the adapter pattern. Here is a C++ example code for this situation:

#include <iostream>
#include <string>

// 目标接口
class Target {
public:
    virtual void request() = 0;
};

// 需要适配的类
class Adaptee {
public:
    void specificRequest() {
        std::cout << "Adaptee's specific request" << std::endl;
    }
};

// 类适配器
class Adapter : public Target {
private:
    Adaptee *adaptee;

public:
    Adapter(Adaptee *adaptee) : adaptee(adaptee) {}

    void request() override {
        adaptee->specificRequest();
    }
};

int main() {
    Adaptee *adaptee = new Adaptee();
    Target *adapter = new Adapter(adaptee);
    adapter->request();

    delete adapter;
    delete adaptee;

    return 0;
}

These sample codes demonstrate the application of the adapter pattern in different scenarios.

Here’s another example scenario:

Suppose we have an old logger class OldLogger which has a method named log and we want to use a new logger Interface Logger, which has a method named writeLog . We can use the adapter pattern to adapt OldLogger to Logger interface.

#include <iostream>
#include <string>

// 旧的日志记录器类
class OldLogger {
public:
    void log(const std::string& message) {
        std::cout << "Old Logger: " << message << std::endl;
    }
};

// 新的日志接口
class Logger {
public:
    virtual void writeLog(const std::string& message) = 0;
};

// 适配器类,将 OldLogger 适配成 Logger 接口
class OldLoggerAdapter : public Logger {
private:
    OldLogger* oldLogger;

public:
    OldLoggerAdapter(OldLogger* logger) : oldLogger(logger) {}

    void writeLog(const std::string& message) override {
        oldLogger->log(message);
    }
};

int main() {
    // 使用适配器将 OldLogger 适配成 Logger 接口
    OldLogger* oldLogger = new OldLogger();
    Logger* logger = new OldLoggerAdapter(oldLogger);

    // 调用新的 Logger 接口
    logger->writeLog("This is a log message using the new Logger interface");

    delete oldLogger;
    delete logger;

    return 0;
}

In this example, we use the adapter pattern to adapt OldLogger to the Logger interface. By creating the OldLoggerAdapter class, we can make it conform to the requirements of the  interface without changing OldLogger . Logger

4. Advantages and Disadvantages

As a common design pattern, the adapter pattern has the following advantages and disadvantages:

advantage:

  1. Improve code reusability and maintainability: The adapter pattern can convert incompatible interfaces into compatible interfaces, thereby improving code reusability and maintainability.

  2. Reduce code coupling: The adapter mode can decouple the client code from the classes that need to be adapted, making the dependencies between them more flexible and reducing the client's need for adaptation. Implementation-specific dependencies.

  3. Adapt to multiple classes: The adapter mode can adapt to multiple classes at the same time, making the system more flexible.

shortcoming:

  1. Increase system complexity: The adapter pattern will introduce new classes and interfaces, thereby increasing the complexity of the system and making the code structure more complex.

  2. May affect performance: The adapter mode may introduce additional overhead. For example, the creation and calling of the adapter class may affect the performance of the system.

  3. Possibly introducing too many adapter classes: Excessive use of the adapter pattern in a system may result in a large number of adapter classes, making the system difficult to maintain and understand.

  4. Potential design issues: The adapter pattern may sometimes mask system design problems, such as unreasonable interface design or system architecture design.

In general, the adapter pattern can effectively solve the problem of interface incompatibility in some cases and improve the reusability and maintainability of the code. However, you need to weigh the advantages and disadvantages when using it to avoid increasing system complexity caused by excessive use of the adapter mode. The adapter mode is suitable for use when existing interfaces need to be adapted, existing functions need to be reused and the interfaces are incompatible.

Guess you like

Origin blog.csdn.net/mars1199/article/details/135022669