设计模式解析与实战之状态模式

本文深入探讨了状态模式的概念及其在代码实现中的应用,通过多个实例展示了如何利用状态模式来管理和切换对象的行为。从抽象的理论到具体的代码实践,包括状态模式的基本原理、UML图示例、类和接口设计、工具类实现以及实际应用案例,旨在为开发者提供一种灵活高效的状态管理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

买了大神关爱民与何红辉所著书籍《设计模式解析与实战》,观后有所感、有所悟。
状态模式:根据其状态改变其行为。例如源码中的控件CheckBox,选中状态和未选中状态,呈现的UI是不同的,原理就是根据isChecked状态,选择绘制不同的背景。下面先来看一组UML(第一次画,画得不好贱笑了):

这里写图片描述

上图是一个关于LOL的简单UML,LOL的英雄都具有QWERDF技能,不同的英雄释放不同的技能,我们需要获取他释放技能的信息。下面开始编码啦啦

LOLHero.java

public abstract class LOLHero {

    protected int heroId;

    public int getHeroId() {
        return heroId;
    }

    public void setHeroId(int heroId) {
        this.heroId = heroId;
    }

    public abstract void skillQ();
    public abstract void skillW();
    public abstract void skillE();
    public abstract void skillR();
    public abstract void skillD();
    public abstract void skillF();

}

为了避免累赘代码,这里以英雄寒冰为例HeroHanBin.java


public class HeroHanBin extends LOLHero {

    @Override
    public void skillQ() {
        System.out.println("我要Q死你丫丫,皮卡丘");
    }

    @Override
    public void skillW() {
        System.out.println("看我万箭齐发让你屁股开花,O(∩_∩)O~");
    }

    @Override
    public void skillE() {
        System.out.println("丛林是遮不住我的视野,光耀大地!");
    }

    @Override
    public void skillR() {
        System.out.println("大招来了,小心了..");
    }

    @Override
    public void skillD() {
        System.out.println("这世道变了,我还是归隐山林吧,挥一挥衣袖,不带走一片云彩");
    }

    @Override
    public void skillF() {
        System.out.println("点燃吧,基情四射了");
    }

}

SkillStatus.java和他的继承子类代码不多这里就随便贴点:

public abstract class SkillStatus {

    public abstract String getSkillInformation();
}

public class SkillStatusQ extends SkillStatus{

    @Override
    public String getSkillInformation() {

        return "皮卡丘,我要q死你";
    }

}

public class SkillStatusW extends SkillStatus{

    @Override
    public String getSkillInformation() {

        return "皮卡丘,我要射死你";
    }

}

下面开始编写我们工具类HeroFactory.java和SkillType枚举类型


public class HeroFactory{

    private static HeroFactory instance;

    public HeroFactory(){

    }

    public static HeroFactory getInstance(){

        if(instance==null){
            synchronized (HeroFactory.class) {
                if(instance==null){
                    instance=new HeroFactory();
                }
            }
        }
        return instance;
    }

    public <T extends SkillStatus> T getSkillStatusInstance(Class<T> cls){
        T t=null;

          try {
            t=cls.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return t;
    }

    public <T extends LOLHero> T getLOLHeroInstance(Class<T> cls){
        T t=null;

          try {
            t=cls.newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return t;
    }

    public void onSkill(Class<? extends LOLHero> classLOLHero,SkillType skillType){

        LOLHero mLOLHero=getLOLHeroInstance(classLOLHero);
        SkillStatus mSkillStatus=null;
        switch (skillType.getType()) {
        case SkillType.SKILL_Q:
            mLOLHero.skillQ();
            mSkillStatus=getSkillStatusInstance(SkillStatusQ.class);
            break;
        case SkillType.SKILL_W:
            mLOLHero.skillW();
            mSkillStatus=getSkillStatusInstance(SkillStatusW.class);
            break;

        //...............此处略.................

        default:
            break;
        }
        System.out.println(mSkillStatus.getSkillInformation());

    }

    public enum SkillType{

        Q(0),W(1),E(2),R(3),D(4),F(5);

        private int type;

        private SkillType(int type){
            this.type=type; 
        }

        public int getType(){

            return this.type;
        }
        public static final int SKILL_Q=0;
        public static final int SKILL_W=1;
        public static final int SKILL_E=2;
        public static final int SKILL_R=3;
        public static final int SKILL_D=4;
        public static final int SKILL_F=5;
    }

}

现在我们可以在测试类HeroTest.java里面开撸啦

public class HeroTest {

    public static void main(String[] args) {
        HeroFactory.getInstance().onSkill(HeroHanBin.class, SkillType.Q);
        System.out.println("*******************************************");
        HeroFactory.getInstance().onSkill(HeroHanBin.class,  SkillType.W);
    }
}

/**
   撸出QW技能结果:

   我要Q死你丫丫,皮卡丘
   皮卡丘,我要q死你
   *******************************************
   看我万箭齐发让你屁股开花,O(∩_∩)O~
   皮卡丘,我要射死你
   **/

以上实例,根据英雄选择的技能类型,选择不同的SkillStatus实现,不同的状态对应不同的行为,通过以上代码你也许会产生错觉,这不就是策略模式么?我写的有点像那种感觉,那么如果我用setChecked(boolean isChecked) 、getChecked()替代getSkillStatusInformation()方法,调用技能是执行setChecked(!getChecked)你是否会有所体会呢,如果你还不能体会,别着急,接着看下面一个更简单的实例,先上图UML

当然用户信息不止一个登陆状态,这里只例举状态模式相关的,在我们登陆了就把登陆后的信息保存下来,注销了就清理缓存,这里通过MyApplication、Login、Logout来帮助我们例举状态模式。

UserStatus.java接口类


public interface UserStatus {

    public abstract boolean isLogin();

}

LoginStatus.java类和LogoutStatus.java类同理,这里就医LoginStatus.java为例:

public class LoginStatus implements UserStatus{

    public LoginStatus() {

    }
    @Override
    public boolean isLogin() {

        return true;
    }

}

下面是一个测试的伪代码类UserTest.java


public class UserTest {

    public void onClick(int type){
        if(type==1){
            //注销
            onLogout(new OnLogoutListener() {

                @Override
                public void onSuccess() {
                    MyApplication.getInstance().setUserStatus(new LogoutStatus());
                    System.out.println("注销成功");
                }

                @Override
                public void onFail() {
                    System.out.println("注销失败");
                }
            });
        }else if(type==2){
            //登录
            onLogin("userName", "password",new OnLoginListener() {

                @Override
                public void onSuccess() {
                    MyApplication.getInstance().setUserStatus(new LoginStatus());
                    System.out.println("登录成功");
                }

                @Override
                public void onFail() {
                    System.out.println("登录失败");
                }
            });
        }
    }

    private void onLogout(OnLogoutListener listener) {

    }

    public void onLogin(String userName,String password,OnLoginListener listener){


    }

    public interface OnLoginListener{

        void onFail();
        void onSuccess();
    }

    public interface OnLogoutListener{

        void onFail();
        void onSuccess();
    }
}

状态模式虽然好用简单,代码扩展性和维护性好,但是会增加很多类出来,具体使用场景根据开发者需求和习惯而定。下面再来看看状态模式在源码中的实践,这里已RadioGroup+RadioButton为例。

public class RadioGroup extends LinearLayout {
   /**
     * {@inheritDoc}
     */
    public RadioGroup(Context context) {
        super(context);
        setOrientation(VERTICAL);
        init();
    }

    /**
     * {@inheritDoc}
     */
    public RadioGroup(Context context, AttributeSet attrs) {
        super(context, attrs);
        // retrieve selected radio button as requested by the user in the
        // XML layout file
        TypedArray attributes = context.obtainSt....
        attributes.recycle();
        init();
    }

    private void init() {

    }
}

RadioGrop一般配合RadioButton使用,我们平时看到的onCheckedChanged()方法源自于内部定义的接口OnCheckedChangeListener,对外公开set 方法,该类还提供了setCheckedId()、getCheckedRadioButtonId()、clearCheck()等方法,这里略提一下clearCheck(),传入-1间接调用check(-1)清除选中状态:

 public void check(int id) {
        // don't even bother
        if (id != -1 && (id == mCheckedId)) {
            return;
        }

        if (mCheckedId != -1) {
           //如果当前RadioGroup选中了就调用该方法
            setCheckedStateForView(mCheckedId, false);
        }

        if (id != -1) {
            setCheckedStateForView(id, true);
        }

        setCheckedId(id);
    }

  private void setCheckedStateForView(int viewId, boolean checked) {
        View checkedView = findViewById(viewId);
        if (checkedView != null && checkedView instanceof RadioButton) {
            ((RadioButton) checkedView).setChecked(checked);
        }
    }

RadioButton继承自CompoundButton实现了接口Checkable

/**
 * Defines an extension for views that make them checkable.
 *
 */
public interface Checkable {

    /**
     * Change the checked state of the view
     * 
     * @param checked The new checked state
     */
    void setChecked(boolean checked);

    /**
     * @return The current checked state of the view
     */
    boolean isChecked();

    /**
     * Change the checked state of the view to the inverse of its current state
     *
     */
    void toggle();
}

我们来看看CompoundButton类的实现,这才是本次重点,废话这么久终于找到组织了

public abstract class CompoundButton extends Button implements Checkable {

    public CompoundButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CompoundButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);

        final TypedArray a = context.obtainStyledAttributes(
                attrs, com.android.internal.R.styleable.CompoundButton, defStyleAttr, defStyleRes);

       //..............此处略...............
        final boolean checked = a.getBoolean(
                com.android.internal.R.styleable.CompoundButton_checked, false);
        setChecked(checked);

        a.recycle();

        applyButtonTint();
    }
}

这里继续跟进setChecked()代码块,发现对mChecked状态重新赋值,并执行刷新了DrawableStatus,具体刷新原理改变flag值: mPrivateFlags |= PFLAG_DRAWABLE_STATE_DIRTY;在setState(getDrawableState())方法里通过mPrivateFlags的位运算重新给StateListAnimator setState从而改变背景图片:

 public void setChecked(boolean checked) {
        if (mChecked != checked) {

            mChecked = checked;
            refreshDrawableState();

           //............此处略...............          
        }
    }

再看这段代码的时候还有奇遇,偶然发现了原型模式在源码中的实践,目标定位StateListAnimator (老实说,在没看爱哥书一前,从来不知原型模式为何物,导致以前阅读源码崩溃现象,现在好多了):

 @Override
    public StateListAnimator clone() {
        try {
            StateListAnimator clone = (StateListAnimator) super.clone();
            clone.mTuples = new ArrayList<Tuple>(mTuples.size());
            clone.mLastMatch = null;
            clone.mRunningAnimator = null;
            clone.mViewRef = null;
            clone.mAnimatorListener = null;
            clone.initAnimatorListener();
            final int tupleSize = mTuples.size();
            for (int i = 0; i < tupleSize; i++) {
                final Tuple tuple = mTuples.get(i);
                final Animator animatorClone = tuple.mAnimator.clone();
                animatorClone.removeListener(mAnimatorListener);
                clone.addState(tuple.mSpecs, animatorClone);
            }
            clone.setChangingConfigurations(getChangingConfigurations());
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError("cannot clone state list animator", e);
        }
    }

时而学习之,不亦说乎,逗比要去吹牛逼啦,看不懂别问我,我不知道,看懂了也别@我,没啥好说的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值