小知识,大挑战!本文正在参与 “程序员必备小知识” 创作活动
初衷 :因架构(开发)场景(需求)而使用设计模式,莫为了使用设计模式而设计架构场景!
记录此文的时候,我在想What?Why?Who?When?Where?How?
设计模式共23种,分为三种类型
- 创建型模式:单例模式、工厂模式、抽象工厂模式、建造者模式、原型模式
- 结构型模式:代理模式、装饰模式、外观模式、享元模式、桥接模式、组合模式、适配器模式
- 行为型模式:观察者模式、策略模式、中介者模式、模版方法模式、命令模式、迭代器模式、职责链模式(责任链模式)、备忘录模式、解释器模式(Interpreter模式)、状态模式、访问者模式
基本概念
个人建议:文字看的烦的话,可以先看图,然后直接看代码,最后在看文字描述 ~
外观模式又名门面模式,其属于对象结果模式的一种,当客户类与子系统通讯时必须通过一个统一的外部对象进行通讯(内部为多个复杂的子系统提供一个一致的接口),使这些子系统更加容易被访问;因对外有一个统一接口,外部应用程序不用关心内部子系统的具体的细节,这样会大大降低应用程序的复杂度,提高了程序的可维护性 ~
使用场景
- 当客户端与多个子系统之间存在很大的联系时,为复杂的模块或子系统提供外界访问的模块,引入外观模式可将它们分离,从而提高子系统的独立性和可移植性
- 整个系统流程完善,且子系统功能独立,同时子系统在此流程中使用又较多,就可以使用外观模式(个人建议系统流程如果有几套的话,可以同时尝试使用模板方法模式)
- 代码优化、重构阶段,某些子系统角色属于统一流程内的操作,同时调用频率高,出口较多,可以考虑通过外观模式统一出口
优缺点
优点
外观(Facade)模式是“迪米特法则”的典型应用
- 降低了客户类与子系统之间的耦合性,不会因子系统改变直接影响到客户类
- 降低了客户类操作的子系统对象数量,一定程度屏蔽了子系统组件
- 降低了大型软件系统中的编译依赖性,简化了系统在不同平台之间的移植过程,因为编译一个子系统不会影响其他的子系统,也不会影响外观对象
缺点
- 不能完全限制客户类调用子系统类(完全可以将外观角色的方法套用在临时开发中,但是这样有点违背外观模式了,毕竟此模式帮我们统一了入口,很大程度上降低了耦合性)
- 增加新的子系统可能需要修改外观类或客户端的源代码,违背了“开闭原则” (从一定程度我并不认为这是缺点,而可以把它看做代码的扩展性,但是当系统到中后期的阶段这个或许能作为一个缺点)
角色划分
- 子系统角色
具体的逻辑实现者,实现了整个系统当前节点的功能,客户可通过外观角色进行访问 - 外观角色
为多个子系统对外提供一个共同接口 - 客户角色(此角色是通用的,适当可以忽略)
通过一个外观角色访问各个子系统的功能
模式区别
门面模式、代理模式、中介者模式的区别
代理模式和外观者模式这两种模式主要不同就是代理模式针对的是单个对象,而外观模式针对的是所有子类
- 中介者模式:A,B之间的对话通过C来传达。A,B可以互相不认识(减少了A和B对象间的耦合,个人感觉更像MVC模式,中后期业务繁重,可以间接升级到MVP模式)
- 代理模式:A要送B礼物,A,B互相不认识,那么A可以找C来帮它实现送礼物的愿望(封装了A对象)
- 外观模式:A和B都要实现送花,送巧克力的方法,那么我可以创造外观角色C类,创建子系统角色D、E 分别实现送花送巧克力的方法,然后再C类中调用D、E(实际执行者还是D、E,只是封装了A,B子类,达成了统一出口)
常规实现
Demo - 思想
我们作为客户,当我们去饭馆吃饭的时候,一般过程都是点餐、吃饭、走人 ~ 其实这里点餐、吃饭、走人就是我们的一套系统,但是这里主讲的是饭店对客人的一套系统 !
如从我们进入饭店,其实就已经属于到了饭店这个外观角色
中,在到接待员的点餐、厨师的炒菜、服务员的送餐其实都是这个流程中的子系统角色
了,而我们作为客户角色
只和饭店这个外观角色进行了沟通!进没有亲自去和每一个子系统角色交流,这就是很好的一个外观模式的例子 ~
作为客户,我只需要和饭店这个外观角色沟通即可,内部具体实现我不管 > , <
Demo - 效果
子系统角色 - 接待员
import android.util.Log;
/**
* @author MrLiu
* @date 2020/7/1
* desc 子系统角色 - 接待员
*/
public class Subsystem_Receptionist {
public void work() {
Log.e("tag", "子系统 - 接待员:客户到来,接待点菜");
}
}
子系统角色 - 厨师
子系统角色的内部方法不需要每个都一致!毕竟每个子系统的职责不同!我这里部分方法名(work)相同只是写了他们的核心职责而已
import android.util.Log;
/**
* @author MrLiu
* @date 2020/7/1
* desc 子系统角色 - 厨师
*/
public class Subsystem_Chef {
public void before() {
Log.e("tag", "子系统 - 厨师:洗菜");
}
public void work() {
Log.e("tag", "子系统 - 厨师:十八般厨艺开炒");
}
public void after() {
Log.e("tag", "子系统 - 厨师:出锅");
}
}
子系统角色 - 服务员
/**
* @author MrLiu
* @date 2020/7/1
* desc 子系统角色 - 服务员
*/
public class Subsystem_Waiter {
public void work() {
Log.e("tag", "子系统 - 服务员:端菜到餐桌");
}
}
外观角色 - Facade
/**
* @author MrLiu
* @date 2020/7/1
* desc 外观角色
*/
public class Facade {
private final Subsystem_Receptionist receptionist;
private final Subsystem_Chef chef;
private final Subsystem_Waiter waiter;
public Facade() {
receptionist = new Subsystem_Receptionist();
chef = new Subsystem_Chef();
waiter = new Subsystem_Waiter();
}
/**
* 接待
*/
public void reception() {
receptionist.work();
}
/**
* 做饭 - 前
*/
public void cook_before() {
chef.before();
}
/**
* 做饭
*/
public void cook() {
chef.work();
}
/**
* 做饭 - 后
*/
public void cook_after() {
chef.after();
}
/**
* 送菜
*/
public void send() {
waiter.work();
}
/**
* 整个流程
*/
public void serviceAll() {
reception();
cook();
send();
}
}
客户角色(使用)
import android.util.Log;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Facade facade = new Facade();
Log.e("tag", "--封装了已有流程(有点像模板方法)--");
facade.serviceAll();
Log.e("tag", "--单个功能点,自个组装--");
facade.reception();
facade.cook_before();
facade.cook();
facade.cook_after();
facade.send();
}
}
思维扩展
- 关于外观角色中子系统实例的生成,可以在子类中采用单例模式
- 关于在外观角色中有具体的某一套或多套系统流程时,可以封装采用模板方法模式
- 当多个子系统角色内拥有共性的行为方法,可以采用接口或者继承类进行实现,不过继承的话,代码容易越搞越多,得不偿失
- 外观角色中的方法是直接声明还是通过接口声明,我觉得是根据场景来定义的,接口的作用除了统一意外,还有复用的功效,如果确定此接口在别处还可以复用的话,可以尝试通过接口声明外观角色中的方法
针对以上的几点个人建议,我优化了外观角色、子系统角色,具体如下 ~
实现效果
外观角色 - Facade
/**
* @author MrLiu
* @date 2020/7/1
* desc 外观角色
*/
public class Facade {
private final Subsystem_Receptionist receptionist;
private final Subsystem_Chef chef;
private final Subsystem_Waiter waiter;
public Facade() {
receptionist = Subsystem_Receptionist.getInstance();
chef = Subsystem_Chef.getInstance();
waiter = Subsystem_Waiter.getInstance();
}
/**
* 接待
*/
public void reception() {
receptionist.work();
}
/**
* 做饭
*/
public void cook_before() {
chef.before();
}
/**
* 做饭
*/
public void cook() {
chef.work();
}
/**
* 做饭
*/
public void cook_after() {
chef.after();
}
/**
* 送菜
*/
public void send() {
waiter.work();
}
/**
* 核心流程 - 模板方法
*/
public void serviceTmp() {
reception();
cook();
send();
}
/**
* 整个流程 - 模板方法
*/
public void serviceAll() {
reception();
cook_before();
cook();
cook_after();
send();
}
}
子系统角色 - 接待员
/**
* @author MrLiu
* @date 2020/7/1
* desc 子系统角色 - 接待员
*/
public class Subsystem_Receptionist {
static Subsystem_Receptionist getInstance() {
return new Subsystem_Receptionist();
}
public void work() {
Log.e("tag", "子系统 - 接待员:客户到来,接待点菜");
}
}
子系统角色 - 厨师
/**
* @author MrLiu
* @date 2020/7/1
* desc 子系统角色 - 厨师
*/
public class Subsystem_Chef {
static Subsystem_Chef getInstance() {
return new Subsystem_Chef();
}
public void before() {
Log.e("tag", "子系统 - 厨师:洗菜");
}
public void work() {
Log.e("tag", "子系统 - 厨师:十八般厨艺开炒");
}
public void after() {
Log.e("tag", "子系统 - 厨师:出锅");
}
}
子系统角色 - 服务员
/**
* @author MrLiu
* @date 2020/7/1
* desc 子系统角色 - 服务员
*/
public class Subsystem_Waiter {
static Subsystem_Waiter getInstance() {
return new Subsystem_Waiter();
}
public void work() {
Log.e("tag", "子系统 - 服务员:端菜到餐桌");
}
}
客户角色(使用)
import android.util.Log;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Facade facade = new Facade();
Log.e("tag", "--部分流程--");
facade.serviceTmp();
Log.e("tag", "--整个流程--");
facade.serviceAll();
}
}