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

 更新时间:2022年4月27日 21:19  点击:275 作者:北冥虾

结构型设计模式

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

适配器模式

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

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

适配器的使用场景:

  • 需要使用第三方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();
    }
}
 
//输出:  
//没有用装饰器的基本功能:
//我正在学习!!!
//
//使用前缀装饰器在基础功能之前做点什么
//学习前看会儿小说
//我正在学习!!!
//
//使用后缀装饰器在前缀装饰器功能之后做点什么
//学习前看会儿小说
//我正在学习!!!
//学习辛苦啦,奖励自己一包辣条 

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

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

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

以上就是C#设计模式之适配器模式与装饰器模式的实现的详细内容,更多关于C#适配器模式 装饰器模式的资料请关注猪先飞其它相关文章!

原文出处:https://www.cnblogs.com/damocleses/p/16194597.html

[!--infotagslink--]

相关文章

  • C#实现简单的登录界面

    我们在使用C#做项目的时候,基本上都需要制作登录界面,那么今天我们就来一步步看看,如果简单的实现登录界面呢,本文给出2个例子,由简入难,希望大家能够喜欢。...2020-06-25
  • 浅谈C# 字段和属性

    这篇文章主要介绍了C# 字段和属性的的相关资料,文中示例代码非常详细,供大家参考和学习,感兴趣的朋友可以了解下...2020-11-03
  • C#中截取字符串的的基本方法详解

    这篇文章主要介绍了C#中截取字符串的的基本方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-11-03
  • C#实现简单的Http请求实例

    这篇文章主要介绍了C#实现简单的Http请求的方法,以实例形式较为详细的分析了C#实现Http请求的具体方法,需要的朋友可以参考下...2020-06-25
  • C#连接SQL数据库和查询数据功能的操作技巧

    本文给大家分享C#连接SQL数据库和查询数据功能的操作技巧,本文通过图文并茂的形式给大家介绍的非常详细,需要的朋友参考下吧...2021-05-17
  • C#中new的几种用法详解

    本文主要介绍了C#中new的几种用法,具有很好的参考价值,下面跟着小编一起来看下吧...2020-06-25
  • 使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序)

    这篇文章主要介绍了使用Visual Studio2019创建C#项目(窗体应用程序、控制台应用程序、Web应用程序),小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#开发Windows窗体应用程序的简单操作步骤

    这篇文章主要介绍了C#开发Windows窗体应用程序的简单操作步骤,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-04-12
  • C#从数据库读取图片并保存的两种方法

    这篇文章主要介绍了C#从数据库读取图片并保存的方法,帮助大家更好的理解和使用c#,感兴趣的朋友可以了解下...2021-01-16
  • C#和JavaScript实现交互的方法

    最近做一个小项目不可避免的需要前端脚本与后台进行交互。由于是在asp.net中实现,故问题演化成asp.net中jiavascript与后台c#如何进行交互。...2020-06-25
  • C++调用C#的DLL程序实现方法

    本文通过例子,讲述了C++调用C#的DLL程序的方法,作出了以下总结,下面就让我们一起来学习吧。...2020-06-25
  • 轻松学习C#的基础入门

    轻松学习C#的基础入门,了解C#最基本的知识点,C#是一种简洁的,类型安全的一种完全面向对象的开发语言,是Microsoft专门基于.NET Framework平台开发的而量身定做的高级程序设计语言,需要的朋友可以参考下...2020-06-25
  • C#变量命名规则小结

    本文主要介绍了C#变量命名规则小结,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2021-09-09
  • C#绘制曲线图的方法

    这篇文章主要介绍了C#绘制曲线图的方法,以完整实例形式较为详细的分析了C#进行曲线绘制的具体步骤与相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25
  • C# 中如何取绝对值函数

    本文主要介绍了C# 中取绝对值的函数。具有很好的参考价值。下面跟着小编一起来看下吧...2020-06-25
  • c#自带缓存使用方法 c#移除清理缓存

    这篇文章主要介绍了c#自带缓存使用方法,包括获取数据缓存、设置数据缓存、移除指定数据缓存等方法,需要的朋友可以参考下...2020-06-25
  • c#中(&&,||)与(&,|)的区别详解

    这篇文章主要介绍了c#中(&&,||)与(&,|)的区别详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-06-25
  • 经典实例讲解C#递归算法

    这篇文章主要用实例讲解C#递归算法的概念以及用法,文中代码非常详细,帮助大家更好的参考和学习,感兴趣的朋友可以了解下...2020-06-25
  • C#学习笔记- 随机函数Random()的用法详解

    下面小编就为大家带来一篇C#学习笔记- 随机函数Random()的用法详解。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧...2020-06-25
  • C#中list用法实例

    这篇文章主要介绍了C#中list用法,结合实例形式分析了C#中list排序、运算、转换等常见操作技巧,具有一定参考借鉴价值,需要的朋友可以参考下...2020-06-25