适配器和外观模式

一、   基本概述

1:现实中存在三角插头适配成双插头,等其他各种形式的适配器来连接不兼容的两个物体。同理在代码中也存在适配器模式来兼容两个不同的代码接口。

2:KTV包间打开一个启动开关,就打开party模式(音响、屏幕、灯光、换气、点歌台等),一个简单的开关来控制其他更多的任务。同理在代码中也存在外观模式来简化子系统(更多任务)的功能。

二、详细说明

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

这个适配器模式充满良好的OO设计原则,使用对象组合,以修改的接口包装被适配者,这种做法还有额外的优点,那就是被适配者的任何子类,都可以搭配着适配器使用。

问:一个适配器需要做多少“适配”工作?如果我需要实现一个很大的目标接口,似乎有“很多”工作要做。

答:实现一个适配器所需要进行的工作,的确和目标接口的大小成正比。如果不用适配器,你就必须改写客户端的代码来调用这个新的接口,将会花许多力气来做大量的调查工作和代码改写工作。相比之下,提供一个适配器类,将所有的改变封装在一个类中,是比较好的做法。

问:一个适配器只能够封装一个类吗?

答:适配器模式的工作是将一个接口转换成另一个。虽然大多数的适配器模式所采取的例子都是让一个适配器包装一个适配器者,但我们都知道这个世界其实复杂多了,所以你可能絮叨一些状况,需要让一个适配器包装多个被被适配者。这涉及另一个模式,被称为外观模式,人们常常将外观模式和适配器模式混为一谈。

问:万一我的系统中新旧并存,旧的部分期望旧的厂商接口,但我们却已经使用新厂商的接口编写了这一部分。

答:可以创建一个双向的适配器,支持两边的接口。想创建一个双向的适配器,就必须实现所涉及的两个接口,这样这个适配器可以当做旧的接口、新的接口使用。

其实适配器模式有两种形式,对象适配器(在上面进行了说明)、和类适配器,类适配器需要用到多重继承(在C#中是不可能的)。

2.外观模式:提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用。

下图为外观模式的示意图

假如有一个家庭影院,在我们观赏电影时,必须先执行一些任务(普通方式)。

(1)  打开爆米花机

(2)  开始爆米花机

(3)  将灯光调暗

(4)  放下屏幕

(5)  打开投影机

(6)  ...

(7)  省略更多步骤

(8)  开始播放

面对以上中情况,可以使用外观来简化接口。如下图

问:如果外观封装了子系统的类,那么需要底层功能的客户如何接触这些了类?

答:外观没有“封装”子系统的类,外观只提供简化的接口。所以客户如果觉得有必要,依然可以直接使用子系统的类。这是外观模式一个很好的特征,提供简化的接口的同时,依然将系统完整的功能暴露出来,以供需要的人使用。

问:每个子系统只能有一个外观吗?

答:不,你可以为一个子系统创建许多个外观。

问:可不可以这样说,适配器模式和外观模式之间的差异在于,适配器包装一个类,而外观可以代表许多类?

答:不对!适配器模式将一个或多个类接口变成客户所期望的一个接口。虽然大多数教科书所采用的例子中适配器只适配一个类,但是你可以适配许多类来提供一个接口让客户编码。类似的,一个外观也可以只针对一个拥有复杂接口的类提供简化的接口。两种模式的差异,不在于它们“包装”了几个类,而是在于它们的意图。(可在总结中查看)

 

3.设计原则:最少知识原则:只和你的密友谈话。

最少知识原则告诉我们要减少对象之间的交互,只留下几个“密友”。这个原则希望我们在设计中,不要让太多的类耦合在一起,免得修改系统中一部分,会影响到其他部分。如果许多类之间相互依赖,那么这个系统就会变成一个易碎的系统,它需要花许多成本维护,也会因为太复杂而不容易被其他人了解。

如何不要赢得太多的朋友和影响太多的对象。

这个原则提供了一些方针:就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:

(1)  该对象本身;

(2)  被当做方法的参数而传递进来的对象;

(3)  此方法所创建或实例化的任何对象;

(4)  对象的任何组件(把组件想象成是被实例化的变量所引用的任何对象);

问:还有另一个原则,叫做得墨忒耳法则(Law of Demeter),它和最少知识原则有什么关系?

答:其实两个名词指的是同一个原则,我们倾向于使用最少知识原则来称呼它是因为以下两个原因。

(1)  这个名字更直接。

(2)  法则(Law)给人的感觉是强制的。事实上,没有任何原则是法律,所有的原则都应该在有帮助的时候才遵守。

所有的设计都不免需要折衷(在抽象和速度之间取舍,在空间和时间之间平衡...)。虽然原则提供了方针,但在采用之前,必须全盘考虑所有的因素。

问:采用最少知识原则有什么缺点吗?

答:虽然这个原则减少了对象之间的依赖,研究显示这会减少软件的维护成本;但是采用这个原则也会导致更多的“包装”类被制造出来,以处理和其他组件的沟通,这可能会导致复杂度和开发时间的增加,并降低运行时的性能。

4.总结:

对比三种模式的意图:

装饰者:不改变接口,但加入责任(将一个对象包装起来以增加新的行为和责任)。

适配器:将一个接口转成另一个接口(将一个对象包装起来以改变其接口)。

外观:让接口更简单(将一群对象“包装”起来以简化其接口)。

三、代码列表

//适配器模式相关测试类
public interface Iterator<T>
{
    bool HasNext();
    T Next();
    void Remove();
}
public class EnumerationIterator<T> : Iterator<T>
{
    private IEnumerator<T> enumerator;

    public EnumerationIterator(IEnumerable<T> enumerable)
    {
        this.enumerator = enumerable.GetEnumerator();
    }

    public bool HasNext()
    {
        return enumerator.MoveNext();
    }

    public T Next()
    {
        return enumerator.Current;
    }

    public void Remove()
    {
        throw new NotSupportedException();
    }
}
//外观模式的相关测试类
public class Amplifier
{
    public void On()
    {
        Console.WriteLine("Top-0-Line Amplifier on");
    }

    public void SetDvd(DvdPlayer dvd)
    {
        Console.WriteLine("Top-0-Line Amplifier setting DVD player to Top-0-Line DVD Player");
    }

    public void SetVolume(int i)
    {
        Console.WriteLine("Top-0-Line Amplifier surround sound on (5 seakers, 1 subwoofer)");
        Console.WriteLine("Top-0-Line Amplifier setting volume to 5");
    }

    public void Off()
    {
        Console.WriteLine("Top-0-Line Ampliier off");
    }
}
public class CdPlayer
{

}
public class Doors
{
    public void Lock()
    {

    }
}
public class DvdPlayer
{
    private string movie;
    public void On()
    {
        Console.WriteLine("Top-0-Line DVD Player on");
    }

    public void Play(string movie)
    {
        this.movie = movie;
        Console.WriteLine("Top-0-Line DVD Player playing \"{0}\"", movie);
    }

    public void Stop()
    {
        Console.WriteLine("Top-0-Line DVD Player stopped \"{0}\"", movie);
    }

    public void Eject()
    {
        Console.WriteLine("Top-0-Line DVD Player eject");
    }

    public void Off()
    {
        Console.WriteLine("Top-0-Line DVD Player off");
    }
}
public class Engine
{
    public void Start()
    {

    }
}
public class PopcornPopper
{
    public void On()
    {
        Console.WriteLine("Popcorn Popper on");
    }

    public void Pop()
    {
        Console.WriteLine("Popcorn popper popping popcorn!");
    }

    public void Off()
    {
        Console.WriteLine("Popcorn Popper off");
    }
}
public class Projector
{
    public void On()
    {
        Console.WriteLine("Top-0-Line Projector on");
    }

    public void WideScreenMode()
    {
        Console.WriteLine("Top-0-Line Projector in widescreen mode (19*9 aspect ratio)");
    }

    public void Off()
    {
        Console.WriteLine("Top-0-Line Projector off");
    }
}
public class Screen
{
    public void Down()
    {
        Console.WriteLine("Theater Screen going down");
    }

    public void Up()
    {
        Console.WriteLine("Theater Screen going up");
    }
}
public class TheaterLights
{
    public void Dim(int i)
    {
        Console.WriteLine("Theater Ceiling Lights dimming to {0}%", i);
    }

    public void On()
    {
        Console.WriteLine("Theater Ceiling Lights on");
    }
}
public class Tuner
{

}
public class HomeTheaterFacade
{
    private Amplifier amp;
    private Tuner tuner;
    private DvdPlayer dvd;
    private CdPlayer cd;
    private Projector projector;
    private TheaterLights lights;
    private Screen screen;
    private PopcornPopper popper;

    public HomeTheaterFacade(Amplifier amp, Tuner tuner, DvdPlayer dvd, CdPlayer cd, Projector projector, TheaterLights lights, Screen screen, PopcornPopper popper)
    {
        this.amp = amp;
        this.tuner = tuner;
        this.dvd = dvd;
        this.cd = cd;
        this.projector = projector;
        this.lights = lights;
        this.screen = screen;
        this.popper = popper;
    }

    public void WatchMovie(string movie)
    {
        Console.WriteLine("Get ready to watch a movie...");
        popper.On();
        popper.Pop();
        lights.Dim(10);
        screen.Down();
        projector.On();
        projector.WideScreenMode();
        amp.On();
        amp.SetDvd(dvd);
        amp.SetVolume(5);
        dvd.On();
        dvd.Play(movie);
    }

    public void EndMovie()
    {
        Console.WriteLine("Shutting movie theathe down...");
        popper.Off();
        lights.On();
        screen.Up();
        projector.Off();
        amp.Off();
        dvd.Stop();
        dvd.Eject();
        dvd.Off();
    }
}
//执行测试
[Test]
public void HomeTheaterTestDrive()
{
    Amplifier amp = new Amplifier();
    Tuner tuner = new Tuner();
    DvdPlayer dvd = new DvdPlayer();
    CdPlayer cd = new CdPlayer();
    Projector projector = new Projector();
    Screen screen = new Screen();
    TheaterLights lights = new TheaterLights();
    PopcornPopper popper = new PopcornPopper();
    HomeTheaterFacade homeTheater = new HomeTheaterFacade(amp, tuner, dvd, cd, projector, lights, screen, popper);
    homeTheater.WatchMovie("Raiders of the lost ark");
    homeTheater.EndMovie();
}

------------------------以上内容根据《Head First Design Mode》进行整理

时间: 2024-11-06 07:13:10

适配器和外观模式的相关文章

读书笔记之设计模式-适配器和外观模式

结构型:Adapter与Facade(适配器和外观模式) 一般作为阅读材料,首先想要明确的是我现在了解的设计模式的初衷,即为了解决什么问题. 适配器,如果有买过港版Iphone在内地使用的人应该会有三角大插头必须接一个转换器才能在一般的插座上使用的情况,当然这只是比较直观的感受.其实我们平时用的手机充电器都是属于适配器,试想如果我们没有这个充电器,我们如何利用普通插座给手机充电? 适配器的定义:将手头上所持有的接口转换成我们需要的接口(业务场景:经常是为了适配旧程序或者对接2套系统的时候使用,当

iOS开发-适配器和外观模式

适配器模式,属于结构型模式,其主要作用是将一个类的接口转换成客户希望的另外一个接口.适配器模式使得原本由于接口不兼容而不能一起工作的那些类可以一起工作.适配器模式有对象适配器和类适配器两种,类适配器模式需要语言实现多继承,OC不支持多继承,所以一般我们都实现对象适配器.外观模式提供了一个统一的接口,用来访问子系统中的一群接口,外观定义了一个高层接口,让子系统更容易使用.适配器是为了转换接口,外观模式是为了简化接口. 适配器模式 对象适配器模式UML类图: 关于适配模式最常见的就是手机充电的例子,

设计模式 8 —— 适配器和外观模式

设计模式目录: 设计模式 1 ——观察者模式 设计模式 2 —— 装饰者模式 设计模式 3 —— 迭代器和组合模式(迭代器) 设计模式 4 —— 迭代器和组合模式(组合) 设计模式 5 —— 工厂模式 设计模式 6 —— 单件模式 设计模式 7 —— 命令模式 设计模式 8 —— 适配器和外观模式 概述 第1部分 问题引入 第2部分 适配器模式定义 第3部分 对象和类的适配者 第4 部分 适配器模式与装饰者模式区别 第5 部分 外观模式 第1 部分 问题引入 OO适配器是什么,现实中到处都是.比

设计模式: 自己手动写一适配器和外观模式

适配器模式: 将一个类的接口,转换成客户所期待的接口,适配器让原本不兼容的类可以合作无间.有两种形式:类适配器和对象适配器.前者需要用到多重继承(java不支持),后者要用到组合. 外观模式: 提供了一个统一的简化的接口,用来访问子系统里的一群接口.外观定义了一个高层接口,让子系统更容易使用. 适配器的类图 适配器的一个简单源码: package adapter; /** * 鸭子接口 * @author Arvon * */ public interface Duck { public voi

适配器模式Adapter、外观模式Facade-- 学习HeadFirst设计模式记录

? 适配器模式:将一个类的接口,转换成客户期望的另一个接口,适配器让原本不兼容的类可以合作无间. 外观模式 :提供了一个统一的接口,用来访问子系统中的一群接口.外观定义了一个高层接口,让子系统更容易使用. ? 对象适配器: 类适配器: ? 外观模式: ?

java/android 设计模式学习笔记(14)---外观模式

这篇博客来介绍外观模式(Facade Pattern),外观模式也称为门面模式,它在开发过程中运用频率非常高,尤其是第三方 SDK 基本很大概率都会使用外观模式.通过一个外观类使得整个子系统只有一个统一的高层的接口,这样能够降低用户的使用成本,也对用户屏蔽了很多实现细节.当然,在我们的开发过程中,外观模式也是我们封装 API 的常用手段,例如网络模块.ImageLoader 模块等.其实我们在开发过程中可能已经使用过很多次外观模式,只是没有从理论层面去了解它. 转载请注明出处:http://bl

设计模式之适配器模式与外观模式

适配器模式将一个类的接口,转换成客户期望的另一个接口.适配器让原本接口不兼容的类可以合作无间. 例子:火鸡变鸭子. 先定义一个鸭子接口. package cn.sp.test06; /** * 鸭子 * @author 2YSP * */ public interface Duck { //具备呱呱叫 和 飞行的能力 public void quack(); public void fly(); } package cn.sp.test06; /** * 绿头鸭是鸭子的子类 * @author

《Head First 设计模式》之适配器模式与外观模式

适配器模式(Adapter) 适配器(adapter-pattern):将一个类的接口,转换成客户期望的另一个接口.适配器让原来接口不兼容的类可以合作无间.两种形式: 对象适配器(组合) 类适配器(多重继承):在Java中不能实现 外观(facade-pattern):提供了一个统一的接口,用来访问子系统中的一群接口.外观定义了一个高层接口,让子系统更容易使用. 原则 最少知识原则:只和你的密友谈话 要点: 当需要使用一个现有的类而其接口不符合需要时,使用适配器.适配器改变接口以符合客户期望.

[设计模式]适配器模式与外观模式

之前的装饰者模式,是将对象包装起来,赋予新的功能.适配器模式则是包装对象,使其接口看起来不像自己而是别的对象,就是将类的接口转换成想要的接口,以便实现不同的接口.而外观模式则是将对象包装起来以简化其接口. 适配器模式讲一个类的接口,转换成客户期望的另一个接口.适配器让原本接口不兼容的类可以合作无间. 外观模式提供了一个统一的接口,用来访问子系统中的一群接口.外观定义了一个高层接口,让子系统更容易使用.这个模式也表现了一个设计原则,最少知识原则.