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

来源:互联网 发布:东盟十国旅游的数据 编辑:程序博客网 时间:2024/05/01 23:56

买了大神关爱民与何红辉所著书籍《设计模式解析与实战》,观后有所感、有所悟。
状态模式:根据其状态改变其行为。例如源码中的控件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);        }    }

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

0 0