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

一、适配器模式

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. Linux服务器修改时区时间

    时间的一致性很关键,对于日志的分析和程序的对接都至关重要! 01.tzselect 修改时区 可以使用命令 tzselect,修改时区.操作示例: $ tzselect Please identify ...

  2. html5实现全屏的api方法

    参考地址 [进入和退出全屏] // Webkit (works in Safari5.1 and Chrome 15) element.webkitRequestFullScreen(); docum ...

  3. Matlab调用返回游标的存储过程的分析和处理

    2.Matlab调用Oracl带游标参数输出的存储过程 笔者也是将工作之中遇到的问题进行了搜集与整理,才完成该文的编写,希望能帮助到有需要的朋友. 2.1.PLSQL中的存储过程 PROCEDURE ...

  4. SVN访问版本库精细的权限控制

    SVN精细权限控制本章将详细介绍前一章所涉及的两个配置文件, svnserve.conf 和 authz.conf,通过对配置逐行的描述,来阐明其中的一些细节含义.除此之外的其他配置.安装等内容,不是 ...

  5. [CXF REST标准实战系列] 一、JAXB xml与javaBean的转换(转)

    转自:[CXF REST标准实战系列] 一.JAXB xml与javaBean的转换 文章Points: 1.不认识到犯错,然后得到永久的教训. 2.认识JAXB 3.代码实战 1.不认识到犯错,然后 ...

  6. PHP5.3新特性

    1.首先对之前滥用的语法进行了规范 众所周知PHP在语言开发过程中有一个很好的容错性,导致在数组或全局变量中包含字符串不使用引号是可以不报错的,很多业余的开发者因为懒惰而产生的安全问题十分严重,之所以 ...

  7. eclipse安装插件的方式 三种:links、eclipse中使用插件安装向导安装、直接copy插件到对应的eclipse目录 MyEclipse10安装SVN插件

    myeclipse安装插件 1.直接将插件copy到myeclipse目录下的dropins目录下(没有目录就新建一个),重启,详细参考 MyEclipse使用总结——MyEclipse10安装SVN ...

  8. C# 小叙 Encoding (一)

    前言 众所周知计算机只能识别二进制数字,如1010,1001.我们屏幕所看到的文字,字符都是和二进制转换后的结果.将我们的文字按照某种规则转换二进制存储在计算机上,这一个过程叫字符编码,反之就是解码. ...

  9. 最简易的PHP Storm调试模式开启方式

    使用的是xdebug调试工具. 其实真的很想吐槽php语言开启调试模式真的好麻烦. 据说xdebug调试工具不支持php7.0以下版本,我同事的php5.6就不支持噗嗤.. 正文: 1.先用phpSt ...

  10. Python练习笔记——计算输入日期为改年的第几天、星期几

    # 输入年月日,如:1995年12月10日,计算是该年的第几天?# 同时计算出当天是星期几? print("请依据提示依次输入您想查询的年 月 日") # 第一段代码块(年月日输入 ...