您的位置:新葡亰496net > 奥门新萄京娱乐场 > 适配器形式和外观形式,和外观形式

适配器形式和外观形式,和外观形式

发布时间:2019-10-20 23:35编辑:奥门新萄京娱乐场浏览(57)

    本文的概念内容来自深入浅出设计模式一书

    • 适配器模式,将类的接口转化为想要的接口样式。
    • 外观模式,让对象包装起来,以简化接口。

    《Head First设计模式》读书笔记

     

    一、模式动机

      在适配器模式中可以定义一个包装类,包装不兼容接口的对象,这个包装类指的就是适配器(Adapter),它所包装的对象就是适配者(Adaptee),即被适配的类。
      适配器提供客户类需要的接口,适配器的实现就是把客户类的请求转化为对适配者的相应接口的调用。也就是说:当客户类调用适配器的方法时,在适配器类的内部将调用适配者类的方法,而这个过程对客户类是透明的,客户类并不直接访问适配者类。因此,适配器可以使由于接口不兼容而不能交互的类可以一起工作。这就是适配器模式的模式动机。

    现实世界中的适配器(模式)

    图片 1

    我带着一个国标插头的笔记本电脑, 来到欧洲, 想插入到欧洲标准的墙壁插座里面, 就需要用中间这个电源适配器.

    鸭子##

    鸭子,依然是鸭子的例子。如何让一只火鸡通过包装适配器成为一个鸭子呢?
    通过适配器,让火鸡实现鸭子的接口!

    public class TurkeyAdapter implements Duck{
        Turkey Turkey;
        public TurkeyAdapter(Turkey turkey){
            this.Turkey = turkey;
        }
        public void quack(){
            turkey.globble();
        }
        public void fly(){
            turkey.fly();
        }
    }
    
    • 客户通过目标接口,调用适配器的方法对适配器发出请求。
    • 适配器使用被适配器接口把请求转换成背适配者的一个或多个调用接口。
    • 客户接受到调用的结果,但是并不能察觉这一切是适配器在作用

    适配器模式将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

    这是对象适配器模式

    适配器模式和外观者模式

    《Head First设计模式》 读书笔记08 适配器模式与外观模式

    二、模式定义

      适配器模式:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。实际上有“两种”适配器:“对象”适配器和“类”适配器。类适配器继承被适配者和目标类,而对象适配器使用组合来适配被适配者。

    图片 2

    面向对象的适配器

    图片 3

    你有个老系统, 现在来了个新供应商的类, 但是它们的接口不同, 如何使用这个新供应商的类呢?

    首先, 我们不想修改现有代码, 你也不能修改供应商的代码. 那么你只能写一个可以适配新供应商接口的类了:

    图片 4

    这里, 中间的适配器实现了你的类所期待的接口, 并且可以和供应商的接口交互以便处理你的请求.

    适配器可以看作是中间人, 它从客户接收请求, 并把它们转化为供应商可以理解的请求:

    图片 5

    所有的新代码都写在适配器里面了.

    实例:用枚举类适配到迭代器

    枚举只能对集合内元素进行遍历,而迭代器除此之外,还可以删除元素。那么实现remove()方法时,只能进行抛出异常的形式进行假实现。

    注意与装饰模式进行区别:

    • 装饰模式知识增加责任而不进行接口转变
    • 适配器是将一个接口转换成另一个接口。

    一,写在最前面

    The Adapter and Façade Patterns

     

    三、模式示例

      还记得《Head First 设计模式》里面的鸭子,具备呱呱叫和飞行的能力,对应方法是quack()和fly()。现在要用火鸡冒充样子,但是火鸡不过呱呱叫,只会咯咯叫和飞行的能力,对应方法是gobble()和fly()。

    鸭子的例子

    有这么一句话不知道您听过没有: 如果它路像个鸭子, 叫起来也像个鸭子, 那它就是个鸭子. (例如: Python里面的duck typing)

    这句话要是用来形容适配器模式就得这么改一下: 如果它走路像个鸭子, 叫起来也像个鸭子, 那么它可能是一个使用了鸭子适配器的火鸡....

    看一下代码的实现:

    鸭子接口:

    namespace AdapterPattern.Abstractions
    {
        public interface IDuck
        {
            void Quack();
            void Fly();
        }
    }
    

    野鸭子:

    using AdapterPattern.Abstractions;
    
    namespace AdapterPattern
    {
        public class MallardDuck : IDuck
        {
            public void Fly()
            {
                System.Console.WriteLine("Flying");
            }
    
            public void Quack()
            {
                System.Console.WriteLine("Quack");
            }
        }
    }
    

    火鸡接口:

    namespace AdapterPattern.Abstractions
    {
        public interface ITurkey
        {
            void Gobble();
            void Fly();
        }
    }
    

    野火鸡:

    using AdapterPattern.Abstractions;
    
    namespace AdapterPattern.Turkies
    {
        public class WildTurkey : ITurkey
        {
            public void Fly()
            {
                System.Console.WriteLine("Gobble gobble");
            }
    
            public void Gobble()
            {
                System.Console.WriteLine("I'm flying a short distance");
            }
        }
    }
    

    火鸡适配器:

    using AdapterPattern.Abstractions;
    
    namespace AdapterPattern.Adapters
    {
        public class TurkeyAdapter : IDuck
        {
            private readonly ITurkey turkey;
    
            public TurkeyAdapter(ITurkey turkey)
            {
                this.turkey = turkey;
            }
    
            public void Fly()
            {
                for (int i = 0; i < 5; i  )
                {
                    turkey.Fly();
                }
            }
    
            public void Quack()
            {
                turkey.Gobble();
            }
        }
    }
    

    测试运行:

    using System;
    using AdapterPattern.Abstractions;
    using AdapterPattern.Adapters;
    using AdapterPattern.Turkies;
    
    namespace AdapterPattern
    {
        class Program
        {
            static void Main(string[] args)
            {
                DuckTestDrive();
            }
    
            static void DuckTestDrive()
            {
                IDuck duck = new MallardDuck();
                var turkey = new WildTurkey();
                IDuck turkeyAdapter = new TurkeyAdapter(turkey);
    
                System.Console.WriteLine("Turkey says.........");
                turkey.Gobble();
                turkey.Fly();
    
                System.Console.WriteLine("Duck says.........");
                TestDuck(duck);
    
                System.Console.WriteLine("TurkeyAdapter says.........");
                TestDuck(turkeyAdapter);
            }
    
            static void TestDuck(IDuck duck)
            {
                duck.Quack();
                duck.Fly();
            }
        }
    }
    

    图片 6

    这个例子很简单, 就不解释了.

    1,为什么要将这两个设计模式写在一起?

    不仅这两个设计模式都比较简单,而且我们可以通过这两个设计模式更好的理解OO思想。

    面向对象适配器

      假设已有一个软件系统,你希望它能和一个新的厂商类库搭配使用,但是这个新的厂商所设计出来的接口不同于旧厂商的接口。

    图片 7

      你不想改变现有的代码,解决这个问题,你可以写一个类,将厂商接口转换成你所期望的接口。

    图片 8

     

    客户使用适配器的过程如下:

      1.客户通过目标接口调用适配器的方法对适配器发出请求。

      2.适配器使用被适配者接口把请求转换成被适配者的一个或多个调用接口。

      3.客户接收到调用的结果,但并未察觉这一切是适配器在起转换作用。

     

    C 代码实现

    #include <iostream>
    
    using namespace std;
    
    class Duck
    {
    public:
        Duck();
        ~Duck();
        virtual void quack() = 0;
        virtual void fly() = 0;
    
    private:
    
    };
    
    Duck::Duck()
    {
    }
    
    Duck::~Duck()
    {
    }
    
    class MallardDuck : public Duck
    {
    public:
        MallardDuck();
        ~MallardDuck();
        void quack() { cout << "绿头鸭:呱呱叫" << endl; }
        void fly() { cout << "绿头鸭:在飞行" << endl; }
    
    private:
    
    };
    
    MallardDuck::MallardDuck()
    {
    }
    
    MallardDuck::~MallardDuck()
    {
    }
    
    class Turkey
    {
    public:
        Turkey();
        ~Turkey();
        virtual void gobble() = 0;
        virtual void fly() = 0;
    
    private:
    
    };
    
    Turkey::Turkey()
    {
    }
    
    Turkey::~Turkey()
    {
    }
    
    class WildTurkey : public Turkey
    {
    public:
        WildTurkey();
        ~WildTurkey();
        void gobble() { cout << "野火鸡:咯咯叫" << endl; }
        void fly() { cout << "野火鸡:在飞行" << endl; }
    
    private:
    
    };
    
    WildTurkey::WildTurkey()
    {
    }
    
    WildTurkey::~WildTurkey()
    {
    }
    
    class TurkeyAdapter : public Duck
    {
    public:
        TurkeyAdapter(Turkey* turkey);
        ~TurkeyAdapter();
        void quack() { turkey_->gobble(); }
        void fly() { turkey_->fly(); }
    
    private:
        Turkey* turkey_;
    
    };
    
    TurkeyAdapter::TurkeyAdapter(Turkey* turkey)
        :turkey_(turkey)
    {
    }
    
    TurkeyAdapter::~TurkeyAdapter()
    {
    }
    
    int _tmain(int argc, _TCHAR* argv[])
    {
        MallardDuck duck;
        duck.fly();
        duck.quack();
        WildTurkey turkey;
        Duck* turkeyAdapter = new TurkeyAdapter(&turkey);
        turkeyAdapter->fly();
        turkeyAdapter->quack();
        system("pause");
        return 0;
    }
    

    理解适配器模式

    图片 9

    Client 客户实现了某种目标接口, 它发送请求到适配器, 适配器也实现了该接口, 并且适配器保留着被适配者的实例, 适配器把请求转化为可以在被适配者身上执行的一个或者多个动作.

    客户并不知道有适配器做着翻译的工作.

    其他:

    适配器可以适配两个或者多个被适配者.

    适配器也可以是双向的, 只需要实现双方相关的接口即可.

    2,在本章节的最后会引入了最少知识设计原则。

    适配器模式定义

      适配器模式将一个类的接口,转换成客户期望的另一个接口。

      适配器让原本接口不兼容的类可以合作无间。

     

    运行结果:

    图片 10

    适配器模式定义

    适配器模式把一个类的接口转化成客户所期待的另一个接口. 适配器让原本因接口不兼容而无法一起工作的类成功的工作在了一起.

    类图:

    图片 11

    其中 Client只知道目标接口, 适配器实现了这个目标接口, 适配器是通过组合的方式与被适配者结合到了一起, 所有的请求都被委托给了被适配者.

    二,适配器模式

    适配器模式类图

     图片 12

      使用对象组合,以修改的接口包装被适配者,被适配者的任何子类,都可以搭配着适配器使用。

      把客户和接口绑定起来,而不是和实现绑定起来。

      我们可以使用数个适配器,每一个都负责转换不同的后台类。

      或者,也可以加上新的实现,只要它们遵守目标接口就可以。

     

    四、分析总结

      适配器模式有两种形式:对象适配器和类适配器。类适配器需要用到多重继承。

    对象适配器和类适配器

    一共有两类适配器: 对象适配器类适配器.

    之前的例子都是对象适配器.

    为什么没有提到类适配器? 

    因为类适配器需要多继承, 这一点在Java和C#里面都是不可以的. 但是其他语言也许可以例如C ?

    它的类图是这样的:

    图片 13

    这个图看着也很眼熟, 这两种适配器唯一的区别就是: 类适配器同时继承于目标和被适配者, 而对象适配器使用的是组合的方式来把请求传递给被适配者.

    1,生活中的适配器

    如果你自己接过水管或者自己接过洗衣机的水管头,你肯定有过类似的体验,无论你怎么接,它都会漏水,然后去店里一问,就知道有水管转换接头这么个东西。他可以让两个粗细不同对接不上的水管无缝的对接在一起。

    对象和类的适配器

      实际上有两种适配器:对象适配器类适配器

      上面一个图是对象适配器的图。

      类适配器,你需要多重继承才能实现它,这在Java和C#中是不可能的。但是当你使用多重继承语言的时候,还是可能遇到这样的需求。

      类适配器的类图:

     图片 14

      对象适配器和类适配器使用两种不同的适配方法,分别是组合继承

     

    优点:

    • 将目标类和适配者类解耦,通过引入一个适配器类来重用现有的适配者类,而无须修改原有代码。
    • 增加了类的透明性和复用性,将具体的实现封装在适配者类中,对于客户端类来说是透明的,而且提高了适配者的复用性。
    • 灵活性和扩展性都非常好,通过使用配置文件,可以很方便地更换适配器,也可以在不修改原有代码的基础上增加新的适配器类,完全符合“开闭原则”。

    通过鸭子的例子来认识两种适配器的角色

    类适配器:

    图片 15

    类适配器里面, 客户认为它在和鸭子谈话, 目标就是鸭子类, 客户调用鸭子上面的方法. 火鸡没有和鸭子一样的方法, 但是适配器可以接收鸭子的方法调用并把该动作转化为调用火鸡上面的方法. 适配器让火鸡可以响应一个针对于鸭子的请求, 实现方法就是同时继承于鸭子类和火鸡类

    对象适配器:

    图片 16

    对象适配器里, 客户仍然认为它在和鸭子说话, 目标还是鸭子类, 客户调用鸭子类的方法, 适配器实现了鸭子类的接口, 但是当它接收到方法调用的时候, 它把该动作转化委托给了火鸡. 火鸡并没有实现和鸭子一样的接口, 多亏了适配器, 火鸡(被适配者)将会接收到客户针对鸭子接口的方法调用.

    两种适配器比较:

    对象适配器: 使用组合的方式, 不仅能是配一个被适配者的类, 还可以适配它的任何一个子类.

    类适配器: 只能适配一个特定的类, 但是它不需要重新实现整个被适配者的功能. 而且它还可以重写被适配者的行为.

    对象适配器: 我使用的是组合而不是继承, 我通过多写几行代码把事情委托给了被适配者. 这样很灵活.

    类适配器: 你需要一个适配器和一个被适配者, 而我只需要一个类就行.

    对象适配器: 我对适配器添加的任何行为对被适配者和它的子类都起作用.

    ...

    2,面向对象的适配器

    两套系统的使用接口不一致,但是你想将两套系统给对接起来,你就必须设计一个适配器将两个接口对接起来,这样你就可以在一个系统中调用另外一个系统的实现方案。
      适配器模式定义:将一个类的接口,转换成客户期望的另一个接口。适配器让原本接口不兼容的类可以合作无间。

    装饰者模式和适配器模式的比较

      装饰者模式用来添加一些新的功能,而维持接口不变

      适配器模式关注于接口的转换

     

    又一个适配器的例子 (Java)

     老版本的java有个接口叫做Enumeration:

    图片 17

    后来又出现了一个Iterator接口:

    图片 18

    现在我想把Enumeration适配给Iterator:

    图片 19

    这个应该很简单, 可以这样设计:

    图片 20

    只有一个问题, Enumeration不支持remove动作, 也就是说适配器也无法让remove变成可能, 所以只能这样做: 抛出一个不支持该操作的异常(C#: NotSupportedException), 这也就是适配器也无法做到完美的地方.

    看一下这个java适配器的实现:

    图片 21

    三,适配器模式(引入书中的demo,而并不只是简单设计一个水管的接口转换,后面会介绍到)

    外观模式

      外观模式(Façade Pattern)是另一个改变接口的模式,它改变接口的原因是为了简化接口。

      它将一个或数个类的复杂的一切都隐藏在背后,只显露出一个干净美好的外观。

     

      书中例子:构造了一个复杂的家庭影院,但是每次看电影都要进行很多繁琐的操作。

      使用一个外观类,将家庭影院的诸多组件视为一个子系统,通过调用这个子系统中的一系列方法,来完成外观类中的看电影操作。

      这样,客户代码可以只调用外观所提供的一个方法,就完成各种操作。

     

      外观没有封装子系统的类,外观只是提供简化的接口。

      外观只是提供你更直接的操作,并未将原来的子系统阻隔起来,如果你需要子系统类的更高层功能,还是可以使用原来的子系统。

      可以为一个子系统创建许多个外观。

     

      外观模式不只是简化了接口,也将客户从组件的子系统中解耦。

      比如,你想升级你的子系统(你的家庭影院),如果当初你的客户代码是针对外观而不是针对子系统编写的,现在你就不需要改变客户代码,只需要修改外观代码。

     

    装饰模式 vs 适配器模式

    你可能发现了, 这两个模式有一些相似, 那么看看它们之间的对话:

    装饰模式: 我的工作全都是关于职责, 使用我的时候, 肯定会涉及到在设计里添加新的职责或行为.

    适配器模式: 我主要是用来转化接口.

    装饰模式: 当我装饰一个大号接口的时候, 真需要写很多代码.

    适配器模式: 想把多个类整合然后提供给客户所需的接口, 这也是很麻烦的工作. 但是熟话说: "解耦的客户都是幸福的客户..."

    装饰模式: 用我的时候, 我也不知道已经套上多少了装饰器了.

    适配器模式: 适配器干活的时候, 客户也不知道我们的存在. 但是我们允许客户在不修改现有代码的情况下使用新的库, 靠我们来转化就行.

    装饰模式: 我们只允许为类添加新的行为, 而无需修改现有代码.

    适配器模式: 所以说, 我们总是转化我们所包裹的接口.

    装饰模式: 我们则是扩展我们包装的对象, 为其添加行为或职责.

    从这段对话可以看出, 装饰模式和适配器模式的根本区别就是它们的意图不同.

     

    1,将火鸡伪装成一个Duck这是我们的目的,看到这句话可能会想到装饰者模式,注意比较,先看适配器模式代码,一个Duck接口和一个Turkey接口。

    public interface Duck {
    public void quak();
    public void fly();
    }
    
    public interface Turkey {
    public void gobble();
    public void fly();
    }
    

    外观模式和适配器模式

      外观模式和适配器模式的差异,在于它们的意图。

      适配器模式的意图是,改变接口以符合客户的期望。

      而外观模式的意图是,提供子系统的一个简化接口

     

    另一种情况

    现在我们可以知道, 适配器模式会把类的接口转化成客户所需要的样子.

    但是还有另外一种情况也需要转化接口, 但却处于不同的目的: 简化接口. 这就需要使用外观模式(Facade Pattern). 

    外观模式会隐藏一个或多个类的复杂性, 并提供一个整洁干净的外观(供外界使用).

    现在在总结一下这三种模式的特点:

    装饰者模式: 不修改接口, 但是添加职责.

    适配器模式: 把一个接口转化成另外一个.

    外观模式: 把接口变得简单.

     

    2,先看两个Duck和Turkey实现类

    public class MallarDuck implements Duck{
    
    @Override
    public void quak() {
        System.out.println("Quack~");
    }
    
    @Override
    public void fly() {
        System.out.println("I'm flying~");
    }
    }
    
    public class WildTurkey implements Turkey{
    
    @Override
    public void gobble() {
        System.out.println("Gobble gobble");
    }
    
    @Override
    public void fly() {
        System.out.println("I'm flying a short distance");
    }
    }
    

    外观模式定义

      外观模式提供了一个统一的接口,用来访问子系统中的一群接口。

      外观定义了一个高层接口,让子系统更容易使用。

     

    一个需求 -- 家庭影院

    这个家庭影院有DVD播放器, 投影仪, 屏幕, 环绕立体音响, 还有个爆米花机:

    图片 22

    你可能花了几周的时间去连线, 组装.....现在你想看一个电影, 步骤如下:

    1. 打开爆米花机
    2. 开始制作爆米花
    3. 把灯光调暗
    4. 把屏幕放下来
    5. 把投影仪打开
    6. 把投影仪的输入媒介设为DVD
    7. 把投影仪调整为宽屏模式
    8. 打开功放
    9. 把功放的输入媒介设为DVD
    10. 把功放设置为环绕立体声
    11. 把功放的音量调到中档
    12. 把DVD播放器打开
    13. 开始播放DVD

    具体用程序描述就是这样的:

    图片 23

    上一篇:指令情势,完毕命令设计形式

    下一篇:没有了