为什么要把适配器模式和外观模式放在同一篇文章中,主要是其相对前面的几个模式来讲会简单些并且具有相似之处。下面就分别通过例子来看理解一下两种模式,然后再进行对其进行比较。

一、适配器模式

1.1适配器模式的定义

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

感觉是从定义就可以看出来怎么实现的设计模式。简单的来说就是把不符合要求的类,通过实现期望的接口来来达到以假乱真的效果。好了,废话不多说,还是通过例子来说明。

1.2适配器的例子

最常见的例子是三孔插座和两孔插座,如果墙上有一个三孔插座,但是我们的充电器又只能使用两孔的插座,那么我们通常是接一条带有两孔和三孔的插座其插头是三个的,以适应不同的需求。在以上的例子中,我们来简单分析一下适配器模式中的对象有哪些:

适配器模式的对象:

1.请求对象(手机)

2.适配器对象(带有两孔和三孔的插座)

3.需要适配的对象(三孔插座)

4.请求对象所需要的接口。(插座要有两孔)

接来下就使用代码实现一下:

 class Program
{
static void Main(string[] args)
{
Mobile mobile = new Mobile();
ThreeHole threeHole = new ThreeHole();
LineWithTwoHole lineWithTwoHole = new LineWithTwoHole(threeHole);
mobile.Charge(lineWithTwoHole);
Console.ReadKey();
}
} /// <summary>
/// 手机类
/// </summary>
public class Mobile
{
public void Charge(ITwoHole twoHole)
{
twoHole.Connect();
AddPower();
}
public void AddPower()
{
Console.WriteLine("电量增加中。。。。");
}
} /// <summary>
/// 两孔插座接口
/// </summary>
public interface ITwoHole
{
void Connect();
} /// <summary>
/// 三孔插座
/// </summary>
public class ThreeHole
{
public void Connect()
{
LeftConnect();
RightConnect();
ExtraConnect();
} public void LeftConnect()
{
Console.WriteLine("零线接通中。。。");
}
public void RightConnect()
{
Console.WriteLine("火线接通中。。。。。");
}
public void ExtraConnect()
{
Console.WriteLine("底线接通中。。。。");
}
} public class LineWithTwoHole:ITwoHole
{
private ThreeHole threeHole;
public LineWithTwoHole(ThreeHole threeHole)
{
this.threeHole = threeHole;
}
public void Connect()
{
threeHole.LeftConnect();
threeHole.RightConnect();
}
}

运行结果:

接下来看看适配器模式的类图:

1.3适配器模式类图

适配器模式的关系:请求对象引用需要适配的接口,适配器引用需要适配对象,适配器需要通过被适配对象来实现需要适配的接口。

1.4适配器模式需要注意的问题

在三孔插座转化成两孔插座时,工作量很小,是只调用了三孔插座的左孔和右孔。但是也有可能是需要很大的目标接口,就会有很多工作要做。也即实现适配器所需要的工作和目标接口的大小成正比。

一个适配器真的只需要一个被适配者吗?

其实适配的目标是一个完成接口,在需要比较大型的接口时,可能需要多个被适配者才能实现。

二、外观模式

2.1外观模式的定义

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

外观模式让我想起了一套设备,不知道大家有没有照过大头贴,我是没有照过,但是我见过照大头贴的设备。其是由电脑,打印机,白炽灯,相机组成,基本的操作是:打开电脑,打印机,白炽灯,相机设备,然后按一下拍照开关,接着点击打印,照片就出来了,最终关闭所有装置。

各个设备要去单独的打开,拍完照,又要单独去关闭,显然不是很方便。其实如果有一个开关能控制所有设备的开关,那么就能省下好多步骤,就可以大大的提高生产率了。

2.2外观模式例子

其实这个就和我们的外观模式相一致,我们可以定义为这套设备定义统一的接口,接口中的方法包括打开,拍照,打印,关闭,在使用外观模式之前,直接在客户端调用每个设备的开关,以及其他的操作,直接将客户端和各个设备耦合在了一起。由于代码比较简单,在此不作演示,下面就只演示使用外观模式的例子。

 class Program
{
static void Main(string[] args)
{ ITakePicture takephoto = new TakePicture();
takephoto.Open();
takephoto.TakePictures();
takephoto.Printing();
takephoto.Close();
Console.ReadKey();
}
} public class Computer
{
public void Open()
{
Console.WriteLine("电脑开了");
}
public void Close()
{
Console.WriteLine("电脑已经关闭了");
}
} public class Light
{
public void Open()
{
Console.WriteLine("灯开了");
}
public void Close()
{
Console.WriteLine("灯关闭了");
}
} public class Print
{
public void Open()
{
Console.WriteLine("打印机打开了");
}
public void Printing()
{
Console.WriteLine("打印完成");
}
public void Close()
{
Console.WriteLine("打印机已关闭");
}
} public class Cinema
{
public void Open()
{
Console.WriteLine("照相机打开了");
}
public void Close()
{
Console.WriteLine("照相机已关闭");
}
public void TakePictures()
{
Console.WriteLine("已经拍完了");
}
} public interface ITakePicture
{
void Open();
void TakePictures();
void Printing();
void Close();
} public class TakePicture : ITakePicture
{
Computer computer = new Computer();
Light light = new Light();
Print print = new Print();
Cinema cinema = new Cinema();
public TakePicture()
{ }
public void Open()
{
light.Open();
computer.Open();
print.Open();
cinema.Open();
} public void TakePictures()
{
cinema.TakePictures();
} public void Printing()
{
print.Printing();
} public void Close()
{
light.Close();
computer.Close();
print.Close();
cinema.Close();
}
}

输出结果:

可以看出外观模式的好处是:简化了设备的接口,同时降低了客户端和各个设备的耦合。例如,照相机的照相方法改成了TakePhoto(),那么也只需要在外观的实现方法中修改该设备的方法即可,不用去修改客户端代码。

2.3外观模式需要注意的地方

外观封装了子设备的类,那么客户端是如何调用子设备的方法的?

通过上面的代码可以看出,对于每一个单独的设备的方法的调用我们也是将设备的方法封装到了外观类中(如cinema的takepicture方法被封装在外观类的一个方法中),这里使用了一个原则:

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

客户端只和外观谈话,不和子设备,如相机,电脑等谈话,降低了客户端和设备的耦合度。

下面给出最少知识原则的指导思想:

就任何对象而言,在该对象的方法内,我们只应该调用属于以下范围的方法:

1.该对象本身

2.被当做方法的参数而传过来的对象

3.该方法所创建或实例化的任何对象

4.对象的任何组件

尽管我们有时可以在客户端使用takephoto.print.Printing();但是最好也不要使用,因为客户端这样不仅耦合了TakePhoto类还耦合了Print。所以尽可能自己封装子设备的方法,以便减少客户端和子设备的耦合。

三、适配器、外观、装饰者模式对比

在介绍装饰者模式时,引出了一个开闭原则,即对修改关闭,对扩展开放。装饰者模式主要强调的是在不改变原有类的基础上,添加新功能。

适配器模式,主要是对适配对象进行调整,以便适合消费者的需求。从而达到消费者和被适配者解耦的目的。

外观模式的特点主要是简化接口,以及减少客户端对外观组件的耦合。因为如果客户端变化来,组件的子系统变化了,不用影响客户端。除此之外,在封装组件时,适当的在外观类中添加一些自己想要的规则。如上面例子中各设备的开关顺序,或者拍照和打印之前其设备是否开启等。

四、总结和源码

本文主要通过举例来介绍了适配器和外观模式,最后对比了适配器,外观,装饰者模式的区别。

源码

适配器模式和外观模式(head first设计模式——6)的更多相关文章

  1. Head First 设计模式之适配器模式与外观模式

    Head First设计模式之适配器模式与外观模式 前言: 之前讲过装饰者模式,将对象包装起来并赋予新的职责,这一章我们也会将对象进行包装,只不过是让它们看起来不像自己而像是别的东西.这样就可以在设计 ...

  2. headfirst设计模式(8)—适配器模式与外观模式

    前言 这一章主要讲2个模式,一个是,适配器模式(负责将一个类的接口适配成用户所期待的),另外一个是外观模式(为子系统提供一个共同的对外接口),看完的第一反应是,为什么要把它们两放在同一章,难道它们有什 ...

  3. 【HeadFirst设计模式】7.适配器模式与外观模式

    今晚学习完第七章,顺便做一下知识备忘. 适配器模模式: 定义:将一个类的接口,转换成客户期望的另一个接口.适配器让原本接口不兼容的类可以合作无间. 对象适配器: 类适配器: 外观模式: 提供了一个统一 ...

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

    适配器模式(Adapter) 适配器(adapter-pattern):将一个类的接口,转换成客户期望的另一个接口.适配器让原来接口不兼容的类可以合作无间.两种形式: 对象适配器(组合) 类适配器(多 ...

  5. 外观模式(Facde)【设计模式】

    定义:为子系统中的一组接口提供一个一致的界面,Fcade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. “外观模式(Facade pattern),是软件工程中常用的一种软件设计模式,它 ...

  6. Java设计模式(11)外观模式(Facade模式)

    外观模式(Facade)的定义:为子系统中的一组接口提供一个一致的界面. Facade一个典型应用就是数据库JDBC的应用,如下例对数据库的操作: public class DBCompare { C ...

  7. Java设计模式(9)适配器模式(Adapter模式)

    适配器模式定义:将两个不兼容的类纠合在一起使用,属于结构型模式,需要有Adaptee(被适配者)和Adaptor(适配器)两个身份. 为何使用适配器模式 我们经常碰到要将两个没有关系的类组合在一起使用 ...

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

    前言:为什么要一次讲解这两个模式,说点骚话:因为比较简单(*^_^*),其实是他们两个有相似和有时候我们容易搞混概念. 讲到这两个设计模式与另外一个“装饰者模式”也有相似,他们三个按照结构模式分类都属 ...

  9. 设计模式(七):Adapter 适配器模式 -- 结构型模式

    1. 概述: 接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题.程序提供者们修改他们的代码;系统库被修正;各种程序语言以及相关库的发展和进化.  例子1:iphone4,你即可以 ...

随机推荐

  1. 网线/双绞线上各标识CAT, AWG, PR, UTP/STP/FTP/SFTP的含义

    CAT5, CAT5e, CAT6 表示网线类别, 常见的有 CAT5, CAT5e, CAT6分别表示五类, 超五类, 六类网线 24AWG, 26AWG American Wire Gauge是美 ...

  2. cocos2dx 3.2 解决输入框(TextField,TextFieldTTF) 中文乱码问题

    近期开发cocos2dx 项目,做一个小游戏.(个人喜欢用最新版本号) 没系统学习就是问题多多,遇到了非常多问题,比方全部的opengl api都必须在主线程中调用, 这让我在多线程载入方面吃了不少亏 ...

  3. 【VUE+laravel5.4】vue给http请求 添加请求头数据

    1.适用于 ajax和普通的http请求 2.vue添加用法如下: <script type="text/javascript src="/dist/js/app.min.j ...

  4. word使用宏定义来统一设置图片大小

    1. 首先手动拖拽将图片调到需要的格式,点击图片在格式选项中查看图片的宽高 2. 视图中点击宏新建 3. 编辑框中输入以下代码并保存,由于我只需要统一宽度,所以将统一高度的代码注释 Sub 图片格式统 ...

  5. PHP中curl的使用

    cURL 函数 curl_close — 关闭一个cURL会话 curl_copy_handle — 复制一个cURL句柄和它的所有选项 curl_errno — 返回最后一次的错误号 curl_er ...

  6. eclipse 在Servers窗口创建一个Tomcat 6.0 Server失败

    web项目部署到tomcat除了用eclipse插件,eclipse也有一个Servers窗口来部署.   问题背景:Servers窗口,我之前创建过一个Tomcat v6.0 Server,后来我把 ...

  7. checkbox选择框如果被选中value值就可以传过去,没有被选中value就不能穿过去(调试了近一天,坑爹的说)

    因为要适合各种分辨率,所以将原来的单选按钮radio换成单个的checkbox

  8. Python max() 函数

    描述 max() 函数返回给定参数的最大值,参数可以为序列. 语法 以下是 max() 函数的语法: max( x, y, z, .... ) 参数 x -- 数值表达式. y -- 数值表达式. z ...

  9. Android-SQLiteOpenHelper

    Android-SQLiteOpenHelper 一 概念 是对SOLiteDatabase的封装.主要用于建立和版本号控制,方便我们去建立库表结构 二 用法 又一次封装一个MySqliteOpenH ...

  10. java日志 -logback的使用和logback.xml详解(转)

    一.logback的介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网站: http://logback.qos.ch.它当前分为下面下个模块: logback-core:其它两 ...