本文实例讲述了Android编程设计模式之状态模式。分享给大家供大家参考,具体如下:

一、介绍

状态模式中的行为是由状态来决定的,不同的状态下有不同的行为。状态模式和策略模式的结构几乎完全一样,但它们的目的、本质却完全不一样。状态模式的行为是平行的、不可替换的,策略模式的行为是彼此独立、可相互替换的。用一句话来表述,状态模式把对象的行为包装在不同的状态对象里,每一个状态对象都有一个共同的抽象状态基类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。

二、定义

当一个对象的内在状态改变时允许改变其行为,这个对象看起来像是改变了其类。

三、使用场景

(1)一个对象的行为取决于它的状态,并且它必须在运行时根据状态改变它的行为。

(2)代码中包含大量与对象状态有关的条件语句,例如,一个操作中含有庞大的多分支语句(if-else或switch-case),且这些分支依赖于该对象的状态。

状态模式将每一个条件分支放入一个独立的类中,这使得你可以根据对象自身的情况将对象的状态作为一个对象,这一对象可以不依赖与其他对象而独立变化,这样通过多态去除过多的、重复的if-else等分支语句。

四、状态模式的UML类图

UML类图:

角色介绍:

Context:环境类,定义客户感兴趣的接口,维护一个State子类的实例,这个实例定义了对象的当前状态。

State:抽象状态类或状态接口,定义一个或者一组接口,表示该状态下的行为。

ConcreteStateA、ConcreteStateB:具体状态类,每一个具体的状态类实现抽象State中定义的接口,从而达到不同状态下的不同行为。

五、简单示例

下面我们就以电视遥控器为例来演示一下状态模式的实现。我们首先将电视的状态简单分为开机状态和关机状态,在开机状态下可以通过遥控器进行频道切换、调整音量等操作,但是,此时重复按开机键是无效的;而在关机状态下,频道切换、调整音量、关机都是无效的操作,只有按开机按钮时才会生效。也就是说电视的内部状态决定了遥控器的行为。

首先是普通的实现方法:

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(){

if(mState ==POWER_OFF){

System.out.println("电视开机了");

}

mState = POWER_ON;

}

public void powerOff(){

if(mState ==POWER_ON){

System.out.println("电视关机了");

}

mState = POWER_OFF;

}

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("没有开机");

}

}

}

可以看到,每次执行通过判断当前状态来进行操作,部分的代码重复,假设状态和功能增加,就会越来越难以维护。这时可以使用状态模式,如下:

电视的状态接口:

/**

* 电视状态接口,定义了电视的操作函数

*

**/

public interface TVState {

public void nextChannel();

public void prevChannel();

public void turnUp();

public void turnDown();

}

关机状态:

/**

*

* 关机状态,操作无结果

*

* */

public class PowerOffState implements TVState{

@Override

public void nextChannel() {

}

@Override

public void prevChannel() {

}

@Override

public void turnUp() {

}

@Override

public 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 interface PowerController {

public void powerOn();

public void powerOff();

}

电视遥控器:

/**

* 电视遥控器

*

* */

public class TVController implements PowerController{

TVState mTVState;

public void setTVState(TVState mTVState){

this.mTVState = mTVState;

}

@Override

public void powerOn() {

setTVState(new PowerOnState());

System.out.println("开机了");

}

@Override

public void powerOff() {

setTVState(new PowerOffState());

System.out.println("关机了");

}

public void nextChannel(){

mTVState.nextChannel();

}

public void prevChannel(){

mTVState.prevChannel();

}

public void turnUp(){

mTVState.turnUp();

}

public void turnDown(){

mTVState.turnDown();

}

}

调用:

public class Client {

public static void main(String[] args) {

TVController tvController = new TVController();

//设置开机状态

tvController.powerOn();

//下一频道

tvController.nextChannel();

//调高音量

tvController.turnUp();

//关机

tvController.powerOff();

//调低音量,此时不会生效

tvController.turnDown();

}

}

输出结果如下:

开机了

下一频道

调高音量

关机了

上述实现中,我们抽象了一个TVState接口,该接口中有操作电视的所有函数,该接口有两个实现类,即开机状态(PowerOnState)和关机状态(PowerOffState)。开机状态下只有开机功能是无效的,也就是说在已经开机的时候用户在按开机键不会产生任何反应;而在关机状态下,只有开机功能是可用的,其他功能都不会生效。同一个操作,如调高音量的turnUp函数,在关机状态下无效,在开机状态下就会将电视的音量调高,也就是说电视内部状态影响了电视遥控器的行为。状态模式将这些行为封装到状态类中,在进行操作时将这些功能转发给状态对象,不同的状态有不同的实现,这样就通过多态的形式去除了重复、杂乱的if-else语句,这也正是状态模式的精髓所在。

六、Android实战中的使用

1、登录系统,根据用户是否登录,判断事件的处理方式。

2、Wi-Fi管理,在不同的状态下,WiFi的扫描请求处理不一。

下面以登录系统为例讲解下状态模式在实战中的使用:

在android开发中,我们遇到登录界面是十分常见的,而状态设计模式在登录界面的应用十分广泛,用户在登录状态下和未登录状态下,对逻辑的操作是不一样的。例如最常见的情况就是在玩新浪微博的时候,用户在登录的情况下才能完成评论和转发微博的操作;而当用户处于未登录的情况下要执行转发和评论微博的操作需要进入登录界面登录以后才能执行,所以面对这两者不同的状况,利用状态设计模式来设计这个例子最好不过。

1、状态基类

前面我们讲过状态设计模式的原理实则是多态,在这里我们用UserState接口表示此基类,包换转发操作和评论这两种状态,代码如下:

public interface UserState {

/**

* 转发操作

* @param context

*/

public void forword(Context context);

/**

* 评论操作

* @param context

*/

public void commit(Context context);

}

2、用户在登录和未登录两种状况下的实现类LoginState和LogoutState;代码如下:

在LoginState.java中,用户是可以执行转发和评论操作。

public class LoginState implements UserState{

@Override

public void forword(Context context) {

Toast.makeText(context, "转发成功", Toast.LENGTH_SHORT).show();

}

@Override

public void commit(Context context) {

Toast.makeText(context, "评论成功", Toast.LENGTH_SHORT).show();

}

}

在LogoutState.java中,用户在未登录的情况下不允许执行操作,而是应该跳转到登录界面执行登录以后才可以执行。

public class LogoutState implements UserState{

/**

* 跳转到登录界面登录以后才能转发

*/

@Override

public void forword(Context context) {

gotoLohinActivity(context);

}

/**

* 跳转到登录界面登录以后才能评论

*/

@Override

public void commit(Context context) {

gotoLohinActivity(context);

}

/**

* 界面跳转操作

* @param context

*/

private void gotoLohinActivity(Context context){

context.startActivity(new Intent(context,LoginActivity.class));

}

}

3、操作角色LoginContext

这里的LoginContext就是在状态模式的Context角色,是用户操作对象和管理对象,LoginContext委托相关的操作给状态对象,在其中状态的发生改变,LoginContext的行为也发生改变。LoginContext的代码如*下:

温馨提示:

这里我们用到单例就是为了全局只有一个LoginContext去控制用户状态;

public class LoginContext {

//用户状态默认为未登录状态

UserState state = new LogoutState();

private LoginContext(){};//私有构造函数,避免外界可以通过new 获取对象

//单例模式

public static LoginContext getInstance(){

return SingletonHolder.instance;

}

/**

*静态代码块

*/

private static class SingletonHolder{

private static final LoginContext instance = new LoginContext();

}

public void setState(UserState state){

this.state = state;

}

//转发

public void forward(Context context){

state.forword(context);

}

//评论

public void commit(Context context){

state.commit(context);

}

}

4、界面展示

LoginActivity.java,此界面执行登录操作,登录成后把 LoginContext.getInstance().setState(new LoginState());设置为登录状态,在MainActivity中就执行的是登录状态下的操作,即可以转发可评论;

public class LoginActivity extends Activity implements OnClickListener{

private static final String LOGIN_URL = "http://10.10.200.193:8080/Day01/servlet/LoginServlet";

private EditText et_username;

private EditText et_password;

private Button btn_login;

private String username;

private String password;

private KJHttp http;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_login);

initView();

initData();

}

private void initView() {

et_username = (EditText) findViewById(R.id.et_username);

et_password = (EditText) findViewById(R.id.et_password);

btn_login = (Button) findViewById(R.id.btn_login);

btn_login.setOnClickListener(LoginActivity.this);

}

private void initData() {

http = new KJHttp();

}

/**

* 执行登录操作

*

* @param username2

* @param password2

*/

protected void sendLogin(String username2, String password2) {

HttpParams params = new HttpParams();

params.put("username", "user1");

params.put("password", "123456");

http.post(LOGIN_URL, params, new HttpCallBack() {

@Override

public void onSuccess(String t) {

if ("200".equals(t)) {

//设置为登录状态

LoginContext.getInstance().setState(new LoginState());

startActivity(new Intent(LoginActivity.this,MainActivity.class));

finish();

Toast.makeText(LoginActivity.this, "登录成功", Toast.LENGTH_SHORT).show();

}

}

});

}

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.btn_login:

username = et_username.getEditableText().toString().trim();

password = et_password.getEditableText().toString().trim();

if (TextUtils.isEmpty(username) || TextUtils.isEmpty(password)) {

Toast.makeText(LoginActivity.this, "用户名密码不能为空", Toast.LENGTH_SHORT).show();

return;

}

sendLogin(username, password);

break;

}

}

}

MainActivity.java,在用户登录成功后,点击转发和评论执行的是登录状态下的操作,而当用户注销时,我们把LoginContext的状态设置为未登录状态;LoginContext.getInstance().setState(new LogoutState());此时在点击转发和评论操作时就会跳到用户登录界面。

public class MainActivity extends Activity {

private Button btn_forward;

private Button btn_commit;

private Button btn_logout;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

initView();

initListener();

}

private void initView() {

btn_forward = (Button) findViewById(R.id.btn_forward);

btn_commit = (Button) findViewById(R.id.btn_commit);

btn_logout = (Button) findViewById(R.id.btn_logout);

}

private void initListener() {

//转发操作

btn_forward.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

//调用LoginContext里面的转发函数

LoginContext.getInstance().forward(MainActivity.this);

}

});

//评论操作

btn_commit.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

//调用LoginContext里面的转发函数

LoginContext.getInstance().commit(MainActivity.this);

}

});

//注销操作

btn_logout.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

//设置为注销状态

LoginContext.getInstance().setState(new LogoutState());

}

});

}

}

七、总结

状态模式的关键点在于不同的状态下对于同一行为有不同的响应,这其实就是一个将if-else用多态来实现的一个具体示例。在if-else或者switch-case形式下根据不同的状态进行判断,如果是状态A那么执行方法A,状态B执行方法B,但这种实现使得逻辑耦合在一起,易于出错,通过状态模式能够很好的消除这类”丑陋“的逻辑处理,当然并不是任何出现if-else的地方都应该通过状态模式重构,模式的运用一定要考虑所处的情景以及你要解决的问题,只有符合特定的场景才建议使用对应的模式。

优点:

将所有与一个特定的状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将繁琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性与可维护性。

缺点:

状态模式的使用必然会增加系统类和对象的个数。

希望本文所述对大家Android程序设计有所帮助。

android 状态模式,Android编程设计模式之状态模式详解相关推荐

  1. java中的装饰模式讲解,java 中设计模式(装饰设计模式)的实例详解

    搜索热词 java 中设计模式(装饰设计模式)的实例详解 应用场景: 在不对原有对象类进行修改的基础上,给一个或多个已有的类对象提供增强额外的功能. 我觉得可以从字面理解,装饰,装饰房子.房子可以看成 ...

  2. java并发编程:设计原则与模式_java编程设计模式一——策略模式

    定义: 策略模式定义了算法族,分别封装起来,让它们之间相互替代.此模式让算法的变化独立于使用算法的客户. 问题描述: 建立一个鸭子Duck的父类,它有很多子类,Duck都会游泳,但是有鸭子会飞,有的鸭 ...

  3. java备忘录模式 类图,Android编程设计模式之备忘录模式详解

    本文实例讲述了Android编程设计模式之备忘录模式.分享给大家供大家参考,具体如下: 一.介绍 备忘录模式是一种行为模式,该模式用于保存对象当前状态,并且在之后可以再次恢复到此状态,这有点像我们平时 ...

  4. android 多闹钟实现代码,Android编程实现闹钟的方法详解

    Android编程实现闹钟的方法详解 发布时间:2020-09-30 10:18:02 来源:脚本之家 阅读:75 作者:Jacob-wj 本文实例讲述了Android编程实现闹钟的方法.分享给大家供 ...

  5. Android 系统(214)---Android 7.1.1时间更新NITZ和NTP详解

    Android 7.1.1时间更新NITZ和NTP详解 1.NTP和NITZ简介 最近在项目中遇到手机首次插上移动卡时不能自动更新时间的问题,就特意跟了下Android系统中手机时间更新有两种方式NT ...

  6. 阿里聚安全Android应用漏洞扫描器解析:本地拒绝服务检测详解

    阿里聚安全Android应用漏洞扫描器解析:本地拒绝服务检测详解 阿里聚安全的Android应用漏洞扫描器有一个检测项是本地拒绝服务漏洞的检测,采用的是静态分析加动态模糊测试的方法来检测,检测结果准确 ...

  7. Android基础入门教程——2.5.3 AlertDialog(对话框)详解

    Android基础入门教程--2.5.3 AlertDialog(对话框)详解 标签(空格分隔): Android基础入门教程 本节引言: 本节继续给大家带来是显示提示信息的第三个控件AlertDia ...

  8. Android绘图Canvas十八般武器之Shader详解及实战篇(上)

    本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布 前言 Android中绘图离不开的就是Canvas了,Canvas是一个庞大的知识体系,有java层的,也有jni层深入到Frame ...

  9. Android异步处理三:Handler+Looper+MessageQueue深入详解

    本博文地址:http://blog.csdn.net/mylzc/article/details/6771331 转载请注明出处 Android异步处理系列文章索引 Android异步处理一:使用Th ...

最新文章

  1. iphone XCode调试技巧之EXC_BAD_ACCESS中BUG解决
  2. UVa 11107 (后缀数组 二分) Life Forms
  3. 设计模式的理解:解释器模式 Interpreter
  4. Handle/Body pattern(Wrapper pattern)
  5. 闪存必须解决的三大问题
  6. 360浏览器怎么关闭全屏看大图提示条
  7. sql1032n sql6048n db2start启动不了 db2修改hostname
  8. ROS笔记(39) 串口配置
  9. linux_basics
  10. Win7 64位中文旗舰版上Cocos2d-x 3.0的Android开发调试环境架设
  11. 传智播客java学习内容
  12. python入门之后须掌握的知识点(excel文件处理+邮件发送+实战:批量化发工资条)【二】
  13. SSH config 配置
  14. golang中的strings.ToLower
  15. easypoi的excel导出单元格只能选下拉选项工具类
  16. 用Html+js自己写了一个小游戏
  17. 在没有源码的情况下修改.class文件
  18. vue项目如何打包部署到后端
  19. 计算机音乐作曲排名2019,2019全球歌曲排行榜_数据 2019全球古典音乐排行榜
  20. iOS 【如何去除 UILabel 边缘异常黑线/阴影】

热门文章

  1. html3d变形,深入理解CSS变形transform(3d) - 小火柴的蓝色理想
  2. Linux中如何调整pe的大小,关于LVM PE大小影响VG容量
  3. Python:random库使用方法
  4. Qt 中static_cast 和 reinterpret_cast的区别
  5. android 打开闪光灯,Android 闪光灯的打开和关闭
  6. 升级插件_全新界面全面升级不能错过 摄影后期插件 CameraRaw12.3 最新版
  7. Python中str.format()字典及list传入详解
  8. HTML CSS JS 特殊字符(转义)表
  9. 禁止root用户远程登录或者禁止其他用户登录而不删除它
  10. 携程是如何借助“预测式外呼”提高呼叫效率的