Android源码设计模式学习笔记-状态模式

状态设计模式抽象一系列操作到一个特定的状态类中,这样避免使用if else或者switch case去区分不同状态逻辑所导致的代码逻辑臃肿,耦合性高.
例如目前有个电视遥控器,它有两种状态,一种开机状态,一种关机状态,它还具有一些操作,例如切换频道和调整音量大小,在开机状态下这下操作才能顺利执行,在关机状态下是不能执行的。于是代码如下:

public class TvController {
    //开机状态
    private final static int POWER_ON = 1;
    //关机状态
    private final static int POWER_OFF = 2;
    private int mState = POWER_OFF;

    public void powerOn(){
        mState = POWER_ON;
        if (mState == POWER_OFF){
            System.out.println("开机啦");
        }
    }

    public void powerOff(){
        mState = POWER_OFF;
        if (mState == POWER_ON){
            System.out.println("关机啦");
        }
    }

    public void nextChannel(){
        if (mState == POWER_ON){
            System.out.println("下一频道");
        }else {
            System.out.println("两个红灯提示没有开机");
        }
    }

    public void prevChannel(){
        if (mState == POWER_ON){
            System.out.println("上一频道");
        }else {
            System.out.println("两个红灯提示没有开机");
        }
    }

    public void turnUp(){
        if (mState == POWER_ON){
            System.out.println("调高音量");
        }else{
            System.out.println("两个红灯提示没有开机");
        }
    }

    public void turnDown(){
        if (mState == POWER_ON){
            System.out.println("调低音量");
        }else{
            System.out.println("两个红灯提示没有开机");
        }
    }
}

可以看到在每次操作nextChannel,prevChannel,turnUp,turnDown时候都要进行if else的判断,如果我们再增加一种待机状态的化,还会增加else if语句,这样每次在增加操作的或者状态的时候都要去修改TvController这个类,这不符合开闭原则,而且容易写代码遗漏某些状态case. 状态模式就是用来解决这个问题的。
先看下状态模式的uml


Android源码设计模式学习笔记-状态模式_第1张图片
image.png

通过抽象所有操作到IState接口中,实现类为不同状态,在不同状态下作出不同的实际操作,Context是一个外部调用用来设置状态,和调用不同操作的类,实际上它也可以实现IState接口。说了这么多,可能还比较抽象,下面看看优化过后的代码。
对电视遥控器的状态操作进行抽象

public interface TvState {
    void nextChannel();
    void prevChannel();
    void turnUp();
    void turnDown();
}

状态抽象类的实现类,在不同状态下实际的操作不一样

public class PowerOnState implements TvState {
    @Override
    public void nextChannel() {
        System.out.println("下一频道");
    }

    @Override
    public void prevChannel() {
        System.out.println("上一频道");
    }

    @Override
    public void turnUp() {
        System.out.println("调高音量");
    }

    @Override
    public void turnDown() {
        System.out.println("调低音量");
    }
}
public class PowerOffState implements TvState {
    @Override
    public void nextChannel() {

    }

    @Override
    public void prevChannel() {

    }

    @Override
    public void turnUp() {

    }

    @Override
    public void turnDown() {

    }
}

对电视遥控器的状态进行抽象(注意之前TvState抽象的是状态操作,这里抽象的是状态)

public interface PowerState {
    void powerOn();
    void powerOff();
}

最后的遥控器类

public class TvController implements PowerState,TvState {

    private TvState tvState;

    @Override
    public void powerOn() {
        tvState = new PowerOnState();
        System.out.println("开机啦");
    }

    @Override
    public void powerOff() {
        tvState = new PowerOffState();
        System.out.println("关机啦");
    }

    @Override
    public void nextChannel() {
        tvState.nextChannel();
    }

    @Override
    public void prevChannel() {
        tvState.prevChannel();
    }

    @Override
    public void turnUp() {
        tvState.turnUp();
    }

    @Override
    public void turnDown() {
        tvState.turnDown();
    }
}

调用实现:

        TvController tvController = new TvController();
        tvController.powerOn();
        tvController.nextChannel();
        tvController.turnDown();
        tvController.powerOff();
        tvController.nextChannel();

最后输出:

01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 开机啦
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 下一频道
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 调低音量
01-12 22:16:07.135 19159-19159/com.example.huangli.statepattern I/System.out: 关机啦

上面讲述一个电视遥控器使用状态模式的一个简单栗子,它把在抽象电源打开状态和关闭状态不同的操作到不同的类中,从而规避了使用if else去判断状态。
什么时候可以使用到这种模式?
只要需要去判断不同状态下面作出不同的操作,并且这几种状态都抽象出一系列操作我们就可以使用这一种模式,举一个实际在安卓开发过程运用状态模式的栗子。
在浏览微博的时候,对感兴趣的微博可以转发,但是在登录和未登陆的情况下操作不一样,登录的情况下我可以直接转发微博,未登录的情况下需要跳转登录页面,评论功能同理。这里就符合使用状态模式的条件,在做出转发和评论的时候需要区分登录和未登录状态,并且登录和未登录状态都能抽象出一系列操作(转发和评论)。
先看下ui


Android源码设计模式学习笔记-状态模式_第2张图片
image.png

当点击转发的时候如果在登录状态下就提示转发成功,未登录的状态就跳转登录画面.
具体代码实现:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.forward_btn).setOnClickListener(this);
        findViewById(R.id.login_btn).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.forward_btn:
                LoginContext.getLoginContext().forward(this);
                break;
            case R.id.login_btn:
                LoginContext.getLoginContext().setState(new LogoutState());
                break;
        }
    }
}
public class LoginActivity extends AppCompatActivity {

    private EditText username;
    private EditText password;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        username = findViewById(R.id.username_et);
        password = findViewById(R.id.password_et);

        findViewById(R.id.login_btn).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                login();
                finish();
            }
        });
    }

    private void login(){
        String userName = username.getText().toString().trim();
        String passWord = password.getText().toString().trim();
        //发送网络请求登陆
        LoginContext.getLoginContext().setState(new LoginedState());
        Toast.makeText(getApplicationContext(),"登录成功",Toast.LENGTH_LONG).show();
    }
}

对状态操作进行抽象

public interface UserState {
    /**
     * 转发
     */
    void forward(Context context);

    /**
     * 评论
     */
    void comment(Context context);
}
public class LoginedState implements UserState{
    @Override
    public void forward(Context context) {
        Toast.makeText(context,"转发微博",Toast.LENGTH_LONG).show();
    }

    @Override
    public void comment(Context context) {
        Toast.makeText(context,"评论微博",Toast.LENGTH_LONG).show();
    }
}
public class LogoutState implements UserState{
    @Override
    public void forward(Context context) {
        gotoLoginActivity(context);
    }

    @Override
    public void comment(Context context) {
        gotoLoginActivity(context);
    }

    private void gotoLoginActivity(Context context){
        Intent intent = new Intent(context,LoginActivity.class);
        context.startActivity(intent);
    }
}

Context类供客户端调用

public class LoginContext implements UserState{
    UserState mState = new LogoutState();

    static LoginContext sLoginContext = new LoginContext();

    private LoginContext() {
    }

    public static LoginContext getLoginContext(){
        return sLoginContext;
    }

    public void setState(UserState userState){
        mState = userState;
    }

    @Override
    public void forward(Context context) {
        mState.forward(context);
    }

    @Override
    public void comment(Context context) {
        mState.comment(context);
    }
}

另外再举一个栗子,目前有款下载上传文件的app, 在3g,wifi,和无网络状态下它们下载和上传的操作也不一样,3g,wifi,和无网络状态下我们可以抽象出来上传和下载的操作,这里同样符合使用状态模式的要求。

你可能感兴趣的:(Android源码设计模式学习笔记-状态模式)