• 策略模式


    一、概述

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

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

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


    二、应用场景

    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变量,在适当时候直接调用变量方法,而不关心具体是哪种解锁方式

    三、总结

    优点:

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

    缺点:

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

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

    用一句话表述策略模式:

    生旦净末丑,上台就是唱

  • 相关阅读:
    关于k12领域Lucene.net+pangu搜索引擎设计开发的一些回顾
    行业门户网站搜索引擎的一些设计开发体会
    快速体验,学习lua(一种可嵌入c++,c#,android,object-c等并进行互调支持热更新的脚本)的入门调试系列(3)
    快速体验,学习lua(一种可嵌入c++,c#,android,object-c等并进行互调支持热更新的脚本)的入门调试系列(2)
    快速体验,学习lua(一种可嵌入c++,c#,android,object-c等并进行互调支持热更新的脚本)的入门调试系列(1)
    记录下自己在Android Studio3.2中导出jar包的过程
    分享下常见的app在手机上安装不上的原因
    关于各种不同开发语言之间数据加密方法(DES,RSA等)的互通的解决方案(c++共享类库方案)
    关于如何解决:服务器上DateTime.Now获取的时间不是北京标准时间的问题
    分享一个自己项目中用到的c++版的日志类(对初学者十分有用的,有问题的可以留言)
  • 原文地址:https://www.cnblogs.com/not2/p/10838708.html
Copyright © 2020-2023  润新知