23种设计模式(1)-Facade设计模式

前记

曾经我遇见的一个需求是这样的,接口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()方法。

本博文并不是讲解模板类设计模式,只是借助遇见的这个问题的解决过程来强调设计模式对实现需求或学习技术源码是多么重要。不再废话,开始正文。


设计模式原则

以下为6种设计原则,直接摘录于别的博文或百度,这些内容的最终来源基本都是设计模式经典书籍《设计模式—可复用面向对象软件的基础》

1、开闭原则(Open Close Principle)

开闭原则就是说对扩展开放,对修改关闭。在程序需要进行拓展的时候,不能去修改原有的代码,实现一个热插拔的效果。所以一句话概括就是:为了使程序的扩展性好,易于维护和升级。想要达到这样的效果,我们需要使用接口和抽象类,后面的具体设计中我们会提到这点。

2、里氏代换原则(Liskov Substitution Principle)

里氏代换原则(Liskov Substitution Principle LSP)面向对象设计的基本原则之一。 里氏代换原则中说,任何基类可以出现的地方,子类一定可以出现。 LSP是继承复用的基石,只有当衍生类可以替换掉基类,软件单位的功能不受到影响时,基类才能真正被复用,而衍生类也能够在基类的基础上增加新的行为。里氏代换原则是对“开-闭”原则的补充。实现“开-闭”原则的关键步骤就是抽象化。而基类与子类的继承关系就是抽象化的具体实现,所以里氏代换原则是对实现抽象化的具体步骤的规范。

3、依赖倒转原则(Dependence Inversion Principle)

这个是开闭原则的基础,具体内容:真对接口编程,依赖于抽象而不依赖于具体。

4、接口隔离原则(Interface Segregation Principle)

这个原则的意思是:使用多个隔离的接口,比使用单个接口要好。还是一个降低类之间的耦合度的意思,从这儿我们看出,其实设计模式就是一个软件的设计思想,从大型软件架构出发,为了升级和维护方便。所以上文中多次出现:降低依赖,降低耦合。

5、迪米特法则(最少知道原则)(Demeter Principle)

为什么叫最少知道原则,就是说:一个实体应当尽量少的与其他实体之间发生相互作用,使得系统功能模块相对独立。

6、合成复用原则(Composite Reuse Principle)

原则是尽量使用合成/聚合的方式,而不是使用继承。


问题描述

交流对于程序员这个职位来说是严重缺乏的,我们需要多锻炼啊。就举这个例子,当别人对我们说一句话,我们要领会别人的意思,粗略的讲这个过程主要是耳朵和大脑配合的结果。耳朵首先要“听”别人说的话,也就是获取别人对我们说的话。然后交给大脑去分析,我们才得以明白,用代码来模拟就如下。


不采用设计模式

Ear类

package com.facade.tradition;
/**
 *
 * @author DC
 *
 */
public class Ear {

    /**
     * 别人说的话
     */
    private String words;

    /**
     * 听-获取别人说的话
     */
    public String getWords(String words){
        System.out.println("别人对我说的话:"+words);
        return words;
    }

    /**
     * 耳朵把话传给大脑
     */
    public boolean sendWordsToBrain(Brain brain,String words){
        return brain.sendWordsInBrain(words);
    }
}

Brain类

package com.facade.tradition;
/**
 *
 * @author DC
 *
 */
public class Brain {

    /**
     * 话语
     */
    private String words;

    /**
     * 把话语记录大脑,别人说了话就设置true,反之false
     */
    public boolean sendWordsInBrain(String words){
        this.words=words;
        if(words==null){
            return false;
        }else{
            return true;
        }
    }

    /**
     * 分析话语
     */
    public void explainWords(){
        //模拟分析过程
        try {
            System.out.println("正在分析对方说的话是什么意思......");
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("额,原来你是这个意思!!");
    }
}

测试类TestTradition

我们来测试一下,当别人对我们说了一句话, 如果若想理解别人,在不采用门面模式是怎样的。如图:

package com.facade.tradition;
/**
 * 不利用设计模式,用代码来模拟这个我们和别人交流接听和理解别人话的过程
 * @author DC
 *
 */
public class TestTradition {

    public static void main(String[] args) {
        //这是我的耳朵和大脑
        Ear ear=new Ear();
        Brain brain=new Brain();

        //别人对我说了句话,我耳朵“听”到了
        String words=ear.getWords("你在干吗?");

        //但是我的耳朵并不理解别人说的什么意思,于是交给我的大脑
        ear.sendWordsToBrain(brain, words);

        //我的大脑分析话语
        brain.explainWords();
    }
}

在我耳朵和大脑的配合下,我终于明白了别人的话语,不容易啊,运行结果如下:

分析一下:

再来看一下这个TestTradition代码,你是不是已经发现什么逻辑代码都得我们自己去完成,耳朵的逻辑处理我们得做,大脑的逻辑处理我们也得做。这导致了几个明显的问题:

1. 测试类与Ear类代表的子系统和Brain类代表的子系统严重的耦合在了一起。

2. 我们的测试类不仅与各个类所代表的子系统进行了交互,而且还必须了解两个子系统的类的内部实现情况。

3. 我们系统中类的所有方法都暴露给了测试类,不论是需要暴露和不需要暴露的。

再来看看上面提到的软件开发的设计原则,是不是这几个问题严重违背了一些设计原则。另外,我们并不需要了解和操作Ear或者Brain内部的一些方法。为解决这些问题,可以采用门面设计模式,也就是本博文的Facade设计模式。


采用Facade设计模式

Ear和Brain类同上

Head类:在此我们添加一个门面类,也就是本设计模式的核心类。其实,作为门面,你不觉得没有比Head更合适的类了吗,Head封装了Ear和Brain。那就定义Head为门面类,代码如下:

package com.facade.pattern;

import javax.swing.plaf.synth.SynthSpinnerUI;

/**
 * 模拟门面类,门面类一般是单例的,本例设计一个简单的单例
 *
 * @author DC
 *
 */
public class Head {
    /**
     * 耳朵引用变量
     */
    public Ear ear=null;

    /**
     * 大脑引用变量
     */
    public Brain brain=null;

    /**
     * 头引用变量
     */
    public static Head head=null;

    private Head(){
        ear=new Ear();
        brain=new Brain();
    }

    /**
     * 获得Head头单例
     * @return
     */
    public static Head getInstance(){
        synchronized(Head.class){
            if(head==null){
                return new Head();
            }
            return head;
        }
    }

    /**
     * "听"后明白了话语
     */
    public void explainWords(String words){

        //别人对我说了句话,我耳朵“听”到了
        String yourWords=ear.getWords(words);

        //但是我的耳朵并不理解别人说的什么意思,于是交给我的大脑
        ear.sendWordsToBrain(brain, yourWords);

        //我的大脑分析话语
        brain.explainWords();
    }

}

测试类TestFacade

package com.facade.pattern;
/**
 * 利用设计模式,用代码来模拟这个我们和别人交流接听和理解别人话的过程
 * @author DC
 *
 */
public class TestFacade {

    public static void main(String[] args) {
        //头引用,已经把具体的各个子系统连同其实现细节都封装在内部
        Head head=Head.getInstance();
        //别人说了,我就明白了,并不需要直接和Ear和Brain交互
        head.explainWords("你在干吗??");
    }
}

运行情况和不采用设计模式一样。

分析一下:

再来看看这次发生了什么,Head门面类把Ear和Brain封装到了内部,并且把很多逻辑处理也封装到了内部,比如Ear实例获得话语,Ear实例传递话语,Brain实例记录话语这些都逻辑处理方法。通过这个门面类,只把和用户直接交互的方法explainWords()暴露给用户,其他的几个逻辑方法是属于系统内部方法,因此不需要交互也就不需要暴露出用户。这样的话,就解决了不采用Facade设计模式的几个问题。是不是感觉这个门面设计模式很简单了?


Facade设计模式

facade门面角色:客户端和facade进行交互。此角色知道相关的(一个或者多个)子系统的功能和责任。当客户端想与众多的子系统进行交互时,facade角色会将所有从客户端发来的请求委派到相应的子系统去。

subsystem子系统角色:可以同时有一个或者多个子系统。每一个子系统都不是一个单独的类,而是一个类的集合。每一个子系统都可以被客户端直接调用,或者被门面角色调用。子系统并不知道门面的存在,对于子系统而言,门面仅仅是另外一个客户端而已。

Facade(外观)模式为子系统中的各类(或结构与方法)提供一个简明一致的界面,隐藏子系统的复杂性,使子系统更加容易使用。 Facade所面对的往往是多个类或其它程序单元,通过重新组合各类及程序单元,对外提供统一的接口/界面。门面模式要求一个子系统的外部与其内部的通信必须通过一个统一的门面(Facade)对象进行。

Facade模式的几个使用场景:

  1、当你要为一个复杂子系统提供一个简单接口时。子系统往往因为不断演化而变得越来越复杂。大多数模式使用时都会产生更多更小的类。这使得子系统更具可重用性,也更容易对子系统进行定制,但这也给那些不需要定制子系统的用户带来一些使用上的困难。Facade可以提供一个简单的缺省视图,这一视图对大多数用户来说已经足够,而那些需要更多的可定制性的用户可以越过Facade层。

  2、客户程序与抽象类的实现部分之间存在着很大的依赖性。引入Facade将这个子系统与客户以及其他的子系统分离,可以提高子系统的独立性和可移植性。

  3、当你需要构建一个层次结构的子系统时,使用Facade模式定义子系统中每层的入口点,如果子系统之间是相互依赖的,你可以让它们仅通过Facade进行通讯,从而简化了它们之间的依赖关系。

特别注意

1, 【GOF】的书中指出:在门面模式中,通常只需要一个门面类,并且此门面类只有一个实例,换言之它是一个单例类。当然这并不意味着在整个系统里只能有一个门面类,而仅仅是说对每一个子系统只有一个门面类。或者说,如果一个系统有好几个子系统的话,每一个子系统有一个门面类,整个系统可以有数个门面类。

2,初学者往往以为通过继承一个门面类便可在子系统中加入新的行为,这是错误的。门面模式的用意是为子系统提供一个集中化和简化的沟通管道,而不能向子系统加入新的行为。

以上比较专业的描述参考于书籍与别的博文。通俗一点理解,不采用facade设计模式就像现在的婚礼,采用facade设计模式和古代婚礼的整个过程是一样。

不采用facade设计模式==现代婚礼。

现在你遇见了一个心仪的女孩,恋爱了,打算和她结婚,上门提亲送彩礼什么的一切过程都得你亲力亲为。这个过程细节繁多。毕竟别人家的女儿,丈夫娘怎么会那么轻易让你得逞啦,已哭晕。

采用facade设计模式==古代婚礼。

周礼中规定的婚礼主要分六步,首先男方请媒人带上礼物去女方家里提亲,然后等女方家里回答,如果答应了,再让媒人带上男女双方的生辰去卜吉凶,如果可以结婚再选择一个日子,到了时间让男方和媒人带上礼物去迎娶。这下方便了,什么事都交给媒婆办了,连对方是不是女的我们自己都不能确定了。我们有什么事就找媒婆,媒婆从女方那里得到什么消息再反馈给我们。

自己领悟吧,由于技术和理解有限就写这些了吧。望指教,谢谢!!



参考书籍或博文:

1,《设计模式—可复用面向对象软件的基础》(很不错的书籍)

2,《JAVA设计模式之门面模式(外观模式)》

3,《JAVA设计模式十九–Facade(外观模式)》

4,《Java开发中的23种设计模式详解(转)》

5,《设计模式(15)-Facade Pattern》

时间: 2024-10-11 02:04:13

23种设计模式(1)-Facade设计模式的相关文章

Java开发中的23种设计模式详解(转)

设计模式(Design Patterns) --可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

23种设计模式【转】

23种设计模式 目录 创建型 1. Factory Method(工厂方法) 2. Abstract Factory(抽象工厂) 3. Builder(建造者) 4. Prototype(原型) 5. Singleton(单例) 结构型 6. Adapter Class/Object(适配器) 7. Bridge(桥接) 8. Composite(组合) 9. Decorator(装饰) 10. Facade(外观) 11. Flyweight(享元) 12. Proxy(代理) 行为型 13.

转23种设计模式

1.FACTORY 追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说"来四个鸡翅"就行了.麦当劳和肯德基就是生产鸡翅的Factory 工厂模式:客户类和工厂类分开.消费者任何时候需要某种产品,只需向工厂请求即可.消费者无须修改就可以接纳新产品.缺点是当产品修改时,工厂类也要做相应的修改.如:如何创建及如何向客户端提供. 2.BUILDER MM最爱听的就是"我爱你"这句话了,见到不同

简述23种软件设计模式

简述23种软件设计模式 一.设计模式分类 总体来说设计模式分为三大类: 五种创建型模式:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 七种结构型模式:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 十一种行为型模式:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 二.设计模式概述 创建型模式 1.工厂方法模式 工厂方法模式的创建是因为简单工厂模式有一个问题,在简单工厂模式

0. Java开发中的23种设计模式详解(转)

设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计模式于己于他人于系统都是多赢的,设计模式使代码编制真正工程化,设计模式是软件工程的基石,如同大厦的一块块砖石一样.项目中合理的运用设计模式可以完美的解决很多问题,每种模式在现在中都有相应的原理来与之对应,每一个模式描述了一个在我们周

23种设计模式全解析

一.设计模式的分类 总体来说设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 其实还有两类:并发型模式和线程池模式.用一个图片来整体描述一下: 二.设计模式的六大原则 总原则:开闭原则(Open C

c#中的23种设计模式

C# 23种设计模式汇总 创建型模式 工厂方法(Factory Method) 在工厂方法模式中,工厂方法用来创建客户所需要的产品,同时还向客户隐藏了哪种具体产品类将被实例化这一细节.工厂方法模式的核心是一个抽象工厂类,各种具体工厂类通过抽象工厂类将工厂方法继承下来.如此使得客户可以只关心抽象产品和抽象工厂,完全不用理会返回的是哪一种具体产品,也不用关系它是如何被具体工厂创建的. 抽象工厂模式(Abstract Factory) 抽象工厂模式的主要优点是隔离了具体类的生成,使得客户不需要知道什么

iOS 23 种设计模式

设计模式主要分三个类型:创建型.结构型和行为型. 其中创建型有: 一.Singleton,单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点 二.Abstract Factory,抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类. 三.Factory Method,工厂方法:定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到了子类. 四.Builder,建造模式:将一个复杂对象的构建与他的表示相分离,使

自己用:23种设计模式+UML入门

学习设计模式之前,总是需要UML来辅助 UML的入门 设计模式的灵魂在于:灵活地多态,继承,封装. 把你所描述的类,抽象出一个关键词,形成父类,再把他继承.实现接口 但是继承还是不太好,因为增加了很多冗余成分,所以产生了组合. 运用组合实现了多态里面的东西 对话的形式来写技术可能会更好啊. 老板:小朱啊,今天给我讲一下责任链设计模式吧 我:责任链设计模式啊,我昨天试着写着写着也不知道哪里出错了,他是把策略这个关键词封装成一个数组,然后如果你想添加新的策略的时候,可以在里面直接添加一个策略元素,这