策略模式

一、概述

一般问题:有时候一个系统需要动态地在几种算法中选择一种,或者一个对象需要动态地在几种行为中切换,如果不用恰当的模式,这些行为就只好使用多重条件选择语句来实现。

核心方案:将这些算法或行为封装成一个一个的类,使它们之间可以任意地替换。

设计意图:策略模式的设计核心是把对算法的调用责任和算法本身分割开来。我们把一个算法封装之后称之为一个策略,调用者只关注调用逻辑,而不关心策略主体,这就要求策略之间可以任意替换。那么定义一个接口来规范和统一各个策略就再合适不过,更何况这些策略本身的功能就是相似的。这样,调用者只需要保留一个接口对象即可,而不需要再保留所有策略实例。策略模式的设计图如下:


 

二、应用场景

Android锁屏密码的架构就采用了策略模式,解锁方式有简单密码解锁、图案密码解锁和复杂密码解锁等,每一种解锁方式都有各自的出场动画、消失动画、信息提示等。采用策略模式就是把每一种解锁方式设计成一个策略,为了方便讲解,我们做了简化和改动,设计图如下:

其中,KeyguardSecurityView是定义的策略接口

  public interface KeyguardSecurityView {
            /**
         * Show a message on the security view with a specified color
         */
        void showMessage(CharSequence message, int color);
    
        /**
         * Starts the animation which should run when the security view appears.
         */
        void startAppearAnimation();
    
        /**
         * Starts the animation which should run when the security view disappears.
         */
        boolean startDisappearAnimation(Runnable finishRunnable);
    }

KeyguardPinView、KeyguardPatternView和KeyguardPasswordView分别对应简单密码、图案解锁和复杂密码三种解锁方式。以KeyguardPatternView代码为例,每一种解锁方式都有各自不同的方法体:

  public class KeyguardPatternView extends LinearLayout implements KeyguardSecurityView{
    
            /**
         * 图案解锁自己的信息展示方法实现
         */
            @Override
        public void showMessage(CharSequence message, int color) {
            if (!mLockPatternView.isEnabled()) {
                return;
            }
            mSecurityMessageDisplay.setNextMessageColor(color);
            mSecurityMessageDisplay.setMessage(message);
        }
    
            /**
         * 图案解锁自己的出场动画方法实现
         */
        @Override
        public void startAppearAnimation() {
            enableClipping(false);
            setAlpha(1f);
            setTranslationY(mAppearAnimationUtils.getStartTranslation());
            AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */, 500 /* duration */,
                    0, mAppearAnimationUtils.getInterpolator());
            mAppearAnimationUtils.startAnimation2d(
                    mLockPatternView.getCellStates(),
                    new Runnable() {
                        @Override
                        public void run() {
                            enableClipping(true);
                        }
                    },
                    this);
            if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
                mAppearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
                        AppearAnimationUtils.DEFAULT_APPEAR_DURATION,
                        mAppearAnimationUtils.getStartTranslation(),
                        true /* appearing */,
                        mAppearAnimationUtils.getInterpolator(),
                        null /* finishRunnable */);
            }
        }
    
            /**
         * 图案解锁自己的消失动画方法实现
         */
        @Override
        public boolean startDisappearAnimation(final Runnable finishRunnable) {
            float durationMultiplier = mKeyguardUpdateMonitor.needsSlowUnlockTransition()
                    ? DISAPPEAR_MULTIPLIER_LOCKED
                    : 1f;
            mLockPatternView.clearPattern();
            enableClipping(false);
            setTranslationY(0);
            AppearAnimationUtils.startTranslationYAnimation(this, 0 /* delay */,
                    (long) (300 * durationMultiplier),
                    -mDisappearAnimationUtils.getStartTranslation(),
                    mDisappearAnimationUtils.getInterpolator());
    
            DisappearAnimationUtils disappearAnimationUtils = mKeyguardUpdateMonitor
                    .needsSlowUnlockTransition()
                            ? mDisappearAnimationUtilsLocked
                            : mDisappearAnimationUtils;
            disappearAnimationUtils.startAnimation2d(mLockPatternView.getCellStates(),
                    () -> {
                        enableClipping(true);
                        if (finishRunnable != null) {
                            finishRunnable.run();
                        }
                    }, KeyguardPatternView.this);
            if (!TextUtils.isEmpty(mSecurityMessageDisplay.getText())) {
                mDisappearAnimationUtils.createAnimation(mSecurityMessageDisplay, 0,
                        (long) (200 * durationMultiplier),
                        - mDisappearAnimationUtils.getStartTranslation() * 3,
                        false /* appearing */,
                        mDisappearAnimationUtils.getInterpolator(),
                        null /* finishRunnable */);
            }
            return true;
        }
    
    }

KeyguardBouncer是调用者,为方便展示,做了些改动

  public class KeyguardBouncer{
    
            //定义解锁方式,即策略变量
            private KeyguardSecurityView mSecurityView;
    
            public void show() {
                    SecurityMode securityMode = mSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser());
            mSecurityView = getSecurityView(securityMode); //这里为解锁方式赋值
    
                    if (mKeyguardView.getHeight() != 0 && mKeyguardView.getHeight() != mStatusBarHeight) {
                mKeyguardView.startAppearAnimation(); //调用解锁方式的出场动画方法,而不关心具体是哪种解锁方式
            }
            }
    
            public void showMessage(String message, int color) {
            if (mKeyguardView != null) {
                mKeyguardView.showMessage(message, color); //调用解锁方式的信息展示方法,而不关心具体是哪种解锁方式
            } else {
                Log.w(TAG, "Trying to show message on empty bouncer");
            }
        }
    
            public void startPreHideAnimation(Runnable runnable) {
            mIsAnimatingAway = true;
            if (mKeyguardView != null) {
                mKeyguardView.startDisappearAnimation(runnable); //调用解锁方式的退场动画方法,而不关心具体是哪种解锁方式
            } else if (runnable != null) {
                runnable.run();
            }
        }
    }

分析上面代码:

  • KeyguardSecurityView是对解锁方式的抽象,即策略模式的策略接口
  • KeyguardPinView、KeyguardPasswordView和KeyguardPatternView是三种具体策略
  • KeyguardBouncer是调用者,维护一个KeyguardSecurity变量,在适当时候直接调用变量方法,而不关心具体是哪种解锁方式

三、总结

优点:

  • 各算法可以自由切换
  • 避免多重条件判断
  • 扩展性良好

缺点:

  • 策略类可能会很多,造成类膨胀
  • 原算法可以私有,但现在所有策略类都对外暴露

总结:策略模式是一种行为型的设计模式,可以优雅地避免多重条件选择语句而实现多种算法的自由切换。当策略多于四个时,应当考虑采用复合模式,从而避免策略类膨胀。

用一句话表述策略模式:

生旦净末丑,上台就是唱

 

posted @ 2019-05-09 15:27  西贝雪  阅读(432)  评论(0)    收藏  举报