English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Este artículo proporciona un ejemplo de la programación de diseño del patrón de estado en Android. Lo compartimos con todos para su referencia, como se detalla a continuación:
Introducción
El comportamiento en el patrón de estado está determinado por el estado, y diferentes estados tienen diferentes comportamientos. El patrón de estado y el patrón de estrategia tienen estructuras casi idénticas, pero sus objetivos y esencia son completamente diferentes. El comportamiento del patrón de estado es paralelo e irremplazable, mientras que el comportamiento del patrón de estrategia es independiente y puede ser intercambiado entre sí. En una frase, el patrón de estado encapsula el comportamiento del objeto en diferentes objetos de estado, y cada objeto de estado tiene una clase base abstracta común. La intención del patrón de estado es que un objeto cambie su comportamiento cuando cambie su estado interno.
Dos, definición
Permite que un objeto cambie su comportamiento cuando cambia su estado interno, haciendo que el objeto parezca que ha cambiado su clase.
Tres, escenarios de uso
(1)El comportamiento de un objeto depende de su estado y debe cambiar su comportamiento en tiempo de ejecución según el estado.
(2)El código contiene muchas condiciones relacionadas con el estado del objeto, por ejemplo, una operación contiene una gran cantidad de declaraciones de ramificación (if-else o switch-(case), y estas ramificaciones dependen del estado del objeto.
El patrón de estado coloca cada condición de ramificación en una clase independiente, lo que le permite usar el estado del objeto como un objeto, este objeto puede cambiar independientemente sin depender de otros objetos, de esta manera, mediante polimorfismo, se eliminan muchos if repetitivos.-else y otras declaraciones de ramificación.
Cuatro, diagrama de clases UML del patrón de estado
Diagrama de clases UML:
Introducción de personajes:
Contexto: clase de entorno, define las interfaces de interés del cliente, mantiene una instancia de la subclase State, esta instancia define el estado actual del objeto.
State: clase de estado abstracta o interfaz de estado, define un o varios interfaces que representan el comportamiento en ese estado.
ConcreteStateA, ConcreteStateB: clases de estado específicas, cada clase de estado específica implementa el interfaz definido en el estado abstracto, logrando diferentes comportamientos en diferentes estados.
Cinco, ejemplo simple
A continuación, demos un ejemplo práctico con un mando a distancia de televisión para mostrar la implementación del patrón de estado. Primero, dividimos el estado del televisor en dos: estado encendido y estado apagado. En el estado encendido, se pueden realizar operaciones como cambiar de canal y ajustar el volumen con el mando a distancia, pero presionar el botón de encendido repetidamente no tiene efecto; mientras que en el estado apagado, las operaciones como cambiar de canal, ajustar el volumen y apagar no tienen efecto, solo presionar el botón de encendido tiene efecto. Es decir, el estado interno del televisor determina el comportamiento del mando a distancia.
Primero, presentamos el método de implementación común:
public class TVController { //Estado encendido private final static int POWER_ON = 1; //Estado apagado private final static int POWER_OFF = 2; //Estado por defecto private int mState = POWER_OFF; public void powerOn(){ if(mState == POWER_OFF){ System.out.println("El televisor se ha encendido"); } mState = POWER_ON; } public void powerOff(){ if(mState ==POWER_ON){ System.out.println("la televisión se apagó"); } mState = POWER_OFF; } public void nextChannel(){ if(mState ==POWER_ON){ System.out.println("canal siguiente"); } System.out.println("no ha encendido"); } } public void prevChannel(){ if(mState ==POWER_ON){ System.out.println("canal anterior"); } System.out.println("no ha encendido"); } } public void turnUp(){ if(mState ==POWER_ON){ System.out.println("aumentar volumen"); } System.out.println("no ha encendido"); } } public void turnDown(){ if(mState ==POWER_ON){ System.out.println("disminuir volumen"); } System.out.println("no ha encendido"); } } }
se puede ver que cada vez que se ejecuta, se realiza la operación mediante la juicio del estado actual, se repiten parte del código, suponiendo que el estado y las funciones aumentan, se vuelve cada vez más difícil de mantener. En este caso, se puede usar el patrón de estado, como se muestra a continuación:
interfaz de estado de televisión:
/** * interfaz de estado de televisión, define las funciones de operación de la televisión * **/ public interface TVState { public void nextChannel(); public void prevChannel(); public void turnUp(); public void turnDown(); }
estado de apagado:
/** * * estado de apagado, las operaciones no tienen resultados * * */ public class PowerOffState implements TVState{ @Override public void nextChannel() { } @Override public void prevChannel() { } @Override public void turnUp() { } @Override public void turnDown() { } }
estado de encendido:
/** * * estado de encendido, las operaciones son válidas * * */ public class PowerOnState implements TVState{ @Override public void nextChannel() { System.out.println("canal siguiente"); } @Override public void prevChannel() { System.out.println("canal anterior"); } @Override public void turnUp() { System.out.println("aumentar volumen"); } @Override public void turnDown() { System.out.println("disminuir volumen"); } }
interfaz de operación de energía:
/** * interfaz de operación de energía * * */ public interface PowerController { public void powerOn(); public void powerOff(); }
controlador de televisión:
/** * controlador de televisión * * */ 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("Encendido"); } @Override public void powerOff() { setTVState(new PowerOffState()); System.out.println("Apagado"); } public void nextChannel(){ mTVState.nextChannel(); } public void prevChannel(){ mTVState.prevChannel(); } public void turnUp(){ mTVState.turnUp(); } public void turnDown(){ mTVState.turnDown(); } }
Llamada:
public class Client { public static void main(String[] args) { TVController tvController = new TVController(); //Configurar estado de encendido tvController.powerOn(); //Siguiente canal tvController.nextChannel(); //Aumentar volumen tvController.turnUp(); //Apagar tvController.powerOff(); //Reducir volumen, esto no tendrá efecto en este momento tvController.turnDown(); } }
El resultado de la salida es el siguiente:
Encendido Siguiente canal Aumentar volumen Apagado
En la implementación anterior, abstractamos una interfaz TVState, que contiene todas las funciones para operar el televisor. Esta interfaz tiene dos clases de implementación, es decir, el estado de encendido (PowerOnState) y el estado de apagado (PowerOffState). En el estado de encendido, la función de encendido no es válida, lo que significa que cuando el televisor ya está encendido, presionar el botón de encendido no producirá ninguna reacción; mientras que en el estado de apagado, solo la función de encendido es disponible, otras funciones no tienen efecto. La misma operación, como la función de aumentar el volumen turnUp, es inválida en el estado de apagado, y en el estado de encendido aumentará el volumen del televisor, lo que significa que el estado interno del televisor afecta el comportamiento del mando a distancia del televisor. El patrón de estado encapsula estos comportamientos en las clases de estado, y durante las operaciones, transfiere estas funciones a los objetos de estado. Diferentes estados tienen diferentes implementaciones, de esta manera, se eliminan la repetición y la confusión mediante la forma de polimorfismo.-else, que es la esencia del patrón de estado.
Seis, uso en la práctica de Android
1、Sistema de inicio de sesión, según si el usuario está logueado o no, se determina la forma de manejo de eventos.
2、Wi-Gestión de Fi, el procesamiento de solicitudes de escaneo de WiFi es diferente en diferentes estados.
A continuación, se explica el uso del patrón de estado en la práctica con el ejemplo de inicio de sesión:
En el desarrollo de Android, es muy común encontrar la interfaz de inicio de sesión, y el diseño de patrones de estado se aplica muy ampliamente en la interfaz de inicio de sesión. Las operaciones lógicas del usuario en el estado de inicio de sesión y no inicio de sesión son diferentes. Por ejemplo, la situación más común es que cuando se juega con el Weibo de Sina, el usuario solo puede completar las operaciones de comentario y reenvío de Weibo cuando está logueado; mientras que cuando el usuario está no logueado y necesita realizar operaciones de reenvío y comentario de Weibo, debe ingresar a la interfaz de inicio de sesión para realizar el inicio de sesión antes de poder realizar estas operaciones. Por lo tanto, para estos dos estados diferentes, es mejor diseñar este ejemplo utilizando el diseño de patrones de estado.
1、Clase base de estado
Como hemos dicho anteriormente, el principio del diseño de patrones de estado es la polimorfismo, aquí usamos el interfaz UserState para representar esta clase base, que incluye las operaciones de reenvío y comentario, el código es el siguiente:
public interface UserState { /** * Operación de reenvío * @param context */ public void forword(Context context); /** * Operación de comentario * @param context */ public void commit(Context context); }
2、Las implementaciones de LoginState y LogoutState para las condiciones de inicio de sesión y no inicio de sesión del usuario; el código es el siguiente:
En LoginState.java, el usuario puede realizar operaciones de reenvío y comentario.
public class LoginState implements UserState{ @Override public void forword(Context context) { Toast.makeText(context, "Reenvío exitoso", Toast.LENGTH_SHORT).show(); } @Override public void commit(Context context) { Toast.makeText(context, "Comentario exitoso", Toast.LENGTH_SHORT).show(); } }
En LogoutState.java, el usuario no está autorizado a realizar operaciones sin estar logueado, sino que debe saltar a la interfaz de inicio de sesión para realizar el inicio de sesión antes de poder realizar operaciones.
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.forward(context); } //comentario public void commit(Context context){ state.commit(context); } }
4、visualización de la interfaz
LoginActivity.java, esta interfaz realiza la operación de inicio de sesión, después de iniciar sesión correctamente se establece LoginContext.getInstance().setState(new LoginState()); como estado de inicio de sesión, en MainActivity se ejecutan las operaciones bajo el estado de inicio de sesión, es decir, se puede retransmitir y comentar;
public class LoginActivity extiende Activity implements OnClickListener{ private static final String URL_INICIO SESIÓN = "http://10.10.200.193:8080/Day01/servlet/LoginServlet"; private EditText et_nombreDeUsuario; private EditText et_contraseña; private Button btn_login; private String nombreDeUsuario; private String contraseña; private KJHttp http; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_login); initView(); inicializarDatos(); } private void initView() { et_nombreDeUsuario = (EditText) findViewById(R.id.et_nombreDeUsuario); et_contraseña = (EditText) findViewById(R.id.et_contraseña); btn_login = (Button) findViewById(R.id.btn_login); btn_login.setOnClickListener(LoginActivity.this); } private void inicializarDatos() { http = new KJHttp(); } /** * realizar la operación de inicio de sesión * * @param nombreDeUsuario2 * @param contraseña2 */ protected void enviarSesión(String nombreDeUsuario2, 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 ("2"00".equals(t)) { //Configurado como estado de inicio de sesión LoginContext.getInstance().setState(new LoginState()); startActivity(new Intent(LoginActivity.this,MainActivity.class)); finish(); Toast.makeText(LoginActivity.this, "Inicio de sesión exitoso", 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, "El nombre de usuario y la contraseña no pueden estar en blanco", Toast.LENGTH_SHORT).show(); return; } sendLogin(username, password); break; } } }
MainActivity.java, después de que el usuario inicie sesión con éxito, hacer clic en compartir y comentar ejecuta la operación en estado de inicio de sesión, mientras que cuando el usuario se desconecte, configuramos el estado de LoginContext como estado no iniciado; LoginContext.getInstance().setState(new LogoutState()); en este momento, al hacer clic en las operaciones de compartir y comentar, se saltará a la interfaz de inicio de sesión del usuario.
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() { //Operación de reenvío btn_forward.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Llamar a la función de reenvío dentro de LoginContext LoginContext.getInstance().forward(MainActivity.this); } }); //Operación de comentario btn_commit.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Llamar a la función de reenvío dentro de LoginContext LoginContext.getInstance().commit(MainActivity.this); } }); //Operación de salida btn_logout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Establecer el estado de desregistrado LoginContext.getInstance().setState(new LogoutState()); } }); } }
Séptima sección: Resumen
El punto clave del patrón de estado está en que diferentes estados tienen diferentes respuestas a la misma acción, que es esencialmente una conversión de if-Un ejemplo específico de implementación usando polimorfismo en lugar de else.-else o switch-Bajo la forma de case, se realiza una determinación según diferentes estados, si es el estado A, se ejecuta el método A, si es el estado B, se ejecuta el método B, pero esta implementación hace que la lógica se耦合 juntos, es fácil de cometer errores, y el patrón de estado puede eliminar muy bien este tipo de lógica 'fea' de procesamiento, por supuesto, no es cualquier aparición de if-En todas las partes donde se usa else, deben ser reconstruidas mediante el patrón de estado, la aplicación del patrón debe considerar la situación en la que se encuentra y el problema que debe resolver, solo se recomienda usar el patrón correspondiente si es adecuado para la situación específica.
Ventajas:
Colocar todos los comportamientos relacionados con un estado específico en un objeto de estado específico, lo que proporciona un mejor método para organizar el código relacionado con un estado específico, convirtiendo la tediosa determinación de estado en una familia de clases de estado estructuradas, evitando la expansión del código al mismo tiempo que garantiza la expansibilidad y mantenibilidad.
Desventajas:
El uso del patrón de estado inevitablemente aumentará el número de clases y objetos del sistema.
Los lectores interesados en más contenido relacionado con Android pueden consultar las secciones especiales de este sitio: 'Introducción y Tutorial de Desarrollo de Android', 'Técnicas de depuración y resumen de métodos de solución de problemas comunes de Android', 'Resumen de uso de componentes básicos de Android', 'Resumen de técnicas de vista View de Android', 'Resumen de técnicas de diseño de layout de Android' y 'Resumen de uso de controles de Android'.
Espero que lo descrito en este artículo sea útil para la programación de aplicaciones Android.
Declaración: El contenido de este artículo se obtiene de la red, es propiedad del autor original, el contenido se contribuye y sube por los usuarios de Internet, este sitio no posee los derechos de propiedad, no se ha procesado editorialmente por humanos y no asume responsabilidades legales relacionadas. Si encuentra contenido sospechoso de copyright, por favor envíe un correo electrónico a: notice#oldtoolbag.com (al enviar un correo electrónico, reemplace # con @) para denunciar y proporcionar evidencia relevante. Una vez verificada, este sitio eliminará inmediatamente el contenido sospechoso de infracción.