门面模式 与 装饰器模式

一、门面模式定义

  • 定义:又叫门面模式,提供了一个统一的接口,用来访问子系统中的一群接口
  • 外观模式定义了一个高层接口,让子系统更容易使用
  • 类型:结构型
  • 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 操作。

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

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值