C#设计模式之适配器模式与装饰器模式的实现

作者:北冥虾 时间:2021-10-30 02:54:32 

结构型设计模式

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

适配器模式

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

C#设计模式之适配器模式与装饰器模式的实现

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

适配器的使用场景:

  • 需要使用第三方SDK的核心功能,但其接口或者功能不符合需求,这时可以使用适配器对其进行兼容和扩展

  • 随着业务发展,旧接口已经不能满足需求,但重写代价又太大,这时可以使用适配器对接口功能进行扩展

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

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

C#设计模式之适配器模式与装饰器模式的实现

实现代码

//已有的旧接口,不兼容于现在的系统
   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电压

总结

优点:

  • 可以扩展和兼容现有类,灵活性高

  • 提高了类的复用,原本不能使用的类适配后能使用

缺点:

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

装饰器模式

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

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

C#设计模式之适配器模式与装饰器模式的实现

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

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

实现代码

//抽象基础类
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();
   }
}

//输出:  
//没有用装饰器的基本功能:
//我正在学习!!!
//
//使用前缀装饰器在基础功能之前做点什么
//学习前看会儿小说
//我正在学习!!!
//
//使用后缀装饰器在前缀装饰器功能之后做点什么
//学习前看会儿小说
//我正在学习!!!
//学习辛苦啦,奖励自己一包辣条

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

  • 想扩展实现类的功能,又不想添加太多子类

  • 需要动态增加和撤销功能(例如游戏技能)

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

来源:https://www.cnblogs.com/damocleses/p/16194597.html

标签:C#,设计模式,适配器模式,装饰器模式
0
投稿

猜你喜欢

  • Java的优先队列PriorityQueue原理及实例分析

    2023-03-14 08:11:00
  • java使用FileVisitor遍历文件和目录

    2021-07-17 13:25:21
  • 详解SpringBoot多跨域请求的支持(JSONP)

    2023-09-21 04:12:54
  • c# 在windows中操作IIS设置FTP服务器的示例

    2023-07-18 06:13:01
  • Mybatis plus 配置多数据源的实现示例

    2023-10-04 15:57:33
  • Java中的static关键字修饰属性和方法(推荐)

    2021-09-29 05:46:20
  • Java中Map遍历的九种方式汇总

    2023-05-02 21:57:27
  • 解决idea爆红 cant resolve symbol String的问题解析

    2023-10-11 10:47:30
  • Java结构型设计模式中建造者模式示例详解

    2023-04-24 13:37:31
  • 深入浅析Java Object Serialization与 Hadoop 序列化

    2023-07-30 17:02:16
  • 浅谈Java slf4j日志简单理解

    2021-07-07 15:49:15
  • Java 深入浅出分析Synchronized原理与Callable接口

    2021-11-26 10:27:17
  • 解决RestTemplate 请求接收自定义400+ 或500+错误

    2023-12-12 00:25:20
  • Java对象类型的判断详解

    2023-07-26 09:55:07
  • java实现订餐系统

    2023-08-12 04:39:06
  • 详解5种Java中常见限流算法

    2023-02-01 16:14:31
  • 结合mybatis-plus实现简单不需要写sql的多表查询

    2021-06-25 12:54:22
  • Android实现图片拖拉功能

    2023-03-27 14:57:51
  • AOP之事务管理<aop:advisor>的两种配置方式

    2023-11-24 22:55:06
  • java组件fileupload文件上传demo

    2022-09-14 11:00:45
  • asp之家 软件编程 m.aspxhome.com