• C# 结构型设计模式----适配器模式


    1、简介

    简单的说就是将一个类的接口转换成客户希望的另一个接口

    举例理解:

    你买了一个苹果手机,但是家里的数据线都是安卓的,你想用安卓的线充你的苹果手机,那你就需要一个转接头。适配器模式就是适用于这种情况。

    适配的本质就是转换,将不满足使用条件的东西通过第三方类进行加工处理成可使用的东西。适配器模式用来解决现有对象与客户端期待接口不一致的问题。

    基本角色:

    • 目标角色(Target):描述了其他类与客户端代码合作时必须遵循的协议。(就是你要调用的接口,相当于苹果充电头)
    • 客户角色(Client):与符合Target接口的对象协同。(就是调用者,需求主体--也就是你)
    • 被适配(服务类,功能类)(Adaptee):定义一个已经存在并已经使用的接口,这个接口需要适配。 客户端与其接口不兼容, 因此无法直接调用其功能。(相当于安卓充电线)
    • 适配器(Adapter) :适配器模式的核心。适配器接受客户端通过适配器接口发起的调用,同时根据其内在逻辑调用对应服务类。客户端代码只需通过接口与适配器交互即可, 无需与具体的服务类耦合。(相当于转接头)

    2、适用场景

    系统需要复用现有类,但是接口又与复用环境要求不一致的情况。

    旧系统与新系统的兼容:

    可以使新系统能够无缝地与老旧系统进行通信。

    第三方组件的集成:

    适配器可以将第三方组件的接口转换为符合我们系统需求的接口形式,从而能够顺利地集成到我们的系统中。

    多个类库之间的互操作:

    适配器模式可以起到桥梁的作用。

    举例:例如海康SDK之类的dll文件没有.NET Core版本的,只有Java的支持跨平台,这时候我们需要使用就可以直接引入Java版本的SDK,用适配器生成接口即可。

    3、创建方式

    适配器模式分为两类:类适配器对象适配器

    下面以苹果手机使用安卓充电线为例

    3.1 类适配器:

    被适配者

    1. /// <summary>
    2. /// 被适配者,,安卓线
    3. /// </summary>
    4. public class MyAdaptee
    5. {
    6. /// <summary>
    7. /// 你希望使用的方法
    8. /// </summary>
    9. public void CD()
    10. {
    11. Console.WriteLine("我开始充电");
    12. }
    13. }

    适配器

    1. /// <summary>
    2. /// 适配器---转接头
    3. /// </summary>
    4. public class MyAdapter : MyAdaptee, MyTarget
    5. {
    6. public void ZH()
    7. {
    8. base.CD();//使用充电功能,但是方法由CD变为了ZH
    9. }
    10. }

    目标角色

    1. /// <summary>
    2. /// 目标角色---苹果充电线,这里可以写成抽象类或者接口
    3. /// </summary>
    4. public interface MyTarget
    5. {
    6. void ZH();
    7. }

    客户端角色(使用者)

    1. /// <summary>
    2. /// 使用,客户端角色
    3. /// </summary>
    4. /// <param name="sender"></param>
    5. /// <param name="e"></param>
    6. private void WTBtn_Click(object sender, EventArgs e)
    7. {
    8. //假如你能直接使用安卓线,也就是被适配者
    9. Console.WriteLine("安卓手机需要充电\n");
    10. MyAdaptee adaptee = new MyAdaptee();
    11. adaptee.CD();
    12. //你买了苹果手机,没法直接用安卓手机冲了,加个转接头
    13. MyTarget my = new MyAdapter();
    14. Console.WriteLine("苹果手机需要充电\n");
    15. my.ZH();
    16. }

    优点:
    1、可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”
    2、可以重新定义Adaptee(被适配的类)的部分行为,因为在类适配器模式中,Adapter是Adaptee的子类
    3、仅仅引入一个对象,并不需要额外的字段来引用Adaptee(被适配者)实例(这个即是优点也是缺点)。
    缺点:
    1、用一个具体的Adapter(适配器)类对Adaptee(被适配者)和Target(目标角色)进行匹配,当如果想要匹配一个类以及所有它的子类时,类的适配器模式就不能胜任了。因为类的适配器模式中没有引入Adaptee的实例。
    2、采用了 “多继承”的实现方式,带来了不良的高耦合。 

    3.2 对象适配器:

    被适配者(与类适配器一致)

    1. /// <summary>
    2. /// 被适配者,,安卓线
    3. /// </summary>
    4. public class MyAdaptee
    5. {
    6. /// <summary>
    7. /// 你希望使用的方法
    8. /// </summary>
    9. public void CD()
    10. {
    11. Console.WriteLine("我开始充电");
    12. }
    13. }

     适配器(区别在这)

    1. /// <summary>
    2. /// 适配器---转接头
    3. /// </summary>
    4. public class MyAdapter : MyTarget
    5. {
    6. MyAdaptee adaptee = new MyAdaptee();
    7. public void ZH()
    8. {
    9. adaptee.CD();//使用充电功能,但是方法由CD变为了ZH
    10. }
    11. }

    目标角色(与类适配器一致)

    1. /// <summary>
    2. /// 目标角色---苹果充电线,这里可以写成抽象类或者接口
    3. /// </summary>
    4. public interface MyTarget
    5. {
    6. void ZH();
    7. }

    客户端角色

    1. /// <summary>
    2. /// 使用,客户端角色
    3. /// </summary>
    4. /// <param name="sender"></param>
    5. /// <param name="e"></param>
    6. private void WTBtn_Click(object sender, EventArgs e)
    7. {
    8. //假如你能直接使用安卓线,也就是被适配者
    9. Console.WriteLine("安卓手机需要充电\n");
    10. MyAdaptee adaptee = new MyAdaptee();
    11. adaptee.CD();
    12. //你买了苹果手机,没法直接用安卓手机冲了,加个转接头
    13. MyTarget my = new MyAdapter();
    14. Console.WriteLine("苹果手机需要充电\n");
    15. my.ZH();
    16. }

     优点:
    1、可以在不修改原有代码的基础上来复用现有类,很好地符合 “开闭原则”(这点是两种实现方式都具有的)
    2、采用 “对象组合”的方式,更符合松耦合。
    缺点:
    1、使得重定义Adaptee的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。 

    总结:

    由例子可以看出类适配与对象适配在于一个继承被适配者,一个实例化被适配者。 类适配器采用“多继承”的实现方式,在C#语言中,如果被适配角色是类,Target的实现只能是接口,因为C#语言只支持接口的多继承的特性。在C#语言中类适配器也很难支持适配多个对象的情况,同时也会带来了不良的高耦合和违反类的职责单一的原则,所以一般不推荐使用。对象适配器采用“对象组合”的方式,更符合松耦合精神,对适配的对象也没限制,可以一个,也可以多个,但是,使得重定义Adaptee的行为较困难,这就需要生成Adaptee的子类并且使得Adapter引用这个子类而不是引用Adaptee本身。

    END--------------------------------------------------------------------------------------------------------------------------

  • 相关阅读:
    Python面试宝典第11题:最长连续序列
    数据集成平台关于【源平台调度&任务生命周期】
    MongoDB工具命令和用户认证
    双绞线连接网卡和集线器时的制作步骤
    (文字)无框按钮设置
    【星球】【slam】研讨会 (3)ViSLAM 算法框架,原理,对比,评测 论文解读 ORB—SLAM3
    ThreeJs学习
    Spring 如何实现一个CGLlB动态代理呢?
    PlayWright(十七)- 参数化
    linux 启动引导找不到内核修复
  • 原文地址:https://blog.csdn.net/qq_44217121/article/details/143306844