外观模式Facade 门面模式 Adapter及Proxy 设计模式之间的关系 云服务商多个sdk的操作 face

 

小结:

1、

外观模式/门面模式 Facade  往是多个类或其它程序单元,通过重新组合各类及程序单元,对外提供统一的接口/界面。

Proxy(代理)注重在为Client-Subject提供一个访问的中间层,如CORBA可为应用程序提供透明访问支持,使应用程序无需去考虑平台及网络造成的差异及其它诸多技术细节

Adapter(适配器)注重对接口的转换与调整

 

52 | 门面模式:如何设计合理的接口粒度以兼顾接口的易用性和通用性? https://time.geekbang.org/column/article/206409

门面模式,也叫外观模式,英文全称是 Facade Design Pattern。在 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.

 

假设有一个系统 A,提供了 a、b、c、d 四个接口。系统 B 完成某个业务功能,需要调用 A 系统的 a、b、d 接口。

利用门面模式,我们提供一个包裹 a、b、d 接口调用的门面接口 x,给系统 B 直接使用。不知道你会不会有这样的疑问,让系统 B 直接调用 a、b、d 感觉没有太大问题呀,为什么还要提供一个包裹 a、b、d 的接口 x 呢?

关于这个问题,我通过一个具体的例子来解释一下。

假设我们刚刚提到的系统 A 是一个后端服务器,系统 B 是 App 客户端。App 客户端通过后端服务器提供的接口来获取数据。

我们知道,App 和服务器之间是通过移动网络通信的,网络通信耗时比较多,为了提高 App 的响应速度,我们要尽量减少 App 与服务器之间的网络通信次数。

假设,完成某个业务功能(比如显示某个页面信息)需要“依次”调用 a、b、d 三个接口,因自身业务的特点,不支持并发调用这三个接口。

如果我们现在发现 App 客户端的响应速度比较慢,排查之后发现,是因为过多的接口调用过多的网络通信。

针对这种情况,我们就可以利用门面模式,让后端服务器提供一个包裹 a、b、d 三个接口调用的接口 x。

App 客户端调用一次接口 x,来获取到所有想要的数据,将网络通信的次数从 3 次减少到 1 次,也就提高了 App 的响应速度。

这里举的例子只是应用门面模式的其中一个意图,也就是解决性能问题。实际上,不同的应用场景下,使用门面模式的意图也不同。

门面模式定义中的“子系统(subsystem)”也可以有多种理解方式。它既可以是一个完整的系统,也可以是更细粒度的类或者模块。

1. 解决易用性问题

门面模式可以用来封装系统的底层实现,隐藏系统的复杂性,提供一组更加简单易用、更高层的接口。比如,Linux 系统调用函数就可以看作一种“门面”。它是 Linux 操作系统暴露给开发者的一组“特殊”的编程接口,它封装了底层更基础的 Linux 内核调用。再比如,Linux 的 Shell 命令,实际上也可以看作一种门面模式的应用。它继续封装系统调用,提供更加友好、简单的命令,让我们可以直接通过执行命令来跟操作系统交互。我们前面也多次讲过,设计原则、思想、模式很多都是相通的,是同一个道理不同角度的表述。实际上,从隐藏实现复杂性,提供更易用接口这个意图来看,门面模式有点类似之前讲到的迪米特法则(最少知识原则)和接口隔离原则:两个有交互的系统,只暴露有限的必要的接口。除此之外,门面模式还有点类似之前提到封装、抽象的设计思想,提供更抽象的接口,封装底层实现细节。

 

2. 解决性能问题

我们通过将多个接口调用替换为一个门面接口调用,减少网络通信成本,提高 App 客户端的响应速度。所以,关于这点,我就不再举例说明了。我们来讨论一下这样一个问题:从代码实现的角度来看,该如何组织门面接口和非门面接口?如果门面接口不多,我们完全可以将它跟非门面接口放到一块,也不需要特殊标记,当作普通接口来用即可。如果门面接口很多,我们可以在已有的接口之上,再重新抽象出一层,专门放置门面接口,从类、包的命名上跟原来的接口层做区分。如果门面接口特别多,并且很多都是跨多个子系统的,我们可以将门面接口放到一个新的子系统中。

 

3. 解决分布式事务问题

在一个金融系统中,有两个业务领域模型,用户和钱包。这两个业务领域模型都对外暴露了一系列接口,比如用户的增删改查接口、钱包的增删改查接口。假设有这样一个业务场景:在用户注册的时候,我们不仅会创建用户(在数据库 User 表中),还会给用户创建一个钱包(在数据库的 Wallet 表中)。

对于这样一个简单的业务需求,我们可以通过依次调用用户的创建接口和钱包的创建接口来完成。但是,用户注册需要支持事务,也就是说,创建用户和钱包的两个操作,要么都成功,要么都失败,不能一个成功、一个失败。要支持两个接口调用在一个事务中执行,是比较难实现的,这涉及分布式事务问题。

虽然我们可以通过引入分布式事务框架或者事后补偿的机制来解决,但代码实现都比较复杂。而最简单的解决方案是,利用数据库事务或者 Spring 框架提供的事务(如果是 Java 语言的话),在一个事务中,执行创建用户和创建钱包这两个 SQL 操作。这就要求两个 SQL 操作要在一个接口中完成,所以,我们可以借鉴门面模式的思想,再设计一个包裹这两个操作的新接口,让新接口在一个事务中执行两个 SQL 操作。

 

 

 

 

 

 

 

Web中的积累:外观模式 Facade - www.bysocket.com - 博客园 https://www.cnblogs.com/Alandre/p/4006831.html

1  web三层构架,其实在数据访问层和业务逻辑层,业务逻辑层和表示层的层与层之间建立外观Facade模式应用,为复杂的子系统提供一个简单的接口或者抽象类,使得耦合大大降低。

2  在开发阶段,子系统往往因为不断的重构演化而变得越来越复杂,大多数的模式使用时也都会产生很多很小的类,这本是好事,但也给外部调用它们的用户程序带来了使用上的困难,增加外观Facade可以提供一个简单的接口,减少它们之间的依赖。

    具体做法:将小类规划,分类。然后实现代理Facade。

3  在维护一个遗留的大型系统时,可能这个系统已经非常难以维护和扩展了,但因为它包含非常重要的功能,新的需求开发必须依赖于它。此时用外观模式Facade也是非常合适的。例如可以开发一个外观Facade类,来提供设计粗糙或高度复杂的遗留代码的比较清晰简单的接口,让新系统与Facade对象交互,Facade与遗留代码交互所有复杂的工作。

 

 

JAVA设计模式十九--Facade(外观模式) - Y-CAT的专栏 - CSDN博客 https://blog.csdn.net/hfmbook/article/details/7702642

它对客户屏蔽子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便。

 

 

23种设计模式(1)-Facade设计模式 - 至尊宝 - CSDN博客 https://blog.csdn.net/duchao123duchao/article/details/51425085

曾经我遇见的一个需求是这样的,接口A有个方法void methodA(),类B需要实现接口A的methodA()方法,并且在类B中需要把methodA()方法内部处理逻辑获得的结果利用C类实例的某个方法进行持久化操作。由于技术功力尚浅,开始我左思右想就是不能实现这个需求。开始纠结于两个难题:1,methodA()方法返回值为void,我无法获得methodA()内部逻辑获得的数据,无法获得这些数据,也就无法利用持久化类C进行处理;2,methodA()方法入参又为空,我的持久化类C也无法注入。当时我就懵逼了。还好,突然脑海想起了曾学spring时遇见的模板类设计模式,于是浅显学了下的模板类设计模式轻松把这个难题搞定。解决方法为,B定义为抽象类,内部再另外定义一个抽象方法absMethod(C c),其入参为持久类C类型,在B类的methodA()方法中调用这个抽象方法absMethod(C c),这样持久化类则注入到了methodA()方法中,则可以对其中的数据进行持久化操作了。然后只需要用D类继承这个B类,并且实现这个B的absMethod(C c)方法,这样就可以把C实例间接传入methodA()方法。

 

设计模式 - 可复用面向对象软件的基础(高清版PDF) 

1, 【GOF】的书中指出:在门面模式中,通常只需要一个门面类,并且此门面类只有一个实例,换言之它是一个单例类。当然这并不意味着在整个系统里只能有一个门面类,而仅仅是说对每一个子系统只有一个门面类。或者说,如果一个系统有好几个子系统的话,每一个子系统有一个门面类,整个系统可以有数个门面类。
2,初学者往往以为通过继承一个门面类便可在子系统中加入新的行为,这是错误的。门面模式的用意是为子系统提供一个集中化和简化的沟通管道,而不能向子系统加入新的行为。

 

 

需求:

客户需要在管理后台给华为云、腾讯云、阿里云等不同的云服务厂商操控云资源

已知云厂商提供创建api接口,但不同接口的方法不统一

 

隔离变化,提供统一的接口

(venv1) D:\pyCGlang\cd1\新建文件夹 (2)\tmp>tree /F
文件夹 PATH 列表
卷序列号为 0000-D760
D:.
│  biz.py
│  __init__.py
│
├─sdk0
│  │  a.py
│  │  __init__.py
│  │
│  └─__pycache__
│          a.cpython-37.pyc
│          __init__.cpython-37.pyc
│
├─sdk1
│  │  a.py
│  │  __init__.py
│  │
│  └─__pycache__
│          a.cpython-37.pyc
│          __init__.cpython-37.pyc
│
└─unifiedSdk
    │  ECS.py
    │  __init__.py
    │
    └─__pycache__
            ECS.cpython-37.pyc
            __init__.cpython-37.pyc

  

/tmp/sdk0/a.py

def f():
    print( 'sdk0-f-通过华为云-创建服务器实例' )

/tmp/sdk1/a.py  

def f():
    print( 'sdk1-f-通过腾讯云-创建服务器实例' )

/tmp/unifiedSdk/ECS.py

def CreateInstance():
    import random
    a  = random.randint( 1 ,  10 )
    if a >  7 :
        from sdk0  import a
        f1  = a.f
    elif a <  3 :
        from sdk1  import a
        f1  = a.f
    else :
        def f1():
            print ( '相关服务的sdk不存在' )
    return f1

/tmp/biz.py

from unifiedSdk.ECS  import CreateInstance

for i  in range ( 10 ):
    print (i)
    CreateInstance()()

  

0
sdk0 - f - 通过华为云 - 创建服务器实例
1
相关服务的sdk不存在
2
sdk0 - f - 通过华为云 - 创建服务器实例
3
相关服务的sdk不存在
4
sdk1 - f - 通过腾讯云 - 创建服务器实例
5
sdk1 - f - 通过腾讯云 - 创建服务器实例
6
相关服务的sdk不存在
7
相关服务的sdk不存在
8
相关服务的sdk不存在
9
sdk1 - f - 通过腾讯云 - 创建服务器实例

  

 

https://github.com/grpc/grpc/blob/master/src/python/grpcio/grpc/framework/interfaces/face/face.py

 

posted @ 2018-08-23 19:34  papering  阅读(339)  评论(0编辑  收藏  举报