首页 > 其他分享 >门面模式

门面模式

时间:2022-11-07 11:55:05浏览次数:102  
标签:void 接口 模式 门面 com public

复习引入

一、几种设计模式

(1)创建型:工厂模式(简单工厂、抽象工厂)、单例模式、原型模式、建造者模式。

​ 随着软件内部分工原来月明确,对象的创建和对象的使用分开也就成为了必然趋势。因为对象的创建会消耗掉系统的很多资源,所以单独对对象的创建进行研究,从而能够高效地创建对象就是创建型模式要探讨的问题。

(2)结构型:装饰器模式、适配器模式、组合模式、代理模式、享元模式、桥接模式、外观模式。

​ 在解决了对象的创建问题之后,对象的组成以及对象之间的依赖关系就成了开发人员关注的焦点,因为如何设计对象的结构、继承和依赖关系会影响到后续程序的维护性、代码的健壮性、耦合性等。

(3)行为型:观察者模式、模板模式、策略模式、迭代器模式、备忘录模式、命令模式、解释器模式、中介模式、职责链模式。

​ 在对象的结构和对象的创建问题都解决了之后,就剩下对象的行为问题了,如果对象的行为设计的好,那么对象的行为就会更清晰,它们之间的协作效率就会提高。

二、适配器模式( Adapter Design Pattern)

(1)适配器模式解决什么问题?

​ 适配器是做接口转换,解决的是原接口和目标接口不匹配的问题。

(2)适配器模式回忆

​ 顾名思义,这个模式就是用来做适配的,它将不兼容的接口转换为可兼容的接口,让原本由于接口不兼容而不能一起工作的类可以一起工作。

​ EG: USB 转接头充当适配器,把两种不兼容的接口,通过转接变得可以一起工作。

​ 实现方式:类适配器和对象适配器。其中,类适配器使用继承关系来实现,对象适配器使用组合关系来实现。

(3)适配器模式的应用场景

​ 封装有缺陷的接口设计、统一多个类的接口设计、替换依赖的外部系统、兼容老版本接口、适配不同格式的数据

三、接口隔离原则、迪米特法则(最小知识原则)、开闭原则、依赖倒转原则

外观模式/门面模式(Facade Design Pattern)

一、为什么要提出门面模式

​ 来解决接口的可复用性(通用性)和易用性之间的矛盾。

​ 为了保证接口的可复用性(或者叫通用性),我们需要将接口尽量设计得细粒度一点,职责 单一一点。但是,如果接口的粒度过小,在接口的使用者开发一个业务功能时,就会导致需 要调用 n 多细粒度的接口才能完成。调用者肯定会抱怨接口不好用。相反,如果接口粒度设计得太大,一个接口返回 n 多数据,要做 n 多事情,就会导致接口不够通用、可复用性不好。接口不可复用,那针对不同的调用者的业务需求,我们就需要开发不同的接口来满足,这就会导致系统的接口无限膨胀。

​ 粒度:粒度就是同一维度下,数据统计的粗细程度,计算机领域中粒度指系统内存扩展增量的最小值。粒度问题是设计数据仓库的一个最重要方面。粒度是指数据仓库的数据单位中保存数据的细化或综合程度的级别。细化程度越高,粒度级就越小;相反,细化程度越低,粒度级就越大。数据的粒度一直是一个设计问题。

​ “粗粒度和细粒度的区别主要是出于重用的目的,像类的设计:为尽可能重用,所以采用细粒度的设计模式,将一个复杂的类(粗粒度)拆分成高度重用的职责清晰的类(细粒度)。对于数据库的设计:原责:尽量减少表的数量与表与表之间的连接,能够设计成一个表的情况就不需要细分,所以可考虑使用粗粒度的设计方式。”

//一下那个TaskService是粗粒度的,哪个是细粒度的?
/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test1;
interface TaskService{
  public List getTaskById(int id);
  public List getTaskByName(String name);
  public List getTaskByAge(int age);
}

/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test1;
interface TaskService{
  public List getTask(Person person);
}

/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test1;
@Data
public class Person {
    private String name;
    private String gender;
    private int age;
}

二、门面模式的定义

​ GoF中的《设计模式》中描述:Provide a unified interface to a set of interfaces in a subsystem. Facade Pattern defines a higher-level interface that makes the subsystem easier to use. 翻译成中文就是:门面模式为子系统提供一组统一的接口,定义一组高层接口让子系统更易用。(下图为门面模式的UML类图)

image-20221106110659894

​ 解释:假设有一个系统 A,提供了 a、b、c、d 四个接口。系统 B 完成某个业务功能,需要调用 A 系统的 a、b、d 接口。利用门面模式,我们提供一个包裹 a、b、d 接口调用的门面接口 x,给系统 B 直接使用。

​ 代码示例:其中Facade是外观角色,也叫门面角色,客户端可以调用这个角色的方法,此角色知晓子系统的所有功能和责任,将客户端的请求代理给适当的子系统对象;Subsystem是子系统角色,可以同时拥有一个或多个子系统,每一个子系统都不是一个单独的类,而是一个类的集合,子系统并不知道门面的存在。

/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test1;

public class Facade {
    //被委托的对象
    SubSystemA a;
    SubSystemB b;
    SubSystemC c;
    SubSystemD d;

    public Facade() {
        a = new SubSystemA();
        b = new SubSystemB();
        c = new SubSystemC();
        d = new SubSystemD();
    }

    //提供给外部访问的方法
    public void methodA() {
        this.a.dosomethingA();
    }

    public void methodB() {
        this.b.dosomethingB();
    }

    public void methodC() {
        this.c.dosomethingC();
    }

    public void methodD() {
        this.d.dosomethingD();
    }
}

/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test1;

public class SubSystemA {
    public void dosomethingA() {
        System.out.println("子系统方法A");
    }

    public void dosomethingAA() {
        System.out.println("子系统方法AA");
    }
}
/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test1;

public class SubSystemB {
    public void dosomethingB() {
        System.out.println("子系统方法B");
    }

    public void dosomethingBB() {
        System.out.println("子系统方法BB");
    }
}

/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test1;

public class SubSystemC {
    public void dosomethingC() {
        System.out.println("子系统方法C");
    }

    public void dosomethingCC() {
        System.out.println("子系统方法CC");
    }
}

/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test1;

public class SubSystemD {
    public void dosomethingD() {
        System.out.println("子系统方法D");
    }

    public void dosomethingDD() {
        System.out.println("子系统方法DD");
    }
}


/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test1;

public class Client {
    public static void main(String[] args) {
        Facade facade = new Facade();

        facade.methodA();
        facade.methodB();
    }
}

运行结果:

image-20221106140454145

三、门面模式的实现

//基金类,基金经理人通过该类作为中间交互者,可以接受投资者的资金,进行购买和赎回操作。
/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test2;

public class Fund {
    Stock1 stock1;
    Stock2 stock2;
    Stock3 stock3;

    public Fund() {
        stock1 = new Stock1();
        stock2 = new Stock2();
        stock3 = new Stock3();
    }

    //购买基金
    public void buyFund() {
        stock1.buy();
        stock2.buy();
        stock3.buy();
    }

    //赎回基金
    public void sellFund() {
        stock1.sell();
        stock2.sell();
        stock3.sell();
    }
}

//股票作为示例。内部由买入和卖出两种操作。
/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test2;

public class Stock1 {

    //买股票
    public void buy() {
        System.out.println("股票1买入");
    }

    //卖股票
    public void sell() {
        System.out.println("股票1卖出");
    }

}
/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test2;

public class Stock2 {

    //买股票
    public void buy() {
        System.out.println("股票1买入");
    }

    //卖股票
    public void sell() {
        System.out.println("股票1卖出");
    }

}
/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test2;

public class Stock3 {

    //买股票
    public void buy() {
        System.out.println("股票1买入");
    }

    //卖股票
    public void sell() {
        System.out.println("股票1卖出");
    }

}

//用户通过该类对基金进行购买和赎回操作。
/**
 * date: 2022/11/6
 *
 * @author Arc
 */
package com.qlu.test2;

public class Client {

    public static void main(String[] args) {
        Fund fund = new Fund();

        //基金购买
        fund.buyFund();
        System.out.println("-------------");
        //基金赎回
        fund.sellFund();
    }

}

​ 我们只需在客户端购买和赎回即可,内部的任何操作都不需要我们关注,对于面向对象有一定基础的朋友,即使没有听说过外观模式,也完全有可能在很多时候使用它,因为它完美地体现了依赖倒转原则和迪米特法则的思想,所以是非常常用的模式之一。

运行结果:

image-20221106142145488

四、应用场景 ——门面模式让子系统更加易用

1、解决易用性问题

门面模式可以用来封装系统的底层实现,隐藏系统的复杂性,提供一组更加简单易用、更高层的接口。比如,Linux 系统调用函数就可以看作一种“门面”。它是 Linux 操作系统暴露给开发者的一组“特殊”的编程接口,它封装了底层更基础的 Linux 内核调用。再比如, Linux 的 Shell 命令,实际上也可以看作一种门面模式的应用。它继续封装系统调用,提供 更加友好、简单的命令,让我们可以直接通过执行命令来跟操作系统交互。

接口隔离原则的英文翻译是“ Interface Segregation Principle”,缩写为 ISP。Robert Martin 在 SOLID 原则中是这样定义它的:“Clients should not be forced to depend upon interfaces that they do not use。”直译成中文的话就是:客户端不应该强迫依赖它不需要的接口。

2、解决性能问题

​ 通过将多个接口调用替换为一个门面接口调用,减少网络通信成本,提高 App 客户端的响应速度。

​ EG:系统 A 是一个后端服务器,系统 B 是 App 客户端。App 客户端通过后端服务器提供的接口来获取数据。我们知道,App 和服务器之间是通过移动网络通信 的,网络通信耗时比较多,为了提高 App 的响应速度,我们要尽量减少 App 与服务器之 间的网络通信次数。 假设,完成某个业务功能(比如显示某个页面信息)需要“依次”调用 a、b、d 三个接 口,因自身业务的特点,不支持并发调用这三个接口。 如果我们现在发现 App 客户端的响应速度比较慢,排查之后发现,是因为过多的接口调用 过多的网络通信。针对这种情况,我们就可以利用门面模式,让后端服务器提供一个包裹 a、b、d 三个接口调用的接口 x。App 客户端调用一次接口 x,来获取到所有想要的数 据,将网络通信的次数从 3 次减少到 1 次,也就提高了 App 的响应速度。

​ 从代码实现的角度来看,该如何组织门面接口和非门面接口? (可以过,因为我没有开发经验)

​ A:如果门面接口不多,我们完全可以将它跟非门面接口放到一块,也不需要特殊标记,当作普通接口来用即可。如果门面接口很多,我们可以在已有的接口之上,再重新抽象出一层,专门放置门面接口,从类、包的命名上跟原来的接口层做区分。如果门面接口特别多,并且很多都是跨多个子系统的,我们可以将门面接口放到一个新的子系统中。

3、 解决分布式事务问题

​ 关于利用门面模式来解决分布式事务问题,我们通过一个例子来解释一下。

在一个金融系统中,有两个业务领域模型,用户和钱包。这两个业务领域模型都对外暴露了 一系列接口,比如用户的增删改查接口、钱包的增删改查接口。假设有这样一个业务场景: 在用户注册的时候,我们不仅会创建用户(在数据库 User 表中),还会给用户创建一个钱包(在数据库的 Wallet 表中)。 对于这样一个简单的业务需求,我们可以通过依次调用用户的创建接口和钱包的创建接口来 完成。但是,用户注册需要支持事务,也就是说,创建用户和钱包的两个操作,要么都成 功,要么都失败,不能一个成功、一个失败。 要支持两个接口调用在一个事务中执行,是比较难实现的,这涉及分布式事务问题。虽然我 们可以通过引入分布式事务框架或者事后补偿的机制来解决,但代码实现都比较复杂。而最 简单的解决方案是,利用数据库事务或者 Spring 框架提供的事务(如果是 Java 语言的 话),在一个事务中,执行创建用户和创建钱包这两个 SQL 操作。这就要求两个 SQL 操作 要在一个接口中完成,所以,我们可以借鉴门面模式的思想,再设计一个包裹这两个操作的 新接口,让新接口在一个事务中执行两个 SQL 操作。

课上思考

一、适配器模式和门面模式的共同点是,将不好用的接口适配成好用的接口。你可以试着总 结一下它们的共同点和区别吗?

​ A1:适配器是做接口转换,解决的是原接口和目标接口不匹配的问题。 门面模式做接口整合,解决的是多接口调用带来的问题。适配器模式与门面模式的共同点都需要二次封装,隐藏内部细节。不同点为适配器是为 了统一格式,门面是为了简单易用。

二、门面模式的优缺点

​ 优点:减少了系统的相互依赖;提高灵活性,不管系统内部如何变化,只要不影响外观对象,可以自由活动;提高了安全性,想让你访问那些业务就开通哪些逻辑,不在外观上开通的方法就访问不到。

​ 缺点:不符合开闭原则,修改麻烦。

三、外观模式体现了哪些设计原则

​ 迪米特法则、接口隔离原则、依赖倒转原则

参考: https://www.cnblogs.com/ajunForNet/p/3492101.html

https://www.cnblogs.com/adamjwh/p/9048594.html

https://www.cnblogs.com/HoneyTYX/p/9396446.html

https://baike.baidu.com/item/粒度/13014724?fr=aladdin

标签:void,接口,模式,门面,com,public
From: https://www.cnblogs.com/purearc/p/16865459.html

相关文章

  • 23种设计模式之自定义Spring框架(五)
    7,自定义Spring框架7.1spring使用回顾自定义spring框架前,先回顾一下spring框架的使用,从而分析spring的核心,并对核心功能进行模拟。数据访问层。定义UserDao接口及其子......
  • 23种设计模式之设计模式介绍(一)
    1,设计模式概述1.1软件设计模式的产生背景"设计模式"最初并不是出现在软件设计中,而是被用于建筑领域的设计中。1977年美国著名建筑大师、加利福尼亚大学伯克利分校环境......
  • 设计模式
    设计模式(一)——简单工厂设计模式(二)——工厂方法设计模式(三)——抽象工厂设计模式(四)——建造者模式设计模式(五)——原型模式设计模式(六)——单例模式......
  • 浅谈PHP设计模式的桥接模式
    简介:桥接模式又叫桥梁模式,属于结构型模式。目的是将抽象与实现分离,使它们都可以独立的变化,解耦。继承有很多好处,但是会增加耦合,而桥接模式偏向组合和聚合的方式来共享。......
  • 23种设计模式-抽象工厂模式介绍加实战代码
    1、描述通俗一点来讲,抽象工厂模式就是在工厂方法模式的抽象工厂类中规范多个同类产品。工厂方法模式是针对一个产品系列的,而抽象工厂模式是针对多个产品系列的,即工厂方法......
  • 设计模式---代理模式
    简述对客户端隐藏目标类,创建代理类拓展目标类,并且对于客户端隐藏功能拓展的细节,使得客户端可以像使用目标类一样使用代理类,面向代理(客户端只与代理类交互)。话不多说,看......
  • 最简单Openwrt ipv6配置,局域网WAN6中继模式获取原生ipv6地址
    条件condition    wan 和wan6 是默认配置  Wanandwan6arethedefaultconfigurations    同时wan6可以获取到原生IPv6  Atthesametimewan6can......
  • 三、创建型模式——单例模式
    一、意图二、问题三、解决方案四、模式结构五、伪代码六、适用场景七、实现方式八、优缺点九、与其他模式的关系......
  • 三、创建型模式——原型模式
    一、意图二、问题三、解决方案四、模式结构五、伪代码六、适用场景七、实现方式八、优缺点九、与其他模式的关系......
  • 三、创建型模式——抽象工厂
    一、意图二、问题三、解决方案四、模式结构五、伪代码六、适用场景七、实现方式八、优缺点九、与其他模式的关系......