English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Questo articolo illustra un esempio di progettazione di modello di stato in Android. Lo condivido con tutti per riferimento, come segue:
Introduzione
Il comportamento nello stato del modello di stato è determinato dallo stato, e diversi stati hanno comportamenti diversi. La struttura del modello di stato e del modello di strategia è quasi identica, ma i loro scopi e natura sono completamente diversi. Il comportamento del modello di stato è parallelo e non sostituibile, mentre il comportamento del modello di strategia è indipendente e può essere sostituito reciprocamente. In una frase, il modello di stato impacchetta il comportamento dell'oggetto in diversi oggetti di stato, ognuno dei quali ha una comune classe base astratta di stato. L'intento del modello di stato è di far cambiare il comportamento di un oggetto quando cambia il suo stato interno.
II. Definizione
Quando lo stato interno di un oggetto cambia, è possibile modificare il suo comportamento, e l'oggetto sembra cambiare la sua classe.
III. Scenari di utilizzo
(1) Il comportamento di un oggetto dipende dal suo stato e deve cambiare il suo comportamento in tempo di esecuzione in base allo stato.
(2) Il codice contiene molte istruzioni condizionali relative allo stato dell'oggetto, ad esempio, un'operazione contiene una vasta serie di istruzioni di branca (if-else o switch-case) che dipendono dallo stato dell'oggetto.
Il pattern di stato mette ogni branca condizionale in una classe indipendente, il che ti permette di considerare lo stato dell'oggetto come un oggetto, che può cambiare indipendentemente dagli altri oggetti, senza dipendere da essi, e così attraverso la polimorfismo eliminare troppi, ripetuti if-else e altre istruzioni di branca.
IV. Diagramma UML del pattern di stato
Diagramma UML:
Presentazione dei ruoli:
Context: classe ambiente, definisce l'interfaccia interessante per il cliente, mantiene un'istanza della sottoclasse State, questa istanza definisce lo stato attuale dell'oggetto.
State: classe di stato astratta o interfaccia di stato, definisce un'interfaccia o un gruppo di interfacce, che rappresenta il comportamento in questo stato.
ConcreteStateA, ConcreteStateB: classi di stato concrete, ogni classe di stato concreto implementa l'interfaccia definita dall'abstract State, raggiungendo comportamenti diversi in diversi stati.
V. Esempio semplice
Di seguito, esamineremo un esempio pratico utilizzando un telecomando per la televisione per illustrare l'implementazione del pattern di stato. Iniziamo dividendo lo stato della televisione in stato acceso e stato spento. Quando la televisione è in stato acceso, è possibile utilizzare il telecomando per cambiare canali e regolare il volume, ma premere nuovamente il pulsante di accensione non avrà alcun effetto; mentre nello stato spento, cambiare canali, regolare il volume e spegnere la televisione non avranno alcun effetto, funzionerà solo premendo il pulsante di accensione. Questo significa che lo stato interno della televisione determina il comportamento del telecomando.
Prima di tutto, vediamo un esempio di implementazione comune:
public class TVController { //Stato acceso private final static int POWER_ON = 1; //Stato spento private final static int POWER_OFF = 2; //Stato predefinito private int mState = POWER_OFF; public void powerOn(){ if(mState == POWER_OFF){ System.out.println("La televisione è stata accesa"); } mState = POWER_OFF; } public void powerOff(){ if (mState == POWER_ON) { System.out.println("La televisione è stata spenta"); } 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("Accensione"); } @Override public void powerOff() { setTVState(new PowerOffState()); System.out.println("Spegnimento"); } public void nextChannel(){ mTVState.nextChannel(); } public void prevChannel(){ mTVState.prevChannel(); } public void turnUp(){ mTVState.turnUp(); } public void turnDown(){ mTVState.turnDown(); } }
Chiamata:
public class Client { public static void main(String[] args) { TVController tvController = new TVController(); //Impostazione dello stato di accensione tvController.powerOn(); //Prossimo canale tvController.nextChannel(); //Aumento del volume tvController.turnUp(); //Spegnimento tvController.powerOff(); //Aumento del volume, non efficace in questo momento tvController.turnDown(); } }
Il risultato dell'output è il seguente:
Accensione Prossimo canale Aumento del volume Spegnimento
Nell'implementazione menzionata, abbiamo astratto un'interfaccia TVState, che contiene tutte le funzioni per operare la TV. Questa interfaccia ha due classi implementanti, ovvero lo stato di accensione (PowerOnState) e lo stato di spegnimento (PowerOffState). Quando la TV è in stato di accensione, la funzione di accensione non è valida, il che significa che premere il pulsante di accensione non ha alcun effetto; mentre nello stato di spegnimento, solo la funzione di accensione è disponibile, altre funzioni non funzionano. La stessa operazione, come la funzione di aumento del volume turnUp, non è valida nello stato di spegnimento, mentre nello stato di accensione aumenterà il volume della TV, il che significa che lo stato interno della TV influisce sul comportamento del telecomando della TV. Lo stato modello impacchetta questi comportamenti nelle classi di stato, trasmettendo queste funzioni agli oggetti di stato durante l'operazione. Ogni stato ha un'implementazione diversa, così attraverso la forma di polimorfismo si eliminano le if-else ripetitive e disordinate, che è proprio l'essenza dello stato modello.
Sezione 6: Uso nella pratica di Android
1、Sistema di login, il modo di gestire gli eventi è diverso a seconda che l'utente sia loggato o meno.
2、Gestione Wi-Fi, la gestione delle richieste di scansione Wi-Fi è diversa in diversi stati.
Di seguito, con l'esempio di sistema di login, spieghiamo l'uso del pattern di stato nella pratica:
Nello sviluppo Android, incontriamo spesso la pagina di login, e l'applicazione del design pattern di stato nella pagina di login è molto diffusa. Le operazioni logiche dell'utente sono diverse in stato di login e non login. Ad esempio, la situazione più comune è quando si gioca a Weibo, l'utente può completare le operazioni di commento e condivisione del Weibo solo quando è loggato; mentre quando l'utente è non loggato e deve eseguire le operazioni di condivisione e commento del Weibo, deve accedere alla pagina di login per eseguire il login prima di poter eseguire. Pertanto, di fronte a queste due situazioni diverse, è meglio utilizzare il design pattern di stato per progettare questo esempio.
1、Classe base di stato
Abbiamo parlato in precedenza del principio del design pattern di stato, che in realtà è polymorphism. In questo caso, usiamo l'interfaccia UserState per rappresentare questa classe base, che include le operazioni di condivisione e commento, il codice è il seguente:
public interface UserState { /** * Operazione di condivisione * @param context */ public void forword(Context context); /** * Operazione di commento * @param context */ public void commit(Context context); }
2、Le implementazioni delle classi LoginState e LogoutState per le due condizioni di login e non login; il codice è il seguente:
Nel file LoginState.java, l'utente può eseguire operazioni di condivisione e commento.
public class LoginState implements UserState{ @Override public void forword(Context context) { Toast.makeText(context, "Condivisione riuscita", Toast.LENGTH_SHORT).show(); } @Override public void commit(Context context) { Toast.makeText(context, "Commento riuscito", Toast.LENGTH_SHORT).show(); } }
Nel file LogoutState.java, l'utente non è autorizzato a eseguire operazioni senza aver effettuato il login, ma deve essere reindirizzato alla pagina di login per eseguire il login prima di poter eseguire.
public class LogoutState implements UserState{ /** * Passa alla schermata di login per poter condividere */ @Override public void forword(Context context) { gotoLohinActivity(context); } /** * Passa alla schermata di login per poter commentare */ @Override public void commit(Context context) { gotoLohinActivity(context); } /** * Operazione di transizione di interfaccia * @param context */ private void gotoLohinActivity(Context context){ context.startActivity(new Intent(context, LoginActivity.class)); } }
3、Operatore di ruolo LoginContext
Questo LoginContext rappresenta il ruolo di Context nello stato del modello, è l'oggetto di operazione dell'utente e l'oggetto di gestione, il LoginContext delega le operazioni correlate agli oggetti di stato, e quando lo stato cambia, anche il comportamento del LoginContext cambia. Il codice di LoginContext è come segue:*
Suggerimento:
qui utilizziamo il singleton per garantire che ci sia solo un LoginContext che controlla lo stato dell'utente;
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); } //Commento public void commit(Context context){ state.commit(context); } }
4, Visualizzazione dell'interfaccia
LoginActivity.java, questa interfaccia esegue l'operazione di login, dopo il login imposta LoginContext.getInstance().setState(new LoginState()); come stato di login, nella MainActivity viene eseguita l'operazione dello stato di login, ossia è possibile inoltrare e commentare;
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(); } /** * Esegui l'operazione di login * * @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)) { // Impostato come stato di login LoginContext.getInstance().setState(new LoginState()); startActivity(new Intent(LoginActivity.this,MainActivity.class)); finish(); Toast.makeText(LoginActivity.this, "Login successo", 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, "Il nome utente e la password non possono essere vuoti", Toast.LENGTH_SHORT).show(); return; } sendLogin(username, password); break; } } }
MainActivity.java, dopo che l'utente si è loggato con successo, fare clic su Condividi e Commenta esegue l'operazione nello stato di login; quando l'utente si disconnette, impostiamo lo stato di LoginContext su stato non connesso; LoginContext.getInstance().setState(new LogoutState()); in questo momento, fare clic su Condividi e Commenta verrà reindirizzato alla pagina di login dell'utente.
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() { // operazione di inoltro btn_forward.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // chiamare la funzione di inoltro nel LoginContext LoginContext.getInstance().forward(MainActivity.this); } }); // operazione di commento btn_commit.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // chiamare la funzione di inoltro nel LoginContext LoginContext.getInstance().commit(MainActivity.this); } }); // operazione di注销 btn_logout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // impostare lo stato di注销 LoginContext.getInstance().setState(new LogoutState()); } }); } }
Settimo, sommario
Il punto chiave dello stato模式 sta nel fatto che la stessa azione ha risposte diverse in diversi stati, che è in realtà un esempio concreto di utilizzare il polimorfismo per implementare if-else. Sotto la forma di if-else o switch-case, la decisione viene effettuata in base allo stato diverso: se lo stato è A, esegui il metodo A, se lo stato è B, esegui il metodo B. Questa implementazione fa sì che la logica sia coinvolta insieme, facile da sbagliare, e attraverso lo stato模式 si può eliminare efficacemente questo tipo di elaborazione logica 'brutta', naturalmente non è necessario che ogni luogo in cui si incontra if-else venga ricostruito attraverso lo stato模式, l'applicazione del modello deve sempre considerare la situazione in cui si trova e il problema che si deve risolvere, e solo quando si adatta alla scena specifica si consiglia di utilizzare il modello corrispondente.
Vantaggio:
Mettere tutte le azioni correlate a uno stato specifico in un oggetto di stato, che fornisce un metodo migliore per organizzare il codice correlato allo stato specifico, trasformando la decisione complessa dello stato in una famiglia di classi di stato ben strutturata, evitando l'espansione del codice mentre si garantisce l'estensibilità e la manutenibilità.
Svantaggio:
L'uso dello stato模式必然会增加系统和对象的个数。
Chi è interessato a ulteriori contenuti relativi a Android può consultare la sezione speciale di questo sito: 'Guida di avvio e avanzamento di Android', 'Tecniche di debug e soluzioni di problemi comuni di Android', 'Sommaria delle usi dei componenti di base di Android', 'Sommaria delle tecniche di View di Android', 'Sommaria delle tecniche di layout di Android' e 'Sommaria delle tecniche di controllo di Android'.
Spero che l'articolo di questo testo possa essere utile per la progettazione di programmi Android.
Dichiarazione: il contenuto di questo articolo è stato tratto da Internet, il diritto d'autore è della proprietà del rispettivo proprietario, il contenuto è stato contribuito e caricato volontariamente dagli utenti di Internet, questo sito non detiene i diritti di proprietà, non è stato editato manualmente e non assume responsabilità legali correlate. Se trovi contenuti sospetti di violazione del copyright, invia un'e-mail a: notice#oldtoolbag.com (al momento dell'invio dell'e-mail, sostituisci # con @) per segnalare e fornire prove pertinenti. Una volta verificata, questo sito rimuoverà immediatamente il contenuto sospetto di violazione del copyright.