概述
一、门面模式定义
- 定义:又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口
- 外观模式定义了一个高层接口,让子系统更容易使用
- 类型:结构型
- UML类图
门面模式是对系统复杂的关系处理做了一个封装,对外提供一个简单的接口,成员介绍: - 子系统:被门面模式封装的子系统,也是具体业务逻辑的细节
- facade类:门面类,对子系统执行流程进行封装,对外开放功能接口,一般为单例对象。
二、门面模式实例
门面模式主要包含2种角色:
**外观角色(Facade):**也称门面角色,系统对外的统一接口;
**子系统角色(SubSystem):**可以同时有一个或多个 SubSystem。每个 SubSytem 都不是一个单独
的类,而是一个类的集合。 SubSystem 并不知道 Facade 的存在,对于 SubSystem 而言, Facade 只
是另一个客户端而已(即 Facade 对 SubSystem 透明)。
下面是门面模式的通用代码,首先分别创建3个子系统的业务逻辑SubSystemA、SubSystemB、
SubSystemC,代码很简单:
// 子系统
public class SubSystemA {
public void doA() {
System.out.println("doing A stuff");
}
}
// 子系统
public class SubSystemB {
public void doB() {
System.out.println("doing B stuff");
}
}
// 子系统
public class SubSystemC {
public void doC() {
System.out.println("doing C stuff");
}
}
来看客户端代码:
// 外观角色 Facade
public class Facade {
private SubSystemA a = new SubSystemA();
private SubSystemB b = new SubSystemB();
private SubSystemC c = new SubSystemC();
// 对外接口
public void doA() {
this.a.doA();
}
// 对外接口
public void doB() {
this.b.doB();
}
// 对外接口
public void doC() {
this.c.doC();
}
}
三、门面模式的优缺点
优点:
1、简化了调用过程,无需深入了解子系统,以防给子系统带来风险
2、减少系统依赖、松散耦合
3、更好地划分访问层次,提高了安全性
4、遵循迪米特法则,即最少知道原则。
缺点:
1、当增加子系统和扩展子系统行为时,可能容易带来位置风险
2、不符合开闭原则
3、某些情况下可能违背的单一职责原则
四、门面模式在实际中的应用
例如:前端ReseFul请求——>Controll ->Dao->Service
五、装饰器模式介绍
装饰器模式在我们生活中应用也比较多如给煎饼加鸡蛋;给蛋糕加上一些水果;给房子装修等,为对象扩展一些额外的职责。装饰器在代码程序中适用于以下场景:
1、用于扩展一个类的功能或给一个类添加附加职责。
2、动态的给一个对象添加功能,这些功能可以再动态的撤销。
3、需要为一批的兄弟类进行改装或加装功能。
六、装饰器模式代码示例
来看一个这样的场景,上班族白领其实大多有睡懒觉的习惯,每天早上上班都是踩点,于是很多小伙伴为了多赖一会儿床都不吃早餐。那么,也有些小伙伴可能在上班路上碰到卖煎饼的路边摊,都会顺带一个到公司茶水间吃早餐。卖煎饼的大姐可以给你的煎饼加鸡蛋,也可以加香肠。
首先创建一个煎饼Battercake类:
public class Battercake {
protected String getMsg(){ return "煎饼";}
public int getPrice(){ return 5;}
}
创建一个加鸡蛋的煎饼BattercakeWithEgg类:
public class BattercakeWithEgg extends Battercake {
@Override
protected String getMsg(){ return super.getMsg() + "+1个鸡蛋";}
@Override
//加一个鸡蛋加 1 块钱
public int getPrice(){ return super.getPrice() + 1;}
}
再创建一个既加鸡蛋又加香肠的BattercakeWithEggAndSausage类:
public class BattercakeWithEggAndSauage extends BattercakeWithEgg {
@Override
protected String getMsg(){ return super.getMsg() + "+1根香肠";}
@Override
//加一个香肠加 2 块钱
public int getPrice(){ return super.getPrice() + 2;}
}
编写客户端测试代码:
public class Test {
public static void main(String[] args) {
Battercake battercake = new Battercake();
System.out.println(battercake.getMsg() + ",总价:" + battercake.getPrice());
BattercakeWithEgg battercakeWithEgg = new BattercakeWithEgg();
System.out.println(battercakeWithEgg.getMsg() + ",总价:" + battercakeWithEgg.getPrice());
BattercakeWithEggAndSauage battercakeWithEggAndSauage = new BattercakeWithEggAndSauage();
System.out.println(battercakeWithEggAndSauage.getMsg() + ",总价:" + battercakeWithEggAndSauage.getPrice());
}
运行结果:
煎饼,总价:5
煎饼+1个鸡蛋,总价:6
煎饼+1个鸡蛋+1根香肠,总价:8
运行结果没有问题。
但是,如果用户需要一个加2个鸡蛋加1根香肠的煎饼,那么用我们现在的类
结构是创建不出来的,也无法自动计算出价格,除非再创建一个类做定制。如果需求再变,一直加定制
显然是不科学的。那么下面我们就用装饰器模式来解决上面的问题。
首先创建一个建煎饼的抽象
Battercake类:
public abstract class Battercake {
protected abstract String getMsg();
protected abstract int getPrice();
}
创建一个基本的煎饼(或者叫基础套餐)BaseBattercake:
public class BaseBattercake extends Battercake{
protected String getMsg(){ return "煎饼";}
public int getPrice(){ return 5;}
}
然后,再创建一个扩展套餐的抽象装饰器BattercakeDecotator类:
public class BattercakeDecorator extends Battercake{
//静态代理,委派
private Battercake battercake;
public BattercakeDecorator(Battercake battercake) {
this.battercake = battercake;
}
@Override
protected String getMsg(){ return this.battercake.getMsg();}
@Override
public int getPrice(){ return this.battercake.getPrice();}
}
然后,创建鸡蛋装饰器EggDecorator类:
public class EggDecorator extends BattercakeDecorator{
public EggDecorator(Battercake battercake) {
super(battercake);
}
@Override
protected String getMsg(){ return super.getMsg() + "1个鸡蛋";}
@Override
public int getPrice(){ return super.getPrice() + 1;}
}
创建香肠装饰器SausageDecorator类:
public class SauageDecorator extends BattercakeDecorator{
public SauageDecorator(Battercake battercake) {
super(battercake);
}
protected String getMsg(){ return super.getMsg() + "1根香肠";}
public int getPrice(){ return super.getPrice() + 2;}
}
编写客户端测试代码:
public class Test {
public static void main(String[] args) {
//路边摊买一个煎饼
Battercake battercake = new BaseBattercake();
//煎饼有点小,想再加一个鸡蛋
battercake = new EggDecorator(battercake);
//再加一个鸡蛋
battercake = new EggDecorator(battercake);
//很饿,再加根香肠
battercake = new SauageDecorator(battercake);
//跟静态代理最大区别就是职责不同
//静态代理不一定要满足 is-a 的关系
//静态代理会做功能增强,同一个职责变得不一样
//装饰器更多考虑是扩展
System.out.println(battercake.getMsg() + ",总价" + battercake.getPrice());
}
}
运行结果:
煎饼,总价:5
煎饼+1个鸡蛋,总价:6
煎饼+1个鸡蛋+1根香肠,总价:8
煎饼+1个鸡蛋+1根香肠+1根香肠,总价:9
七、装饰器模式应用与实例
1.解决易用性问题
门面模式可以用来封装系统的底层实现,隐藏系统的复杂性,提供一组更加简单易用、更高层的接口。
比如,Linux 系统调用函数就可以看作一种“门面”。它是 Linux 操作系统暴露给开发者的一组“特殊”的编程接口,它封装了底层更基础的 Linux 内核调用。
再比如,Linux 的 Shell 命令,实际上也可以看作一种门面模式的应用。它继续封装系统调用,提供更加友好、简单的命令,让我们可以直接通过执行命令来跟操作系统交互。
2.解决性能问题
我们通过将多个接口调用替换为一个门面接口调用,减少网络通信成本,提高 App 客户端的响应速度。
3.解决分布式事务问题
在用户注册的时候,我们不仅会创建用户(在数据库 User 表中),还会给用户创建一个钱包(在数据库的 Wallet 表中)。用户注册需要支持事务,也就是说,创建用户和钱包的两个操作,要么都成功,要么都失败,不能一个成功、一个失败。
最简单的解决方案是,利用数据库事务或者 Spring 框架提供的事务(如果是 Java 语言的话),在一个事务中,执行创建用户和创建钱包这两个 SQL 操作。这就要求两个 SQL 操作要在一个接口中完成,所以,我们可以借鉴门面模式的思想,再设计一个包裹这两个操作的新接口,让新接口在一个事务中执行两个 SQL 操作。
门面模式与装饰器模式详解
最后
以上就是孝顺大船为你收集整理的门面模式 与 装饰器模式的全部内容,希望文章能够帮你解决门面模式 与 装饰器模式所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复