设计模式之门面模式---Facade Pattern

模式的定义

门面模式(Facade Pattern)也叫做外观模式,定义如下:

Provide a unified interface to a set of interfaces in a subsystem. Facade defines a highet-level interface that makes the subsystem easier to use.

要求一个子系统的外部与其内部的通信必须通过一个统一的对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。

类型

结构类

模式的使用场景

  • 为一个复杂的模块或子系统提供一个借外界访问的接口
  • 子系统相对独立—外界对子系统的访问只要黑箱操作即可

    比如利息的计算问题,比较复杂并且是一个动态的变化过程,但是对于使用该系统的人员来说,他只需要输入金额和存期,其它的不想关心,就需要得到一个结果。这时,门面模式就可以只提供一个访问接口就得到结果。非常合适。

  • 预防低水平人员带来的风险扩散

    比如一个低水平的技术人员参与项目的开发,为了降低个人代码对整体项目的影响风险,一般的做法是画地为牢,中能在指定的子系统中开发,然后再提供门面接口进行访问操作。

UML类图

门面模式注重统一的对象,也就是提供一个访问子系统的接口,除了这个接口,不允许有任何访问子系统的行为发生。其通用类图的uml如下:

Subsystem classes是子系统所有类的简称,它可能代表一个类,也可能代表几十个对象的集合。再简单的说,门面系统中门面对象是外界访问子系统内部的唯一通道,不管子系统内部是多么的杂乱无章,只要有门面对象在,就可以做到”金玉其外,败絮其中“。

门面模式的示意图如下:

角色介绍

Facade门面角色

客户端可以调用这个角色的方法。此角色知晓子系统的所有功能和责任。一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统去,也就是说该角色没有实际的业务逻辑,只是一个委托类。

subsystem classes子系统角色

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

模式的通用源码

subsystem classes 子系统:

public class ClassA {

    public void doSomethingA(){
        System.out.println("ClassA---doSomethingA");
    }
}

public class ClassB {

    public void doSomethingB(){
        System.out.println("ClassB---doSomethingB");
    }
}

public class ClassC {

    public void doSomethingC(){
        System.out.println("ClassC---doSomethingC");
    }
}

Facade–门面对象:


public class Facade {

    private ClassA a = new ClassA();
    private ClassB b = new ClassB();
    private ClassC c = new ClassC();

    public void methodA(){
        this.a.doSomethingA();
    }

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

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

}

客户端:


public class Client {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Facade facade = new Facade();
        facade.methodA();
        facade.methodB();
        facade.methodC();
    }

}

输出结果:

ClassA---doSomethingA
ClassB---doSomethingB
ClassC---doSomethingC

优点

  • 减少系统的相互依赖

    想想看,如果我们不使用门面模式,外界访问直接深入到子系统内部,相互之间是一种强耦合关系,互相互相依赖和关联,是系统设计所不能接受的,门面模式的出现很好的解决了此问题,所有的依赖都是对门面对象的依赖,与子系统无关。

  • 提高了系统的灵活性

    依赖减少了,灵活性就自然提高了。不管子系统如何变化,只要不影响门面对象,就可以自由修改。

  • 提高安全性

    想让你访问子系统中哪些业务就开通哪些逻辑,不在门面对象上开通,我们就不能访问,安全性自然提高了。

缺点

门面模式最大的缺点是不符合开闭原则,对修改关闭,对扩展开放。门面模式中门面对象是重中之重,一旦系统投产生,如果发现了有错误,你怎么解决?完全遵从开闭原型,根本没有办法解决。继承?覆写?都用不上。唯一能做的就是修改门面对象的代码,这个风险是相当大的。请大家要注意。

注意事项

  • 一个子系统可以有多个门面
  • 门面不参考子系统内的业务逻辑

Android源码中的模式实现

门面模式作为结构类设计模式,其作用我们可以理解为一个对用户公开的接口,非常方便用户直接调用其内部方法以实现对所有的子系统(也就是其它的类)的访问和调用。也就是说用户经常使用一个统一的接口来实现对其他的类的访问和调用。

那么android开发时,我们是不是有这样的情况了?

我们是不是在开发时,经常有下面的操作:

使用一个Context对象来发送广播—使用门面对象Context的sendBroadcast方法来实现对ActivityManagerNative类的访问。

使用一个Context对象来启动服务—使用门面对象Context的startService方法来实现对ActivityManagerNative类的访问。

使用一个Context对象来启动activity—使用门面对象Context的startActivity方法来实现对mMainThread.getInstrumentation()的访问。

使用一个Context对象来获取PackageManager信息—使用门面对象Context的getPackageManager()方法来实现的ApplicationPackageManager类的访问。

等等.

也就是说,android开发时的门面模式的样例是Context类。

我们查看Context类:

public abstract class Context {

    ......
    public abstract AssetManager getAssets();

    /** Return a Resources instance for your application‘s package. */
    public abstract Resources getResources();

    /** Return PackageManager instance to find global package information. */
    public abstract PackageManager getPackageManager();

    /** Return a ContentResolver instance for your application‘s package. */
    public abstract ContentResolver getContentResolver();
    ......
    public abstract void sendBroadcast(Intent intent);
    public abstract ComponentName startService(Intent service);
    public abstract void startActivity(Intent intent);
    ......

}

从代码来看,Context 只是一个抽象类,定义了许多抽象接口和定量。其它实现类主要有二个:ContextImpl和ContextWrapper。

先看ContextImpl类,其继承抽象类Context,实现其具体方法,充当一个门面对象的作用,方便用户对所有的三方类访问。

class ContextImpl extends Context {

    final ActivityThread mMainThread;
    final LoadedApk mPackageInfo;

    private final IBinder mActivityToken;

    private final UserHandle mUser;

    private final ApplicationContentResolver mContentResolver;

    private final String mBasePackageName;
    private final String mOpPackageName;

    private final ResourcesManager mResourcesManager;
    private final Resources mResources;
    private final Display mDisplay; // may be null if default display
    private final DisplayAdjustments mDisplayAdjustments = new DisplayAdjustments();

    private final boolean mRestricted;

    private Context mOuterContext;
    private int mThemeResource = 0;
    private Resources.Theme mTheme = null;
    private PackageManager mPackageManager;
    private Context mReceiverRestrictedContext = null;
    ......
    //获取具体的Context
    static ContextImpl getImpl(Context context) {
        Context nextContext;
        while ((context instanceof ContextWrapper) &&
                (nextContext=((ContextWrapper)context).getBaseContext()) != null) {
            context = nextContext;
        }
        return (ContextImpl)context;
    }

    //获取AssetManager
    @Override
    public AssetManager getAssets() {
        return getResources().getAssets();
    }
    //获取Resources
    @Override
    public Resources getResources() {
        return mResources;
    }
    //获取PackageManager
    @Override
    public PackageManager getPackageManager() {
        if (mPackageManager != null) {
            return mPackageManager;
        }

        IPackageManager pm = ActivityThread.getPackageManager();
        if (pm != null) {
            // Doesn‘t matter if we make more than one instance.
            return (mPackageManager = new ApplicationPackageManager(this, pm));
        }

        return null;
    }
    //获取ContentResolver
    @Override
    public ContentResolver getContentResolver() {
        return mContentResolver;
    }
    //获取Looper
    @Override
    public Looper getMainLooper() {
        return mMainThread.getLooper();
    }
    //获取Context
    @Override
    public Context getApplicationContext() {
        return (mPackageInfo != null) ?
                mPackageInfo.getApplication() : mMainThread.getApplication();
    }
    ......
    //发送广播
    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess();
            ActivityManagerNative.getDefault().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                    getUserId());
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
    }
    //启动服务的操作实现方法,关键是调用 ActivityManagerNative.getDefault().startService
    private ComponentName startServiceCommon(Intent service, UserHandle user) {
        try {
            validateServiceIntent(service);
            service.prepareToLeaveProcess();
            ComponentName cn = ActivityManagerNative.getDefault().startService(
                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(
                            getContentResolver()), getOpPackageName(), user.getIdentifier());
            if (cn != null) {
                if (cn.getPackageName().equals("!")) {
                    throw new SecurityException(
                            "Not allowed to start service " + service
                            + " without permission " + cn.getClassName());
                } else if (cn.getPackageName().equals("!!")) {
                    throw new SecurityException(
                            "Unable to start service " + service
                            + ": " + cn.getClassName());
                }
            }
            return cn;
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
    }
    //启动activity,关键是调用方法mMainThread.getInstrumentation().execStartActivity
    public void startActivity(Intent intent, Bundle options) {
        warnIfCallingFromSystemProcess();
        if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
            throw new AndroidRuntimeException(
                    "Calling startActivity() from outside of an Activity "
                    + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
                    + " Is this really what you want?");
        }
        mMainThread.getInstrumentation().execStartActivity(
                getOuterContext(), mMainThread.getApplicationThread(), null,
                (Activity) null, intent, -1, options);
    }
......
}

再查看ContextWrapper 类:

public class ContextWrapper extends Context {
    Context mBase;

    public ContextWrapper(Context base) {
        mBase = base;
    }
    ......
    public Context getBaseContext() {
        return mBase;
    }

    @Override
    public AssetManager getAssets() {
        return mBase.getAssets();
    }
    ......
}

其中所有的方法,只是简单的调用mBase 对应的方法就可以。

再来二张门面模式的示意图,大家看一眼就明白了。

门面模式(ContextImpl 和ContextWrapper),确实是充当了一个门面,其真正的价值是提供一个访问内部系统类的一个统一接口,非常方便用户来访问系统内部,。作为结构类设计模式,这确实是一个在所有类的充当门面的结构类。

参考资料

(1).设计模式之禅—第23章 门面模式

(2)门面模式

https://github.com/simple-android-framework/android_design_patterns_analysis/tree/master/facade/elsdnwn

时间: 2024-11-02 23:32:14

设计模式之门面模式---Facade Pattern的相关文章

门面模式-Facade Pattern(Java实现)

门面模式-Facade Pattern 为一个复杂的模块或子系统提供一个简单的供外界访问的接口 本文中代码的例子如下: 一个矿场有很多矿工, 矿工的职责也都不一样. 但一样的是什么呢? 一样的就是每个矿工每天都在重复一样的事情....起床, 上班, 工作, 下班, 睡觉...... 要想管理这么多矿工的这么多事情可不简单(好麻烦啊...挨个调用的时候开发者也有可能会忘掉某个人, 或者忘掉某一个方法)., 再举个栗子, 比如操作数据库也一样, 要加载驱动, 建立连接, 操作数据库, 关闭连接. 这

设计模式-10外观模式(Facade Pattern)

1.模式动机 在现实生活中,常常存在办事较复杂的例子,如办房产证或注册一家公司,有时要同多个部门联系,这时要是有一个综合部门能解决一切手续问题就好了. 软件设计也是这样,当一个系统的功能越来越强,子系统会越来越多,客户对系统的访问也变得越来越复杂.这时如果系统内部发生改变,客户端也要跟着改变,这违背了"开闭原则",也违背了"迪米特法则(最少知道原则)",所以有必要为多个子系统提供一个统一的接口,从而降低系统的耦合度,这就是外观模式的目标. 2.模式定义 外观模式(F

二十四种设计模式:外观模式(Facade Pattern)

外观模式(Facade Pattern) 介绍为子系统中的一组接口提供一个一致的界面,Facade模式定义了一个高层接口,这个接口使得这一子系统更加容易使用. 示例有一个Message实体类,某对象对它的操作有Get()方法,另外还有一个对象有一个Validate()方法来判断用户是否有权限.现在提供一个高层接口来封装这两个方法. MessageModel using System; using System.Collections.Generic; using System.Text; nam

php门面模式(facade pattern)

书上不全的代码,我自己补全的. <?php /* The facade pattern is used when we want to simplify the complexities of large systems through a simpler interface. It does so by providing convenient methods for most common tasks, through a single wrapper class used by a cli

设计模式之门面模式(facade)

1.定义 门面模式(Facade Pattern)也叫做外观模式,是一种比较常用的封装模式.要求一个子系统的外部与其内部的通讯必须通过一个统一的对象进行.门面模式提供一个高层次的接口,使得子系统更易于使用. 2.通用类图 Facade门面角色:客户端可以调用这个角色的方法.此角色知晓子系统的所有功能和责任.一般情况下,本角色会将所有从客户端发来的请求委派到相应的子系统中去,也就是说该角色没有实际的业务逻辑,只是一个委托类. subsystem子系统角色:可以同时有一个或多个子系统.每个子系统都不

设计模式 - 外观模式(facade pattern) 详解

外观模式(facade pattern) 详解 本文地址: http://blog.csdn.net/caroline_wendy 外观模式(facade pattern): 提供了一个统一的接口, 用来访问子系统中的一群接口. 外观定义了一个高层接口, 让子系统更容易使用. 外观模式包含三个部分: 1. 子系统: 子类, 单个复杂子类 或 多个子类; 2. 外观(facade)类: 把子系统设计的更加容易使用; 3. 客户: 只需要调用外观类. 与适配器模式(adapter pattern)的

大熊君说说JS与设计模式之(门面模式Facade)迪米特法则的救赎篇------(监狱的故事)

一,总体概要 1,笔者浅谈 说起“门面”这个设计模式其实不论新老程序猿都是在无意中就已经运用到此模式了,就像我们美丽的JS程序员一样不经意就使用了闭包处理问题, 1 function Employee(name) { 2 var name = name; 3 this.say = function () { 4 console.log("I am employee " + name) ; 5 } ; 6 } 代码中“say”是一个function,Employee也是一个functio

外观模式 门面模式 Facade 创建型 设计模式(十三)

外观模式(FACADE) 又称为门面模式 意图 为子系统中的一组接口提供一个一致的界面 Facade模式定义了一个高层接口,这一接口使得这一子系统更加易于使用. 意图解析 随着项目的持续发展,系统基本上都是会往功能更全面的方向发展,那么也就意味着我们的系统将会变得更加复杂. 系统会被划分为多个单独的子系统,每个子系统完成一部分功能,通过分工协作完成全部功能. 一个子系统也可能进一步拆分为更小的几个子系统. 程序中的文件将会越来越多,相互关联也会变得更加复杂 当使用一个功能的时候,作为客户端 你需

说说设计模式~门面模式(Facade)

返回目录 门面模式(Facade)属于结构型模式的一种,它符合面向对象的封装原则,但又不符合开闭原则,呵呵,今天我们主要说它的优点,不谈缺点. 定义 门面模式,是指提供一个统一的接口去访问多个子系统的多个不同的接口,它为子系统中的一组接口提供一个统一的高层接口.使用子系统更容易使用. 技巧 我们在设计子类时,由于外部通过门面去访问它的功能,所以一般子类都会被声明为internal. 何时能用到它? 1.客户只需要使用某个复杂系统的子集,或者需要以一种特殊的方式与系统交互时,使用门面模式. 2.当