研磨设计模式之外观模式(Facade)(场景问题)

2019-03-28 21:46|来源: 网络

3.1  场景问题

3.1.1  生活中的示例

   外观模式在现实生活中的示例很多,比如:组装电脑,通常会有两种方案。

   一个方案是去电子市场把自己需要的配件都买回来,然后自己组装,绝对DIY(Do It Yourself)。这个方案好是好,但是需要对各种配件都要比较熟悉,这样才能选择最合适的配件,而且还要考虑配件之间的兼容性。如图3.1所示:

图3.1  客户完全自己组装电脑

   另外一个方案,就是到电子市场,找一家专业装机的公司,把具体的要求一讲,然后就等着拿电脑就好了。当然价格会比自己全部DIY贵一些,但综合起来这还算是个不错的选择,估计也是大多数人的选择。如图3.2所示:

图3.2  找专业的装机公司组装电脑

   这个专业的装机公司就相当于本章的主角——Facade。有了它,我们就不用自己去跟众多卖配件的公司打交道,只需要跟装机公司交互就可以了,由装机公司去跟各个卖配件的公司交互,并组装好电脑返回给我们。

   把上面的过程抽象一下,如果把电子市场看成是一个系统,而各个卖配件的公司看成是模块的话,就类似于出现了这样一种情况:客户端为了完成某个功能,需要去调用某个系统中的多个模块,把它们称为是A模块、B模块和C模块吧,对于客户端而言,那就需要知道A、B、C这三个模块的功能,还需要知道如何组合这多个模块提供的功能来实现自己所需要的功能,非常麻烦。

   要是有一个简单的方式能让客户端去实现相同的功能多好啊,这样,客户端就不用跟系统中的多个模块交互,而且客户端也不需要知道那么多模块的细节功能了,实现这个功能的就是Facade。


3.1.2  代码生成的应用

   考虑这样一个实际的应用:代码生成。

   很多公司都有这样的应用工具,能根据配置生成代码。一般这种工具都是公司内部使用,较为专有的工具,生成的多是按照公司的开发结构来实现的常见的基础功能,比如增删改查。这样每次在开发实际应用的时候,就可以以很快的速度把基本的增删改查实现出来,然后把主要的精力都放在业务功能的实现上。

   当然这里不可能去实现一个这样的代码生成工具,那需要整本书的内容,这里仅用它来说明外观模式。

   假设使用代码生成出来的每个模块都具有基本的三层架构,分为:表现层、逻辑层和数据层,那么代码生成工具里面就应该有相应的代码生成处理模块。

   另外,代码生成工具自身还需要一个配置管理的模块,通过配置来告诉代码生成工具,每个模块究竟需要生成哪些层的代码,比如:通过配置来描述,是只需要生成表现层代码呢,还是三层都生成。具体的模块示意如图3.3所示;

图3.3  代码生成工具的模块示意图

   那么现在客户端需要使用这个代码生成工具来生成需要的基础代码,该如何实现呢?


3.1.3  不用模式的解决方案

   有朋友会想,开发一个这样的工具或许会比较麻烦,但是使用一下,应该不难吧,直接调用不就可以了。

   在示范客户端之前,先来把工具模拟示范出来,为了简单,每个模块就写一个类,而且每个类只是实现一个功能,仅仅做一个示范。

(1)先看看描述配置的数据Model,示例代码如下:

/**

* 示意配置描述的数据Model,真实的配置数据会很多

*/

public class ConfigModel {

   /**

    * 是否需要生成表现层,默认是true

    */

   private boolean needGenPresentation = true;

   /**

    * 是否需要生成逻辑层,默认是true

    */

   private boolean needGenBusiness = true;

   /**

    * 是否需要生成DAO,默认是true

    */

   private boolean needGenDAO = true;

   public boolean isNeedGenPresentation() {

      return needGenPresentation;

   }

   public void setNeedGenPresentation(

      this.needGenPresentation = needGenPresentation;

   }

   public boolean isNeedGenBusiness() {

      return needGenBusiness;

   }

   public void setNeedGenBusiness(boolean needGenBusiness) {

      this.needGenBusiness = needGenBusiness;

   }

   public boolean isNeedGenDAO() {

      return needGenDAO;

   }

   public void setNeedGenDAO(boolean needGenDAO) {

      this.needGenDAO = needGenDAO;

   }

}


(2)接下来看看配置管理的实现示意,示例代码如下:

/**

* 示意配置管理,就是负责读取配置文件,

* 并把配置文件的内容设置到配置Model中去,是个单例

*/

public class ConfigManager {

   private static ConfigManager manager = null;

   private static ConfigModel cm = null;

   private ConfigManager(){

      //

   }

   public static ConfigManager getInstance(){

      if(manager == null){

          manager = new ConfigManager();

          cm = new ConfigModel();

          //读取配置文件,把值设置到ConfigModel中去,这里省略了

      }

      return manager;

   }

   /**

    * 获取配置的数据

    * @return 配置的数据

    */

   public ConfigModel getConfigData(){

      return cm;

   }

}


(3)接下来就来看看各个生成代码的模块,在示意中,它们的实现类似,就是获取配置文件的内容,然后按照配置来生成相应的代码。分别看看它们的示意实现,先看生成表现层的示意实现,示例代码如下:

/**

* 示意生成表现层的模块

*/

public class Presentation {

   public void generate(){

      //1:从配置管理里面获取相应的配置信息

      ConfigModel cm = ConfigManager.getInstance().getConfigData();

      if(cm.isNeedGenPresentation()){

          //2:按照要求去生成相应的代码,并保存成文件

          System.out.println("正在生成表现层代码文件");

      }

   }

}

生成逻辑层的示意实现,示例代码如下:

/**

* 示意生成逻辑层的模块

*/

public class Business {

   public void generate(){

      ConfigModel cm = ConfigManager.getInstance().getConfigData();

      if(cm.isNeedGenBusiness()){

          System.out.println("正在生成逻辑层代码文件");

      }

   }

}

生成数据层的示意实现,示例代码如下:

/**

* 示意生成数据层的模块

*/

public class DAO {

   public void generate(){

      ConfigModel cm = ConfigManager.getInstance().getConfigData();

      if(cm.isNeedGenDAO()){

          System.out.println("正在生成数据层代码文件");

      }

   }

}


(4)此时的客户端实现,就应该自行去调用这多个模块了,示例代码如下:

public class Client {

   public static void main(String[] args) {

      //现在没有配置文件,就直接使用默认的配置,通常情况下,三层都应该生成,

      //也就是说客户端必须对这些模块都有了解,才能够正确使用它们

      new Presentation().generate();

      new Business().generate();

      new DAO().generate();

   }

}

运行结果如下:

正在生成表现层代码文件

正在生成逻辑层代码文件

正在生成数据层代码文件


3.1.4  有何问题

仔细查看上面的实现,会发现其中有一个问题:那就是客户端为了使用生成代码的功能,需要与生成代码子系统内部的多个模块交互。

这对于客户端而言,是个麻烦,使得客户端不能简单的使用生成代码的功能。而且,如果其中的某个模块发生了变化,还可能会引起客户端也需要跟着变化。

那么如何实现,才能让子系统外部的客户端在使用子系统的时候,既能简单的使用这些子系统内部的模块功能,而又不用客户端去与子系统内的多个模块交互呢?


本文链接:研磨设计模式之外观模式(Facade)(场景问题),转自:http://sishuok.com/forum/blogPost/list/5062.html

相关问答

更多
  • Singleton--单态模式 vo--值对象 DAO MVC 工厂模式 常用的吧
  • 设计模式就是设计程序的一种方法的总结,比如单例模式 public class A { private static final A a = new A() ; private A(){} public static A getInstance(){ return a ; } } 这样A永远只能有一个实例,因为他的构造方法已经是私有的,必须通过方getInstance方法来获取实例而且无论是多线程还是单线程无论获得多少次只有一个A的对象在为你服务。
  • 我认为你把太多的责任(实现)放在门面上。 我通常会认为这是其他类中实际实现的前端。 因此,外观中的私有方法可能是一个或多个其他类中的公共方法。 然后你可以在那里测试它们。 I think you are putting too many responsibilities (implementations) into the facade. I would normally consider this to be a front-end for actual implementations that are ...
  • 设计模式是解决重复问题的常用方式。 所有设计模式中的类只是正常类。 重要的是如何组织结构,以及如何共同努力以最佳方式解决给定的问题。 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”之前,您应该问自己,它是否提供了简化的界面并 ...