适配器模式和外观模式(head first设计模式——6)
为什么要把适配器模式和外观模式放在同一篇文章中,主要是其相对前面的几个模式来讲会简单些并且具有相似之处。下面就分别通过例子来看理解一下两种模式,然后再进行对其进行比较。
一、适配器模式
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)的更多相关文章
- Head First 设计模式之适配器模式与外观模式
Head First设计模式之适配器模式与外观模式 前言: 之前讲过装饰者模式,将对象包装起来并赋予新的职责,这一章我们也会将对象进行包装,只不过是让它们看起来不像自己而像是别的东西.这样就可以在设计 ...
- headfirst设计模式(8)—适配器模式与外观模式
前言 这一章主要讲2个模式,一个是,适配器模式(负责将一个类的接口适配成用户所期待的),另外一个是外观模式(为子系统提供一个共同的对外接口),看完的第一反应是,为什么要把它们两放在同一章,难道它们有什 ...
- 【HeadFirst设计模式】7.适配器模式与外观模式
今晚学习完第七章,顺便做一下知识备忘. 适配器模模式: 定义:将一个类的接口,转换成客户期望的另一个接口.适配器让原本接口不兼容的类可以合作无间. 对象适配器: 类适配器: 外观模式: 提供了一个统一 ...
- 《Head First 设计模式》之适配器模式与外观模式
适配器模式(Adapter) 适配器(adapter-pattern):将一个类的接口,转换成客户期望的另一个接口.适配器让原来接口不兼容的类可以合作无间.两种形式: 对象适配器(组合) 类适配器(多 ...
- 外观模式(Facde)【设计模式】
定义:为子系统中的一组接口提供一个一致的界面,Fcade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. “外观模式(Facade pattern),是软件工程中常用的一种软件设计模式,它 ...
- Java设计模式(11)外观模式(Facade模式)
外观模式(Facade)的定义:为子系统中的一组接口提供一个一致的界面. Facade一个典型应用就是数据库JDBC的应用,如下例对数据库的操作: public class DBCompare { C ...
- Java设计模式(9)适配器模式(Adapter模式)
适配器模式定义:将两个不兼容的类纠合在一起使用,属于结构型模式,需要有Adaptee(被适配者)和Adaptor(适配器)两个身份. 为何使用适配器模式 我们经常碰到要将两个没有关系的类组合在一起使用 ...
- Head First设计模式——适配器和外观模式
前言:为什么要一次讲解这两个模式,说点骚话:因为比较简单(*^_^*),其实是他们两个有相似和有时候我们容易搞混概念. 讲到这两个设计模式与另外一个“装饰者模式”也有相似,他们三个按照结构模式分类都属 ...
- 设计模式(七):Adapter 适配器模式 -- 结构型模式
1. 概述: 接口的改变,是一个需要程序员们必须(虽然很不情愿)接受和处理的普遍问题.程序提供者们修改他们的代码;系统库被修正;各种程序语言以及相关库的发展和进化. 例子1:iphone4,你即可以 ...
随机推荐
- Oracle EBS WMS功能介绍(二)
Oracle EBS WMS功能介绍(二) (版权声明,本人原创或者翻译的文章如需转载,如转载用于个人学习,请注明出处.否则请与本人联系,违者必究) 出货物流逻辑主要包括 1. 打包.能够进 ...
- Chrome 编译错误汇总
由于各种你懂的原因,訪问google的服务总是出错,先是hosts不工作.代理也不好使,最后最终能够短暂訪问了.我的版本号还是採用svn维护的,直接svn update也不行.试试git吧,一晚上才下 ...
- 【微信小程序】解决 竖向<scroll-view>组件 “竖向滚动页面出现遮挡”问题
问题图: 问题原因: <scroll-view class="scroll-container" upper-threshold="{{sortPanelDist} ...
- TP3.2中filed和find()使用
1.总结:filed和find(),进行一维数组查询指定字段时,可以进行配合使用,获得结果:key:value; 但官方没有明确指出. 2.filed和getFiled最终的结果是不一样的,一个获得的 ...
- hduoj---Tempter of the Bone
Tempter of the Bone Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Othe ...
- AsyncTask与ProgressDialog使用笔记(安卓在背景运行耗时任务)
AsyncTask用在需要在ui线程中调用.在背景线程中执行耗时任务.并且在ui线程中返回结果的场合.下面就是一个在背景中运行的AsyncTask的实现DownloadDBTask, Android中 ...
- C# 采用钩子捕获键盘和鼠标事件-验证是否处于无人操作状态
原文地址:https://www.cnblogs.com/gc2013/p/4036414.html 全局抽象类定义 using System; using System.Collections.Ge ...
- Python 之ConfigParser 学习笔记
一.ConfigParser简介 ConfigParser 是用来读取配置文件的包.配置文件的格式如下:中括号“[ ]”内包含的为section.section 下面为类似于key-value 的配置 ...
- Hspice仿真打印某个子模块中所有信号信息
简单的说就是在你要打印的子模块中加一句:.probe v(*) i(*)就可以了,这个子模块的每一个实例都会被打印出来.
- js 与 php 时间戳的区别(毫秒与秒的计算方式)
js是以毫秒为单位计算的,php是以秒为单位计算的,所以转换时记得*/1000 //距离时间的时间戳 var suoshengshijian = <?php echo $expire_time_ ...