• 设计模式(七)—— 适配器模式


    模式简介


    将一个类的接口转换成客户希望的另外一个接口。Adapter模式使得原本由于接口不兼容而不能一起工作的类可以一起工作。

    Adpater模式又叫包装器(Wrapper)。适配器模式既可以作为类结构型模式,也可以作为对象结构型模式。

    结构说明


    Adapter模式一般包含两个版本:类适配器模式对象适配器模式。类适配器模式通过继承一个类与接口(Java、C#等不支持多重继承的语言),在接口的实现中调用适配者的方法,如下图所示。

    对象适配器通过对象的组合,适配器类中包含适配者的实例对象,在接口的实现方法中调用实例对象的方法。

    角色说明

    • Adapter

    适配器,对Adaptee与Target进行适配。

    • Adaptee

    适配者,提供一个方法,但这个方法与Target接口不兼容,需要适配。

    • Target

    特定领域相关使用的接口。

    类适配器和对象适配器比较

    1. 类适配器的优点:因为Adapter是Adaptee的子类,所以可以在Adapter中重写Adaptee的方法,使得Adapter更加灵活。
    2. 对象适配器的优点:一个对象适配器可以把Adaptee和它的子类全都适配到目标接口上。

    示例分析


    假设我们有一个转换器程序,能够将系统中的数据导出到不同格式的文件,只需要实现IExporter接口,客户端就可以动态调用程序输出不同格式的文件。

    interface IExporter
    {
        void Export();
    }
    
    class ExportToExcel : IExporter
    {
        public void Export()
        {
            Console.WriteLine("Export to Excel...");
        }
    }
    
    //客户端调用
    static void Main(string[] args)
    {
        IExporter exporter = new ExportToExcel();
        exporter.Export();
        Console.ReadLine();
    }
    

    公司购买了一个第三方类库PDFWriter,使用这个类库可以轻松地将数据导入到PDF文件中,但是,它只提供了WriteToPDF方法,这与我们的IExporter接口不兼容。

    //这里仅仅是为了方便展示示例提供,我们没有第三方类库的源码
    class PDFWriter
    {
        public void WriteToPDF()
        {
            Console.WriteLine("Write to PDF..");
        }
    }
    

    由于没有源码,无法进行修改。当然,即使可以修改,也不应该为了实现一个应用去实现特定领域的接口。为了使我们的转换器程序也能够动态调用WriteToPDF方法,添加类PDFAdapter。

    class PDFAdapter : IExporter
    {
        private PDFWriter writer = new PDFWriter();
        public void Export()
        {
            writer.WriteToPDF();
        }
    }
    

    通过适配器进行动态调用。

    static void Main(string[] args)
    {
        IExporter exporter = new PDFAdapter();
        exporter.Export();
        Console.ReadLine();
    }
    

    双向适配器(Two-way Adapter)


    如果适配器同时包含目标类和适配者类的引用,适配者可以通过它调用目标类中的方法,目标类也可以通过它调用适配者类中的方法,那么这个适配器就是一个双向适配器,结构示意图如下:

    代码如下:

    interface IAdaptee
    {
        void SpecificRequest();
    }
    
    class ConcreteAdaptee : IAdaptee
    {
        public void SpecificRequest()
        {
            Console.WriteLine("SpecificRequest by ConcreteAdaptee");
        }
    }
    
    interface ITarget
    {
        void Request();
    }
    
    class ConcreteTarget : ITarget
    {
        public void Request()
        {
            Console.WriteLine("Request by ConcreteTarget");
        }
    }
    
    class Adapter : IAdaptee, ITarget
    {
        private IAdaptee adaptee;
        private ITarget target;
        public Adapter(IAdaptee adaptee)
        {
            this.adaptee = adaptee;
        }
        public Adapter(ITarget target)
        {
            this.target = target;
        }
        public void Request()
        {
            adaptee.SpecificRequest();
        }
    
        public void SpecificRequest()
        {
            target.Request();
        }
    }
    

    可插入适配器(Pluggable Adapter)


    观察以上介绍的Adapter类,有一个共性,就是只能适配固定的Adaptee(编译阶段就已经确定),不够灵活。而可插入适配器则是可以适配包含不同接口的Adaptee,换句话来说,可插入适配器允许适配在程序运行时动态传入的适配者。下面我们介绍使用Action委托的方式来实现可插入适配器:

    首先创建Cat类和Dog类,并且这两个类拥有不同的方法。

    class Cat
    {
        public void Miaow()
        {
            Console.WriteLine("miao miao miao");
        }
    
    }
    
    class Dog
    {
        public void Bark()
        {
            Console.WriteLine("wang wang wang");
        }
    
    }
    

    创建可插入适配器,由于不确定适配者的类型及接口,只知道将来客户端要调用MakeSound方法。也就是说,适配器本身不知道将来会适配什么样的适配者,也不知道调用适配者的哪个方法,这一切都是由客户端动态传入。

    class PluggableAdapter
    {
        public Action MakeSound { get; private set; }
        public PluggableAdapter(Action makeSound)
        {
            this.MakeSound = makeSound;
        }
    }
    

    客户端调用,向PluggableAdapter的构造函数传入委托方法,完成适配。

    static void Main(string[] args)
    {
        Dog dog = new Dog();
        (new PluggableAdapter(dog.Bark)).MakeSound();
        Console.ReadLine();
    }
    

    适用场景


    1. 使用一个已经存在的类,但它提供的方法与系统中定义的接口不兼容

    2. 创建一个可以复用的类,用于与一些彼此之间没有太大关联的类,包括一些可能在将来引进的类一起工作

    3. 使用一些已经存在的子类,但是不可能对每一个都进行子类化以匹配它们的接口。对象适配器可以适配它们父类的接口

    源码下载


    dotnet-design-pattern_adapter

  • 相关阅读:
    java 23种设计模式 深入理解
    ORACLE 一条记录 某字段值以';'拆分为多条记录
    rabbitmq集群故障恢复
    ORACLE 时间加减操作
    Asp.net MVC Razor输出字符串方法(js中嵌入razor)
    C# ToString() 数据格式
    DOM的整个知识体系
    EF 连接模式
    EF Code First 数据库连接方式
    使用border实现提示框的
  • 原文地址:https://www.cnblogs.com/Answer-Geng/p/9099026.html
Copyright © 2020-2023  润新知