研磨设计模式之外观模式(Facade)(模式讲解)

2019-03-28 22:08|来源: 网络

3.3  模式讲解

3.3.1  认识外观模式

(1)外观模式的目的

       外观模式的目的不是给子系统添加新的功能接口,而是为了让外部减少与子系统内多个模块的交互,松散耦合,从而让外部能够更简单的使用子系统。

       这点要特别注意,因为外观是当作子系统对外的接口出现的,虽然也可以在这里定义一些子系统没有的功能,但不建议这么做。外观应该是包装已有的功能,它主要负责组合已有功能来实现客户需要,而不是添加新的实现。


(2)使用外观跟不使用相比有何变化

       看到Facade的实现,可能有些朋友会说,这不就是把原来在客户端的代码搬到Facade里面了吗?没有什么大变化啊?

没错,说的很对,表面上看就是把客户端的代码搬到Facade里面了,但实质是发生了变化的,请思考:Facade到底位于何处呢?是位于客户端还是在由A、B、C模块组成的系统这边呢?

       答案肯定是在系统这边,这有什么不一样吗?

       当然有了,如果Facade在系统这边,那么它就相当于屏蔽了外部客户端和系统内部模块的交互,从而把A、B、C模块组合成为一个整体对外,不但方便了客户端的调用,而且封装了系统内部的细节功能,也就是说Facade与各个模块交互的过程已经是内部实现了。这样一来,如果今后调用模块的算法发生了变化,比如变化成要先调用B,然后调用A,那么只需要修改Facade的实现就可以了。

      另外一个好处,Facade的功能可以被很多个客户端调用,也就是说Facade可以实现功能的共享,也就是实现复用。同样的调用代码就只用在Facade里面写一次就好了,而不用在多个调用的地方重复写。

      还有一个潜在的好处,对使用Facade的人员来说,Facade大大节省了他们的学习成本,他们只需要了解Facade即可,无需再深入到子系统内部,去了解每个模块的细节,也不用和这多个模块交互,从而使得开发简单,学习也容易。


(3)有外观,但是可以不使用

   虽然有了外观,如果有需要,外部还是可以绕开Facade,直接调用某个具体模块的接口,这样就能实现兼顾组合功能和细节功能。比如在客户端就想要使用A模块的功能,那么就不需要使用Facade,可以直接调用A模块的接口。

示例代码如下:

public class Client {

   public static void main(String[] args) {

      AModuleApi a = new AModuleImpl();

      a.testA();

   }

}


(4)外观提供了缺省的功能实现

      现在的系统是越做越大、越来越复杂,对软件的要求也就更高。为了提高系统的可重用性,通常会把一个大的系统分成很多个子系统,再把一个子系统分成很多更小的子系统,一直分下去,分到一个一个小的模块,这样一来,子系统的重用性会得到加强,也更容易对子系统进行定制和使用。

      但是这也带来一个问题,如果用户不需要对子系统进行定制,仅仅就是想要使用它们来完成一定的功能,那么使用起来会比较麻烦,需要跟这多个模块交互。

      外观对象就可以为用户提供一个简单的、缺省的实现,这个实现对大多数的用户来说都是已经足够了的。但是外观并不限制那些需要更多定制功能的用户,直接越过外观去访问内部的模块的功能。


(5)外观模式的调用顺序示意图

外观模式的调用顺序如图3.6所示:

图3.6  外观模式调用顺序示意图


3.3.2  外观模式的实现

(1)Facade的实现

       对于一个子系统而言,外观类不需要很多,通常可以实现成为一个单例。

       也可以直接把外观中的方法实现成为静态的方法,这样就可以不需要创建外观对象的实例而直接就可以调用,这种实现相当于把外观类当成一个辅助工具类实现。简要的示例代码如下:

public class Facade {

   private Facade(){     }

   public static void test(){

      AModuleApi a = new AModuleImpl();

      a.testA();

      BModuleApi b = new BModuleImpl();

      b.testB();

      CModuleApi c = new CModuleImpl();

      c.testC();

   }

}


(2)Facade可以实现成为interface

       虽然Facade通常直接实现成为类,但是也可以把Facade实现成为真正的interface,只是这样会增加系统的复杂程度,因为这样会需要一个Facade的实现,还需要一个来获取Facade接口对象的工厂,此时结构如图3.7所示:

图3.7  外观实现成为接口的结构示意图


(3)Facade实现成为interface的附带好处

       如果把Facade实现成为接口,还附带一个功能,就是能够有选择性的暴露接口方法,尽量减少模块对子系统外提供的接口方法。

       换句话说,一个模块的接口里面定义的方法可以分成两部分,一部分是给子系统外部使用的,一部分是子系统内部的模块间相互调用时使用的。有了Facade接口,那么用于子系统内部的接口功能就不用暴露给子系统外部了。

比如,定义如下的A、B、C模块的接口:

public interface AModuleApi {

public void a1();

public void a2();

 

public void a3();

}


同理定义B、C模块的接口

public interface BModuleApi {

   //对子系统外部

   public void b1();

   //子系统内部使用

   public void b2();

   //子系统内部使用

   public void b3();

}

public interface CModuleApi {

   //对子系统外部

   public void c1();

   //子系统内部使用

   public void c2();

   //子系统内部使用

   public void c3();

}

 

定义好了各个模块的接口,接下来定义Facade的接口:

public interface FacadeApi {

   public void a1();

   public void b1();

   public void c1();

   public void test();

}

       这样定义Facade的话,外部只需要有Facade接口,就不再需要其它的接口了,这样就能有效地屏蔽内部的细节,免得客户端去调用A模块的接口时,发现了一些不需要它知道的接口,这会造成“接口污染”。

       比如a2、a3方法就不需要让客户端知道,否则既暴露了内部的细节,又让客户端迷惑。对客户端来说,他可能还要去思考a2、a3方法用来干什么呢?其实a2、a3方法是对内部模块之间交互的,原本就不是对子系统外部的,所以干脆就不要让客户端知道。


(4)Facade的方法实现

       Facade的方法实现中,一般是负责把客户端的请求转发给子系统内部的各个模块进行处理,Facade的方法本身并不进行功能的处理,Facade的方法的实现只是实现一个功能的组合调用。

       当然在Facade中实现一个逻辑处理也并无不可,但是不建议这样做,这不是Facade的本意,也超出了Facade的边界。


3.3.3  外观模式的优缺点

l 松散耦合
   外观模式松散了客户端与子系统的耦合关系,让子系统内部的模块能更容易扩展和维护。

l 简单易用
   外观模式让子系统更加易用,客户端不再需要了解子系统内部的实现,也不需要跟众多子系统内部的模块进行交互,只需要跟外观交互就可以了,相当于外观类为外部客户端使用子系统提供了一站式服务。

l 更好的划分访问层次
   通过合理使用Facade,可以帮助我们更好的划分访问的层次。有些方法是对系统外的,有些方法是系统内部使用的。把需要暴露给外部的功能集中到外观中,这样既方便客户端使用,也很好的隐藏了内部的细节。

l 过多的或者是不太合理的Facade也容易让人迷惑,到底是调用Facade好呢,还是直接调用模块好。


3.3.4  思考外观模式

1:外观模式的本质

外观模式的本质:封装交互,简化调用

Facade封装了子系统外部和子系统内多个模块的交互过程,从而简化外部的调用。通过外观,子系统为外部提供一些高层的接口,以方便它们的使用。

2:对设计原则的体现

      外观模式很好的体现了“最少知识原则”。

      如果不使用外观模式,客户端通常需要和子系统内部的多个模块交互,也就是说客户端会有很多的朋友,客户端和这些模块之间都有依赖关系,任意一个模块的变动都可能会引起客户端的变动。

      使用外观模式过后,客户端只需要和外观类交互,也就是说客户端只有外观类这一个朋友,客户端就不需要去关心子系统内部模块的变动情况了,客户端只是和这个外观类有依赖关系。

这样一来,客户端不但简单,而且这个系统会更有弹性。当系统内部多个模块发生变化的时候,这个变化可以被这个外观类吸收和消化,并不需要影响到客户端,换句话说就是:可以在不影响客户端的情况下,实现系统内部的维护和扩展。

3:何时选用外观模式

建议在如下情况中,选用外观模式:

  • 如果你希望为一个复杂的子系统提供一个简单接口的时候,可以考虑使用外观模式,使用外观对象来实现大部分客户需要的功能,从而简化客户的使用

  • 如果想要让客户程序和抽象类的实现部分松散耦合,可以考虑使用外观模式,使用外观对象来将这个子系统与它的客户分离开来,从而提高子系统的独立性和可移植性

  • 如果构建多层结构的系统,可以考虑使用外观模式,使用外观对象作为每层的入口,这样可以简化层间调用,也可以松散层次之间的依赖关系

3.3.5  相关模式

l          外观模式和中介者模式
   
这两个模式非常类似,但是有本质的区别。
   中介者模式主要用来封装多个对象之间相互的交互,多用在系统内部的多个模块之间;而外观模式封装的是单向的交互,是从客户端访问系统的调用,没有从系统中来访问客户端的调用。
   在中介者模式的实现里面,是需要实现具体的交互功能的;而外观模式的实现里面,一般是组合调用或是转调内部实现的功能,通常外观模式本身并不实现这些功能。
   中介者模式的目的主要是松散多个模块之间的耦合,把这些耦合关系全部放到中介者中去实现;而外观模式的目的是简化客户端的调用,这点和中介者模式也不同。

l          外观模式和单例模式
   
通常一个子系统只需要一个外观实例,所以外观模式可以和单例模式组合使用,把Facade类实现成为单例。当然,也可以跟前面示例的那样,把外观类的构造方法私有化,然后把提供给客户端的方法实现成为静态的。

l          外观模式和抽象工厂模式
   
外观模式的外观类通常需要和系统内部的多个模块交互,每个模块一般都有自己的接口,所以在外观类的具体实现里面,需要获取这些接口,然后组合这些接口来完成客户端的功能。
   那么怎么获取这些接口呢?就可以和抽象工厂一起使用,外观类通过抽象工厂来获取所需要的接口,而抽象工厂也可以把模块内部的实现对Facade进行屏蔽,也就是说Facade也仅仅只是知道它从模块中获取的它需要的功能,模块内部的细节,Facade也不知道了。


本文链接:研磨设计模式之外观模式(Facade)(模式讲解),转自:http://sishuok.com/forum/blogPost/list/5064.html

相关问答

更多
  • 对于java转过来程序员说是单态设计模式,在PHP上通常说是单例模式,说法不一样,手册上也有介绍: 单例模式(Singleton)用于为一个类生成一个唯一的对象。最常用的地方是数据库连接。 使用单例模式生成一个对象后,该对象可以被其它众多对象所使用。 这样我们可以得到一个独一无二的Example类的对象。 bark(); // 复制对象将导致一个E_USER_ERROR. $test_clone = clone $test; ?>
  • 给你个代码看下吧。更直观一些
  • 都是设计模式,mvc是一种复合的模式,工厂是基本
  • 设计模式是解决重复问题的常用方式。 所有设计模式中的类只是正常类。 重要的是如何组织结构,以及如何共同努力以最佳方式解决给定的问题。 Facade设计模式简化了复杂系统的界面; 因为它通常由组成复杂系统的子系统的所有类组成。 门面将用户屏蔽系统的复杂细节,并为其提供easy to use的simplified view 。 它还将使用系统的代码与子系统的细节decouples ,以便稍后修改系统。 http://www.dofactory.com/Patterns/PatternFacade.aspx ht ...
  • 适配器将给定的类/对象适配到新的接口。 在前者的情况下,通常采用多重继承。 在后一种情况下,对象被一个符合适配器对象包裹并传递。 我们在这里解决的问题是不兼容的接口 。 Facade更像是一个复杂的功能集的简单网关。 您为客户制作一个黑盒子,以减少担心,即使界面更简单 。 代理提供与代理类相同的界面,通常会自己做一些内务管理。 (所以,而不是制作一个重型对象X多个副本,您可以制作一个轻量级代理P副本,而后者又会根据需要管理X并转换您的呼叫。)您正在解决客户端不必管理重型和/或复杂的对象 。 装饰器用于向对象 ...
  • 以下代码段在设计模式中看起来很相似,但我感到困惑。 我认为你看到了两种模式的组成方面。 Facade链接到子系统的各种现有类,以添加一些简化子系统使用的典型功能。 在您引用的示例代码中, ShapeMaker提供了便于制作形状的服务。 调解员与需要合作的各种同事进行联系,以尽量减少同事之间对彼此的了解。 最大限度地减少知识有减少同事之间的耦合(他们只知道调解员)和增加他们的凝聚力(他们通常不需要担心,因为他们不知道更大的图片)的副作用。 在这两种模式中,集中式课程都承担着处理与其相关类别的复杂性的责任。 以 ...
  • 好吧,一旦将子系统封装为对象并为客户端提供所有业务对象和服务的聚合接口,Facade就非常适合SOA架构。 它还减少了架构中的耦合。 我认为您的方法并不重,不会影响系统的可扩展性。 Well, Facade is highly adequate for SOA architectures, once it encapsulates a subsystem as an object and provides an aggregated interface for all business objects an ...
  • 外观基本上是一种方便的功能分组。 如果存在多个这样的分组,您肯定可以制作多个外观。 请注意,使用依赖注入模式时,使用Facade模式远比以前少得多。 A facade is basically a convenient grouping of functionality. If multiple such groupings exist you may certainly make multiple facades. Please note that when using dependency inject ...
  • 在某种程度上你编写了你的例子,是的,它将紧密地结合你的代码。 主要是由于new关键字对你的依赖关系起着glue作用。 请记住,Facade模式不会阻止您创建紧密耦合的依赖项或代码。 使用它的主要目的是使您的软件组件更易于使用,更易读和可维护,并且最后但并非最不可测试的更多。 如果要避免紧密耦合,则需要在Facade类中传递抽象依赖项: public class Facade { private readonly ISystemA subsystemA; private readonly ISystemB ...
  • Facade模式的主要目的不是使您的接口不可变。 来自维基百科 : 外观是一个对象, 它为更大的代码体提供了简化的接口 ,例如类库。 门面可以: 使软件库更易于使用,理解和测试,因为外观具有方便的常用任务方法; 出于同样的原因,使库更具可读性; 减少外部代码对库内部工作的依赖性,因为大多数代码使用外观,从而允许更灵活地开发系统; 使用一个设计良好的API(根据任务需要)包装设计不佳的API集合。 外墙可以变化。 它们用于提供简化的界面。 在将类称为“Facade”之前,您应该问自己,它是否提供了简化的界面并 ...