我是靠谱客的博主 孝顺大船,这篇文章主要介绍门面模式 与 装饰器模式,现在分享给大家,希望可以做个参考。

一、门面模式定义

  • 定义:又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口
  • 外观模式定义了一个高层接口,让子系统更容易使用
  • 类型:结构型
  • UML类图
    在这里插入图片描述
    门面模式是对系统复杂的关系处理做了一个封装,对外提供一个简单的接口,成员介绍:
  • 子系统:被门面模式封装的子系统,也是具体业务逻辑的细节
  • facade类:门面类,对子系统执行流程进行封装,对外开放功能接口,一般为单例对象。

二、门面模式实例

门面模式主要包含2种角色:

**外观角色(Facade):**也称门面角色,系统对外的统一接口;

**子系统角色(SubSystem):**可以同时有一个或多个 SubSystem。每个 SubSytem 都不是一个单独
的类,而是一个类的集合。 SubSystem 并不知道 Facade 的存在,对于 SubSystem 而言, Facade 只
是另一个客户端而已(即 Facade 对 SubSystem 透明)。

下面是门面模式的通用代码,首先分别创建3个子系统的业务逻辑SubSystemA、SubSystemB、
SubSystemC,代码很简单:

复制代码
1
2
3
4
5
6
7
8
// 子系统 public class SubSystemA { public void doA() { System.out.println("doing A stuff"); } }
复制代码
1
2
3
4
5
6
7
8
// 子系统 public class SubSystemB { public void doB() { System.out.println("doing B stuff"); } }
复制代码
1
2
3
4
5
6
7
// 子系统 public class SubSystemC { public void doC() { System.out.println("doing C stuff"); } }

来看客户端代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 外观角色 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类:

复制代码
1
2
3
4
5
6
7
8
public class Battercake { protected String getMsg(){ return "煎饼";} public int getPrice(){ return 5;} }

创建一个加鸡蛋的煎饼BattercakeWithEgg类:

复制代码
1
2
3
4
5
6
7
8
9
public class BattercakeWithEgg extends Battercake { @Override protected String getMsg(){ return super.getMsg() + "+1个鸡蛋";} @Override //加一个鸡蛋加 1 块钱 public int getPrice(){ return super.getPrice() + 1;} }

再创建一个既加鸡蛋又加香肠的BattercakeWithEggAndSausage类:

复制代码
1
2
3
4
5
6
7
8
9
10
public class BattercakeWithEggAndSauage extends BattercakeWithEgg { @Override protected String getMsg(){ return super.getMsg() + "+1根香肠";} @Override //加一个香肠加 2 块钱 public int getPrice(){ return super.getPrice() + 2;} }

编写客户端测试代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
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()); }

运行结果:

复制代码
1
2
3
4
5
6
煎饼,总价:5 煎饼+1个鸡蛋,总价:6 煎饼+1个鸡蛋+1根香肠,总价:8

运行结果没有问题。

但是,如果用户需要一个加2个鸡蛋加1根香肠的煎饼,那么用我们现在的类
结构是创建不出来的,也无法自动计算出价格,除非再创建一个类做定制。如果需求再变,一直加定制
显然是不科学的。那么下面我们就用装饰器模式来解决上面的问题。

首先创建一个建煎饼的抽象
Battercake类:

复制代码
1
2
3
4
5
6
7
public abstract class Battercake { protected abstract String getMsg(); protected abstract int getPrice(); }

创建一个基本的煎饼(或者叫基础套餐)BaseBattercake:

复制代码
1
2
3
4
5
6
7
8
public class BaseBattercake extends Battercake{ protected String getMsg(){ return "煎饼";} public int getPrice(){ return 5;} }

然后,再创建一个扩展套餐的抽象装饰器BattercakeDecotator类:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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类:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
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类:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
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;} }

编写客户端测试代码:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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()); } }

运行结果:

复制代码
1
2
3
4
5
煎饼,总价: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 操作。

门面模式与装饰器模式详解

最后

以上就是孝顺大船最近收集整理的关于门面模式 与 装饰器模式的全部内容,更多相关门面模式内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(62)

评论列表共有 0 条评论

立即
投稿
返回
顶部