• 适配器、装饰器


    适配器、装饰器

    结构型设计模式

    创建型设计模式主要是为了解决创建对象的问题,而结构型设计模式则是为了解决已有对象的使用问题。

    适配器模式

    适配器模式比较好理解,因为在我们的日常生活中就很常见,如耳机转换线、充电器适配器、插座等,举个最常见的例子:

    插座

    插座就是个适配器,将一个接口扩展为多个接口,将墙上的双孔接口转换为三孔接口。而这也就是适配器的作用:将一个接口转换为用户期望的另一个接口。

    适配器的使用场景:

    • 需要使用第三方SDK的核心功能,但其接口或者功能不符合需求,这时可以使用适配器对其进行兼容和扩展
    • 随着业务发展,旧接口已经不能满足需求,但重写代价又太大,这时可以使用适配器对接口功能进行扩展

    注意:适配器是对已有资源进行兼容和扩展,属于一种折中的方式,如果可以的话,尽量重构系统而不是使用适配器

    继承器的实现有两种方式:继承组合,基于合成复用的原则,组合优于继承,所以应尽量使用组合的方式实现适配器。类图如下:

    适配器类图

    实现代码:

      //已有的旧接口,不兼容于现在的系统
      public interface IAmericanElectrictService
      {
      int Get110VElectric();
      }
       
      //adaptee,需要适配的SDK
      public class AmericanElectrictService : IAmericanElectrictService
      {
      public int Get110VElectric()
      {
      Console.WriteLine("美国的电压是110v,只能提供110V的电压");
      return 110;
      }
      }
       
      //已有接口,现在的系统需要使用这个接口
      public interface IChineseElectricService
      {
      int Get220VElectric();
      }
       
      //适配器,采取组合的方式
      //这里是为了适配已有接口,所以实现了这个接口
      public class AdapterPattern : IChineseElectricService
      {
      private readonly IAmericanElectrictService _service;
       
      public AdapterPattern(IAmericanElectrictService service)
      {
      this._service = service;
      }
      public int Get220VElectric()
      {
      var electric = this._service.Get110VElectric();
      Console.WriteLine("劈里啪啦劈里啪啦,经过一番操作,现在电压转换为220V的了");
      return electric + 110;
      }
      }
       
      //使用适配器,将110V电压转换成220V
      public class AdapterRunner : IRunner
      {
      public void Run()
      {
      //实际情况中,adaptee有可能是已有SDK,有可能是interface,通过IOC容器对应具体实现类
      var americanElectric = new AmericanElectrictService();
      var electric = americanElectric.Get110VElectric();
      Console.WriteLine($"获得了{electric}V电压");
      Console.WriteLine("使用适配器");
      var adapter = new AdapterPattern(americanElectric);
      electric = adapter.Get220VElectric();
      Console.WriteLine($"使用适配器后获得了{electric}V电压");
      }
      }
      //输出
      //------------------------------------
      //美国的电压是110v,只能提供110V的电压
      //获得了110V电压
      //使用适配器
      //美国的电压是110v,只能提供110V的电压
      //劈里啪啦劈里啪啦,经过一番操作,现在电压转换为220V的了
      //使用适配器后获得了220V电压

    总结

    优点:

    • 可以扩展和兼容现有类,灵活性高
    • 提高了类的复用,原本不能使用的类适配后能使用

    缺点:

    • 适配器本质是套一层,如果使用过多,可能导致系统混乱,甚至出现套中套的复杂情况

    装饰器模式

    利用继承和组合,在不改变现有结构的情况下对功能进行扩展的模式称为装饰器模式

    装饰器模式和适配器模式很像,但侧重点不一样。适配器的重心在于兼容已有系统,而装饰器的重心在于功能扩展。装饰器的类图如下:

    装饰器

    上图中,基础装饰器继承抽象类,每个装饰器继承前一个装饰器,一步一步添加功能,并且所有装饰器都用到具体实现类,因为需要扩展具体功能。

    这里其实就能看出一些装饰器和适配器的区别,适配器和装饰器都使用组合来包装已有类,不同的是装饰器用到了继承。装饰器的核心原则是里氏替换原则,即父类一定能被子类替换而不影响现有代码。实现代码如下:

      //抽象基础类
      public abstract class AbstractStudent
      {
      public abstract void Study();
      }
       
      //具体实现类
      public class Student : AbstractStudent
      {
      public override void Study()
      {
      Console.WriteLine("我正在学习!!!");
      }
      }
       
      //基础装饰器,什么也不做
      //注意,这里标记为抽象类,此后的装饰器以此为基础
      public abstract class BaseDecorator : AbstractStudent
      {
      private readonly AbstractStudent _student;
      public BaseDecorator(AbstractStudent student)
      {
      this._student = student;
      }
      //这里使用override还是Virtual取决于AbstractStudent基础类是抽象类还是接口
      public override void Study()
      {
      this._student.Study();
      }
      }
       
      //前缀装饰器,在调用具体功能前做点什么
      public class PreDecorator : BaseDecorator
      {
      public PreDecorator(AbstractStudent student) : base(student)
      {
      }
      public override void Study()
      {
      Console.WriteLine("学习前看会儿小说");
      base.Study();
      }
      }
       
      //后缀装饰器,在调用具体功能后做点什么
      public class NextDecorator : PreDecorator
      {
      public NextDecorator(AbstractStudent student) : base(student)
      {
      }
      public override void Study()
      {
      base.Study();
      Console.WriteLine("学习辛苦啦,奖励自己一包辣条");
      }
      }
       
      //测试代码
      public class DecoratorRunner : IRunner
      {
      public void Run()
      {
      Console.WriteLine("没有用装饰器的基本功能:");
      var student = new Student();
      student.Study();
      Console.WriteLine();
       
      Console.WriteLine("使用前缀装饰器在基础功能之前做点什么");
      var preDecorator = new PreDecorator(student);
      preDecorator.Study();
      Console.WriteLine();
       
      Console.WriteLine("使用后缀装饰器在前缀装饰器功能之后做点什么");
      //注意:这里传入的前缀装饰器,在前缀装饰器的基础之上做扩展
      var nextDecorator = new NextDecorator(student);
      nextDecorator.Study();
      }
      }
       
      //输出:
      //没有用装饰器的基本功能:
      //我正在学习!!!
      //
      //使用前缀装饰器在基础功能之前做点什么
      //学习前看会儿小说
      //我正在学习!!!
      //
      //使用后缀装饰器在前缀装饰器功能之后做点什么
      //学习前看会儿小说
      //我正在学习!!!
      //学习辛苦啦,奖励自己一包辣条
    C# 折叠 复制 全屏

    可以看出,装饰器其实就是利用组合+继承(实现)+override不断包装和更新对象,使其功能得到扩展。装饰器是用于替换继承的设计模式,主要使用场景如下:

    • 想扩展实现类的功能,又不想添加太多子类
    • 需要动态增加和撤销功能(例如游戏技能)

    装饰器的优点在于灵活,耦合性低,且不会改变现有结构。缺点则是嵌套过多会增加系统复杂度。

  • 相关阅读:
    Java WebSocket通信Demo
    JAVA FTP/SFTP 上传下载文件
    SpringMVC+MyBatis 事务中 基于注解的声明式事务
    Java 调用支付宝接口
    linux安装Tomcat
    使用cxf发布restful的webservice
    restful的webservice
    oracle时间比较和分页查询
    jenkins问题
    linux安装jenkins
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/16571062.html
Copyright © 2020-2023  润新知