2-1设计模式之适配器模式(Adapter)

2.1.1 模式意图:

当需要将现有的接口转化为目的期望的接口,且保持功能不变,这时运用适配器模式就可以完成转化。

2.1.2 模式概念:

属于结构型模式,将一个类的接口转换成用户期望的另外一个接口。适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。

2.1.3 模式元素:

  • 结构抽象(IHelper)
  • 结构细节(MysqlHelper、OracleHelper )
  • 适配元素(RedisHelper)
  • 适配器:类适配器或对象适配器(RedisHelperClass或RedisHelperObject)

2.1.4 代码示例:

以数据库的增删改查为例
在项目之初,准备接入两种数据库Mysql与Oracle,而且对应的增删改查的接口也是一致的。

    public interface IHelper
    {
        void Add();
        void Delete();
        void Update();
        void Query();
    }
    public class MysqlHelper : IHelper
    {
        public void Add()
        {
            this.Log($"This is {GetType().Name} Add");
        }
        public void Delete()
        {
            this.Log($"This is {GetType().Name} Delete");
        }
        public void Update()
        {
            this.Log($"This is {GetType().Name} Update");
        }
        public void Query()
        {
            this.Log($"This is {GetType().Name} Query");
        }
    }
    public class OracleHelper : IHelper
    {
        public void Add()
        {
            this.Log($"This is {GetType().Name} Add");
        }
        public void Delete()
        {
            this.Log($"This is {GetType().Name} Delete");
        }
        public void Update()
        {
            this.Log($"This is {GetType().Name} Update");
        }
        public void Query()
        {
            this.Log($"This is {GetType().Name} Query");
        }
    }

随着项目需求的变更,需要接入RedisHelper,但是它对应增删改查的接口和项目中的不一致, 这时就可以使用适配器模式。
适配器模式有两个类型【类适配器模式】和【对象适配器模式】

A.适配类型

    public class RedisHelper
    {
        public void AddRedis()
        {
            this.Log($"This is {GetType().Name} Add");
        }
        public void DeleteRedis()
        {
            this.Log($"This is {GetType().Name} Delete");
        }
        public void UpdateRedis()
        {
            this.Log($"This is {GetType().Name} Update");
        }
        public void QueryRedis()
        {
            this.Log($"This is {GetType().Name} Query");
        }
    }

B.类适配器

    public class RedisHelperClass : RedisHelper, IHelper
    {
        public void Add()
        {
            base.AddRedis();
        }

        public void Delete()
        {
            base.DeleteRedis();
        }

        public void Update()
        {
            base.UpdateRedis();
        }

        public void Query()
        {
            base.QueryRedis();
        }
    }

C.对象适配器

    public class RedisHelperObject : IHelper
    {
        private RedisHelper redisHelper = null;
        public RedisHelperObject(RedisHelper redisHelper)
        {
            this.redisHelper = redisHelper;
        }

        public void Add()
        {
            this.redisHelper.AddRedis();
        }

        public void Delete()
        {
            this.redisHelper.DeleteRedis();
        }

        public void Update()
        {
            this.redisHelper.UpdateRedis();
        }

        public void Query()
        {
            this.redisHelper.QueryRedis();
        }
    }

调用如下

    void Start()
    {
        IHelper helper = new MysqlHelper();
        helper.Update();

        helper = new OracleHelper();
        helper.Update();

        helper = new RedisHelperClass();
        helper.Update();

        helper = new RedisHelperObject(new RedisHelper());
        helper.Update();
    }

是不是很简单呢,只需要在适配的类上嵌套一个类,就可以成为适配器模式。

2.1.5 写法对比:

2.1.6 模式分析:

在使用适配器的时候有两点需要注意的地方:
一、要在双方都不易修改的时候再使用适配器模式进行适配。如果在项目设计初,发现这种接口不一致的情况就需要及时避免。适配器类似一个补丁,不是只要接口不一致就使用适配器模式,而是在后期无法更改或更改成本过高的情况下再进行弥补操作。
二、类适配器和对象适配器,笔者更倾向于对象适配器,因为组合优于继承。
a.通过构造函数传入指定的适配类型,易于适配类的后期扩展。
b.符合里氏替换原则,使用对象适配器不会暴露父类中的内容,封装性更好。

2.1.7 应用场景:

  • 系统需要使用的类,但这些类的接口不符合系统的需要。

2.1.8 小结:

适配器模式关注的核心:因无法更改或更改成本过高,迫使用户使用适配器模式来完成接口的转换。


更多设计模式详见:设计模式全家桶

发表评论