美文网首页
设计模式知识梳理(4) - 结构型 - 装饰模式

设计模式知识梳理(4) - 结构型 - 装饰模式

作者: 泽毛 | 来源:发表于2018-11-26 20:26 被阅读62次

    一、基本概念

    1.1 定义

    装饰模式使用一种对客户端透明的方式来 动态地扩展对象的功能,同时它是继承关系的一种替代方案,其包含以下四种角色:

    • Component:抽象组件。可以是一个接口或抽象类,其充当的就是被装饰的原始对象。
    • ConcreteComponent:组件具体实现类。Component类的基本实现,也是我们装饰的具体对象。
    • Decorator:抽象装饰者。用于装饰组件对象,内部 一定有一个指向组件对象的引用,大多数情况下该类为抽象类,需要根据不同的装饰逻辑实现不同的具体子类。
    • ConcreteDecoratorA:装饰者具体实现类。

    1.2 例子

    • 定义抽象组件,其包含抽象方法operate()
    /**
     * 抽象组件类。
     *
     * @author lizejun
     **/
    public abstract class Component {
    
        /**
         * 抽象组件类的抽象方法。
         */
        public abstract void operate();
    }
    
    • 定义组件具体实现类。
    /**
     * 组件具体实现类。
     *
     * @author lizejun
     **/
    public class ConcreteComponent extends Component {
    
        @Override
        public void operate() {}
    }
    
    • 定义抽象装饰者。
    /**
     * 抽象装饰者。
     *
     * @author lizejun
     **/
    public class Decorator extends Component {
    
        private Component mComponent;
    
        public Decorator(Component component) {
            mComponent = component;
        }
    
        @Override
        public void operate() {
            mComponent.operate();
        }
    }
    
    • 定义装饰者具体实现类,它包含了两个装饰方法,该方法可以在父类方法的前后调用,为装饰对象 增强功能
    /**
     * 装饰者具体实现类。
     * 
     * @author lizejun
     **/
    public class ConcreteDecoratorA extends Decorator {
    
        public ConcreteDecoratorA(Component component) {
            super(component);
        }
    
        @Override
        public void operate() {
            //在父类之前调用装饰方法。
            operateBefore();
            super.operate();
            //在父类之后调用装饰方法。
            operateAfter();
        }
    
        /**
         * 装饰方法 A。
         */
        private void operateBefore() {
    
        }
    
        /**
         * 装饰方法 B。
         */
        private void operateAfter() {
    
        }
    }
    

    1.3 应用场景

    • 动态地为一个类新增或删除功能。
    • 不能使用继承,但要提供继承的功能。

    1.4 优缺点

    优点

    • 可以通过动态地选择不同的 装饰器(被装饰者),来实现不同的功能。
    • 通过 组合替代继承,避免造成的无限扩张。
    • 装饰者和被装饰者 解耦,新增装饰者功能时无需改变被装饰者的代码,符合开闭原则。

    缺点

    • 当要修改抽象组件的时候,装饰者的代码也可能需要修改。

    二、Android 源码

    Android源码当中,有一个装饰模式的典型应用 Context,它的类图我们在之前分析SP的内部实现原理的时候也有分析过, Android 数据存储知识梳理(3) - SharedPreference 源码解析

    Context是一个抽象类,在其中定义了我们常用的大量抽象方法,它对应于我们上面谈到的 抽象组件

    public abstract class Context {
    
        public abstract void startActivity(@RequiresPermission Intent intent);
    
        public abstract void sendBroadcast(@RequiresPermission Intent intent);
    
        public abstract ComponentName startService(Intent service);
    
    }
    

    其真正的实现是在ContextImpl当中,它继承自Context抽象类,它的角色是 组件的具体实现类

    class ContextImpl extends Context {
    
        @Override
        public void startActivity(Intent intent) {
            warnIfCallingFromSystemProcess();
            startActivity(intent, null);
        }
    
        @Override
        public void sendBroadcast(Intent intent) {
            warnIfCallingFromSystemProcess();
            String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
            try {
                intent.prepareToLeaveProcess(this);
                ActivityManager.getService().broadcastIntent(
                        mMainThread.getApplicationThread(), intent, resolvedType, null,
                        Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                        getUserId());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    
        @Override
        public ComponentName startService(Intent service) {
            warnIfCallingFromSystemProcess();
            return startServiceCommon(service, false, mUser);
        }
    }
    

    而上面谈到的装饰者就是最常见的ActivityServiceApplication这三个,它们有一个共同的基类ContextWrapper,而在ContextWrapper中持有一个指向具体抽象对应的引用mBase,当调用Context定义的抽象方法时,其实是由mBase来完成的。

    public class ContextWrapper extends Context {
    
        Context mBase;
    
        public ContextWrapper(Context base) {
            mBase = base;
        }
    
        protected void attachBaseContext(Context base) {
            if (mBase != null) {
                throw new IllegalStateException("Base context already set");
            }
            mBase = base;
        }
    
        @Override
        public void startActivity(Intent intent) {
            mBase.startActivity(intent);
        }
    

    那么mBase是在哪里赋值的呢?

    我们以Activity为例,看一下ActivityThread当中的代码,当启动一个Activity的时候,必然需要走到performLaunchActivity方法,这里会创建ContextImplActivity对象,最后通过setOuterContextattach方法建立双向的关联,其中Activity#attach就会给基类中的mBase变量赋值。

      private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
            //为 Activity 创建 ContextImpl 对象。
            ContextImpl appContext = createBaseContextForActivity(r);
            Activity activity = null;
            try {
                java.lang.ClassLoader cl = appContext.getClassLoader();
                //创建 Activity。
                activity = mInstrumentation.newActivity(
                        cl, component.getClassName(), r.intent);
                StrictMode.incrementExpectedActivityCount(activity.getClass());
                r.intent.setExtrasClassLoader(cl);
                r.intent.prepareToEnterProcess();
                if (r.state != null) {
                    r.state.setClassLoader(cl);
                }
            } catch (Exception e) {
                if (!mInstrumentation.onException(activity, e)) {
                    throw new RuntimeException(
                        "Unable to instantiate activity " + component
                        + ": " + e.toString(), e);
                }
            }
           
           //ContextImpl 和 Activity 建立双向的关联关系。
           appContext.setOuterContext(activity);
           activity.attach(appContext, this, getInstrumentation(), r.token,
                            r.ident, app, r.intent, r.activityInfo, title, r.parent,
                            r.embeddedID, r.lastNonConfigurationInstances, config,
                            r.referrer, r.voiceInteractor, window, r.configCallback);
      }
    

    三、项目应用

    待补充。

    四、参考文献

    • <<Android 源码设计模式 - 解析与实战>>

    相关文章

      网友评论

          本文标题:设计模式知识梳理(4) - 结构型 - 装饰模式

          本文链接:https://www.haomeiwen.com/subject/yrhiqqtx.html