Adapter Pattern

C# Design Patterns (7) - Adapter Pattern (Adapter Pattern)

I. Introduction

In the actual development process, due to changes in the application environment (such as changes in the language used), the implementation we need cannot be satisfied by existing objects in the new environment, but there are such existing objects in other environments. So what if the "existing object" is called in the new environment? The solution to this problem is the adapter pattern that we are going to introduce in this article - so that the existing objects (referring to the existing objects in the original environment) are well added to the new environment without needing to repeat the implementation of the existing ones. environment to use .

Second, the detailed introduction of the adapter mode

2.1 Definitions 

Let's take a look at the definition of an adapter, the adapter pattern - transforming the interface of one class into another interface expected by the client, so that two classes that originally do not match and cannot work together can work together. There are two forms of adapter mode: class adapter mode and object adapter mode. Below we discuss the implementation of these two forms and give the corresponding class diagram to help you clarify the relationship between classes.

2.2 Adapter pattern implementation of classes

Here is an example in life to demonstrate the implementation of the adapter mode. The specific scenario is: In life, the electrical plugs we buy have 2 holes, but the sockets we buy only have three holes. At this time, we will I hope that the plug of the electrical appliance can be converted into a three-hole plug, so that we can directly plug it into the socket. At this time, the three-hole plug is another interface expected by the client. Naturally, the two-hole plug is the current interface. For some interfaces, the adapter mode is used to complete this conversion. The specific implementation code is as follows:

copy code
using System;

/// The adapter mode is explained by the example of socket and plug
 /// The electrical plug we bought now has 2 holes, but the socket we bought only has 3 holes
 /// This is what we want to plug the electrical appliance into the socket If it is on, you need an adapter mode of the electrical adapter 
namespace design mode
{
    ///  <summary> 
    /// Client, the client wants to convert a 2-hole plug into a 3-hole plug, this conversion is just fine to the adapter
     /// Since the adapter needs to complete this function, it must be done at the same time Specific features of 2-hole plug and three-hole plug
     ///  </summary> 
    class Client
    {
        static void Main(string[] args)
        {
            // Now the client can use the plug with 2 holes through the power adapter 
            IThreeHole threehole = new PowerAdapter();
            threehole.Request();
            Console.ReadLine();
        }
    }

    ///  <summary> 
    /// Three-hole plug, which is the target role in adapter mode
     ///  </summary> 
    public  interface IThreeHole
    {
        void Request();
    }

    /// <summary>
    /// 两个孔的插头,源角色——需要适配的类
    /// </summary>
    public abstract class TwoHole
    {
        public void SpecificRequest()
        {
            Console.WriteLine("我是两个孔的插头");
        }
    }

    /// <summary>
    /// 适配器类,接口要放在类的后面
    /// 适配器类提供了三个孔插头的行为,但其本质是调用两个孔插头的方法
    /// </summary>
    public class PowerAdapter:TwoHole,IThreeHole
    {
        /// <summary>
        /// 实现三个孔插头接口方法
        /// </summary>
        public void Request()
        {
            // 调用两个孔插头方法
            this.SpecificRequest();
        }
    }
}
copy code

从上面代码中可以看出,客户端希望调用Request方法(即三个孔插头),但是我们现有的类(即2个孔的插头)并没有Request方法,它只有SpecificRequest方法(即两个孔插头本身的方法),然而适配器类(适配器必须实现三个孔插头接口和继承两个孔插头类)可以提供这种转换,它提供了Request方法的实现(其内部调用的是两个孔插头,因为适配器只是一个外壳罢了,包装着两个孔插头(因为只有这样,电器才能使用),并向外界提供三个孔插头的外观,)以供客户端使用。

2.3 类图

上面实现中,因为适配器(PowerAdapter类)与源角色(TwoHole类)是继承关系,所以该适配器模式是类的适配器模式,具体对应的类图为:

2.4 对象的适配器模式

上面都是类的适配器模式的介绍,然而适配器模式还有另外一种形式——对象的适配器模式,这里就具体讲解下它的实现,实现的分析思路:既然现在适配器类不能继承TwoHole抽象类了(因为用继承就属于类的适配器了),但是适配器类无论如何都要实现客户端期待的方法的,即Request方法,所以一定是要继承ThreeHole抽象类或IThreeHole接口的,然而适配器类的Request方法又必须调用TwoHole的SpecificRequest方法,又不能用继承,这时候就想,不能继承,但是我们可以在适配器类中创建TwoHole对象,然后在Requst中使用TwoHole的方法了。正如我们分析的那样,对象的适配器模式的实现正式如此。下面就让我看看具体实现代码:

copy code
namespace 对象的适配器模式
{
    class Client
    {
        static void Main(string[] args)
        {
            // 现在客户端可以通过电适配要使用2个孔的插头了
            ThreeHole threehole = new PowerAdapter();
            threehole.Request();
            Console.ReadLine();
        }
    }

    /// <summary>
    /// 三个孔的插头,也就是适配器模式中的目标(Target)角色
    /// </summary>
    public class ThreeHole
    {
        // 客户端需要的方法
        public virtual void Request()
        {
            // 可以把一般实现放在这里
        }
    }

    /// <summary>
    /// 两个孔的插头,源角色——需要适配的类
    /// </summary>
    public class TwoHole
    {
        public void SpecificRequest()
        {
            Console.WriteLine("我是两个孔的插头");
        }
    }

    /// <summary>
    /// 适配器类,这里适配器类没有TwoHole类,
    /// 而是引用了TwoHole对象,所以是对象的适配器模式的实现
    /// </summary>
    public class PowerAdapter : ThreeHole
    {
        // 引用两个孔插头的实例,从而将客户端与TwoHole联系起来
        public TwoHole twoholeAdaptee = new TwoHole();

        /// <summary>
        /// 实现三个孔插头接口方法
        /// </summary>
        public override void Request()
        {
            twoholeAdaptee.SpecificRequest();
        }
    }
}
copy code

从上面代码可以看出,对象的适配器模式正如我们开始分析的思路去实现的, 其中客户端调用代码和类的适配器实现基本相同,下面让我们看看对象的适配器模式的类图,具体类图如下:

三、适配器模式的优缺点

在引言部分已经提出,适配器模式用来解决现有对象与客户端期待接口不一致的问题,下面详细总结下适配器两种形式的优缺点。

类的适配器模式:

优点:

  • 可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
  • 可以重新定义Adaptee(被适配的类)的部分行为,因为在类适配器模式中,Adapter是Adaptee的子类
  • 仅仅引入一个对象,并不需要额外的字段来引用Adaptee实例(这个即是优点也是缺点)。

缺点:

  • 用一个具体的Adapter类对Adaptee和Target进行匹配,当如果想要匹配一个类以及所有它的子类时,类的适配器模式就不能胜任了。因为类的适配器模式中没有引入Adaptee的实例,光调用this.SpecificRequest方法并不能去调用它对应子类的SpecificRequest方法。
  • 采用了 “多继承”的实现方式,带来了不良的高耦合。

对象的适配器模式

优点:

  • 可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”(这点是两种实现方式都具有的)
  • 采用 “对象组合”的方式,更符合松耦合。

缺点:

  • 使得重定义Adaptee的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。

四、使用场景

在以下情况下可以考虑使用适配器模式:

  1. 系统需要复用现有类,而该类的接口不符合系统的需求
  2. 想要建立一个可重复使用的类,用于与一些彼此之间没有太大关联的一些类,包括一些可能在将来引进的类一起工作。
  3. For the object adapter pattern, it is necessary to change the interfaces of multiple existing subclasses in the design. If the class adapter pattern is used, it is necessary to make an adapter for each subclass, which is not practical.

5. Implementation of the Adapter Pattern in .NET

1. One of the biggest applications of the adapter pattern in the .NET Framework is COM Interop . COM Interop is like a bridge between COM and .NET (for more information on COM interop, please refer to my interop series ). COM component objects are completely different from .NET class objects, but in order to make .NET programs

Using COM components like using .NET objects, Microsoft adopts the Adapter mode to wrap the COM objects. This wrapper class is RCW (Runtime Callable Wrapper). RCW is actually a .NET class generated by runtime, which wraps the methods of COM components and implements calls to COM components internally. As shown below:

 

2. Another application of the adapter pattern in .NET is the DataAdapter . ADO.NET provides multiple interfaces and base classes for unified data access, one of the most important interfaces is IdataAdapter. The DataAdpter acts as a bridge from the database to the DataSet, which unifies the data operations of the application to the DataSet, regardless of the specific database type. You can even program your own DataAdpter for special data sources, making our application compatible with these special data sources.

6. Summary

The introduction of the adapter mode is over here. This article mainly introduces the two implementations of the adapter mode, analyzes their advantages and disadvantages, and introduces the usage scenarios. In the adapter mode, the adapter can be an abstract class, and the implementation of the adapter mode is very important. Flexible, we can use the "existing object" in the Adapter mode as a new interface method parameter, and the adapter class can return an appropriate instance to the client according to the parameter parameters.

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324647970&siteId=291194637