买了大神关爱民与何红辉所著书籍《设计模式解析与实战》,观后有所感、有所悟。
状态模式:根据其状态改变其行为。例如源码中的控件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);
}
}
时而学习之,不亦说乎,逗比要去吹牛逼啦,看不懂别问我,我不知道,看懂了也别@我,没啥好说的