设计之禅——外观模式
概述
平时在我们生活中,我们常常会接触学习各种各样的新事物,而能够快速吸引留住大量客户的都有一个共性,就是简单易学好上手。比如,windows和linux系统,前者比后者更加普及的原因也就是不需要经过专业系统的学习就能轻松使用。同样的,这种思想在编程界有一个专业的术语——外观模式。
定义
首先我们来看看它的定义:
外观模式定义了一个统一的接口,用来访问子系统中的一群接口,使得子系统更容易使用。
概念比较简单,也很容易懂,不过还是有些抽象,下面我通过一个例子来具体讲讲:想象一下你刚住进你的新家,如你所想的布置了 一套家庭影院系统(投屏、音箱、灯光等)。当你下班回家时,你想先泡个澡,然后开启家庭影院系统,享受惬意的生活。但是,每次回去你都得一个个的将浴缸水放上,打开灯、电视等,之后又得一个个关上,这样繁杂的操作反而会令人感到烦躁。而外观模式的作用就是他提供给你一个统一的操作按钮(比如叫影院模式),你只需要按下这一个按钮上述的家居就都自动开始工作了,看完之后再按下统一的关闭按钮就行,这样我们就能更加的惬意地享受下班生活了(PS:我之前写过一篇关于命令模式的文章,里面有一个关于命令party模式的例子,两者非常相似,注意区分本质的意图)。因此外观模式帮助我们屏蔽掉了底层那些繁杂的操作,不过他并没有屏蔽掉你对底层的直接操作,比如洗完澡就可以自动清洗浴缸了,你就可以直接按下浴缸的清洗按钮就可以了,不用等到电影看完后再一起进行。
Coding
明白外观模式的意图和原理,代码实现就非常容易了,首先是我们的智能家居类:
// 音箱
public class Amplifier {
public void on() {
System.out.println("TV is starting....");
}
public void off() {
System.out.println("TV has stopped!");
}
public void setCd() {
System.out.println("CD has set!");
}
}
// 灯光
public class RoomLamp {
public void on() {
System.out.println("Light on!");
}
public void off() {
System.out.println("Light off!");
}
}
// 浴缸
public class Bathtub {
public void on() {
System.out.println("Bathtub is working!");
}
public void off() {
System.out.println("Bathtub is stopped!");
}
}
// 电视
public class TV {
public void on() {
System.out.println("TV is starting....");
}
public void off() {
System.out.println("TV has stopped!");
}
}
接着是我们的外观类:
public class HomePartyFacade {
private TV tv;
private Bathtub bathtub;
private RoomLamp roomLamp;
private Amplifier amplifier;
public HomePartyFacade(TV tv, Bathtub bathtub, RoomLamp roomLamp, Amplifier amplifier) {
this.tv = tv;
this.bathtub = bathtub;
this.roomLamp = roomLamp;
this.amplifier = amplifier;
}
public void startParty() {
roomLamp.on();
bathtub.on();
amplifier.on();
amplifier.setCd();
tv.on();
}
public void endParty() {
roomLamp.off();
amplifier.off();
tv.off();
}
}
最后你只需要调用startParty方法就可以开始看电影了,外观模式就是这么简单。不过还没完,外观模式隐含了一个设计原则——最少知道原则(迪米特法则),那什么是最少知道原则呢?
最少知道原则
简单的说,一个类越少被其它类知道越好。为什么呢?我相信大家从开始接触编程起,就听到过高内聚,低耦合的概念,避免在代码修改时牵一发而动全身,最少知道原则就是为了降低各个类之间的耦合而努力。那要如何才能满足这个原则呢?
它提供了一些方针,就任何对象而言,在该对象的方法内,我们只调用属于以下范围的方法:
- 该对象的本身;
- 通过方法参数传进的对象;
- 当前方法实例化的所有对象;
- 该对象的所有成员变量引用的对象。
简单的说,也就是不要调用另外一次调用所返回的对象的方法(即避免链式调用)。上面的例子里没有链式调用,但是对于用户来说他只想轻松的看场电影,而不想费时费力的去操作一个个开关,所以只需要为他提供一个统一的操作接口,那就只需要记住这个操作的使用就行了,也不用担心以后升级了某些系统而又得去学一遍该如何操作新系统,厂家只需要提供一个新的外观就行了,极大的降低了用户和子系统的耦合度。 不过,我们也不难看出,最少知道原则的缺陷就在于会产生许多的外观中间类,也就使得系统变得更庞大和复杂。因此也不要盲目的遵循这一原则。
总结
外观模式和装饰者模式以及适配器模式都是通过组合来实现各自的目的,在需要简化并统一一群复杂的接口时,使用外观;需要改变接口以符合客户的预期要求时使用适配器模式;而需要给对象增加新的行为和责任时使用装饰者模式。
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 「翻译」在生物信息学中使用 GNU-Parallel
- Kafka运维小贴士 | Kafka 消息监控
- MySQL慢查询优化 | 联结原理
- MySQL千万大表优化实践
- GitHub 标星过万!计算机与网络知识总结电子书下载!
- 盘一盘,那些提效/创意的 vscode 插件
- 基于jenkins实现手动拉取码云代码,实现半自动化部署
- so easy!网页骨架屏自动生成方案(dps)
- 深夜,我偷听到程序员要对session下手……
- CAM 系列论文阅读总结
- kafka客户端指标上报Prometheus方案(已开源)
- 2020-09-25:rust中Point是结构体类型,【let p1=Point{x:25,y:25};let p2=p1;】...
- R语言在BRFSS数据中可视化分析探索糖尿病的影响因素
- R语言可视化探索BRFSS数据并逻辑回归Logistic回归预测中风
- R语言对BRFSS数据探索回归数据分析