《Head First 设计模式》《设计模式之禅(第二版)》 学习笔记,码云同步更新中
如有错误或不足之处,请一定指出,谢谢~
目录
设计原则
- “依赖倒置”原则
- 未完待续...
设计模式
- 设计模式——策略模式
- 设计模式——装饰者模式
- 设计模式——观察者模式
- 设计模式——简单工厂
- 设计模式——工厂方法模式
- 设计模式——抽象工厂模式
- 设计模式——单例模式
- 设计模式——命令模式
- 设计模式——适配器模式
- 设计模式——门面模式(外观模式)
- 未完待续...
门面模式(外观模式)(Facade Pattern)
定义
- 要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。 门面模式提供一个高层次的接口, 使得子系统更易于使用。
结构
- Facade:门面(外观)角色,将收到的请求委派给相应的子系统
- Subsystem:子系统角色,处理请求,并不知道门面的存在,只是一个正常的客户端
优点
- 减少系统的相互依赖
- 提高灵活性。无论子系统内部如何变化,只需要修改门面一个对象就可以
- 提供安全性。子系统只能被门面对象访问
缺点
不符合“开闭原则”。门面对象有可能会被修改,有一定的风险性。
使用场景
- 为一个复杂的子系统向外界提供一个简单明了的访问接口
- 保持子系统的独立性
- 降低因个人代码质量差而对整体质量的影响,“画地为牢”,规定只能在指定子系统中开发,然后对外提供访问接口
示例
现在来模拟一个写信、寄信的过程,步骤如下:
/**
* 写信寄信流程
*/
public class LetterWriter {
/**
* 写内容
*/
public void writeContext(String context) {
System.out.println(context);
}
/**
* 写信封
*/
public void writeAddress(String address) {
System.out.println(address);
}
/**
* 装进信封
*/
public void putIntoEnvelope() {
System.out.println("mua~");
}
/**
* 投递
*/
public void sendLetter() {
System.out.println("send the letter...");
}
}
来模拟这个过程:
public class Test {
public static void main(String[] args) {
LoveLetter loveLetter = new LoveLetter();
loveLetter.writeContext("loving u...");
loveLetter.writeAddress("To my lover...");
loveLetter.putIntoEnvelope();
loveLetter.sendLetter();
}
}
我们发现,写信的过程很复杂,步骤顺序不能乱,也不能漏掉,这还是只有4个步骤的情况。
让我们用门面模式来改造一下:引入一个邮局类,我们只需要告诉他内容和地址,他会帮我们完成整个过程。
public class PostOffice {
private LetterWriter letterWriter = new LetterWriter();
public void sendLetter(String context, String address) {
letterWriter.writeContext(context);
letterWriter.writeAddress(address);
letterWriter.putIntoEnvelope();
letterWriter.sendLetter();
}
}
这时我们写信就会变得很简单:
public class Test {
public static void main(String[] args) {
PostOffice postOffice = new PostOffice();
postOffice.sendLetter("love u", "xxx");
}
}
这样带来的一个好处就是,简化客户端的操作。至于你邮局到底做了什么,我不关心,也没必要关心。信送到了就行。比如有一天突然多了一个步骤:警察需要检查信件内容。我们只需要修改门面:
public class PostOffice {
private LetterWriter letterWriter = new LetterWriter();
private Police police = new Police();
public void sendLetter(String context, String address) {
letterWriter.writeContext(context);
letterWriter.writeAddress(address);
// 增加警察检查内容步骤
police.checkLetter(letterWriter);
letterWriter.putIntoEnvelope();
letterWriter.sendLetter();
}
}
到这里还没完,细心的朋友们可能已经发现了,门面类参与了子系统的业务逻辑,这是我们非常不希望看到的,而且违反了单一职责原则。门面应该只提供访问子系统的路径。我们再来封装一下,在子系统中再封装一下
/**
* 寄信流程
*/
public class LetterProcess {
private LetterWriter letterWriter = new LetterWriter();
private Police police = new Police();
public void sendLetter(String context, String address) {
letterWriter.writeContext(context);
letterWriter.writeAddress(address);
police.checkLetter(letterWriter);
letterWriter.putIntoEnvelope();
letterWriter.sendLetter();
}
}
然后修改门面:
public class PostOffice {
private LetterProcess letterProcess = new LetterProcess();
public void sendLetter(String context, String address) {
letterProcess.sendLetter(context, address);
}
}
ok,大功告成。
注意事项
- 一个子系统可以有多个门面,例如:
- 门面已经非常庞大,可以按功能分类拆分(增删改查)
- 多个高层系统访问门面,权限不同,可访问的方法不同,根据权限创建不同的门面
- 门面不参与子系统业务逻辑
- 子系统的业务逻辑是会经常变动的,但门面在系统投入使用后应该保持稳定。 子系统不应该依赖门面才能被访问。
门面模式 vs 适配器模式
门面模式其实和上一节的适配器模式非常非常相似,他们都封装了(来自一个类或多个类的)一组接口,让客户与子系统解耦。但他们的意图完全不同:
* 适配器模式:将一组接口转化为另一组不同的接口
* 门面模式:简化接口