English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Dieser Artikel beschreibt ein Beispiel für das Zustandsmodell in der Android-Programmierung und -Design. Hiermit möchte ich etwas teilen, das alle zur Verfügung stehen, wie folgt:
Einleitung
Das Verhalten im Zustandsmodell wird durch den Zustand bestimmt, und unterschiedliche Zustände haben unterschiedliche Verhaltensweisen. Das Zustandsmodell und das Strategiemodell haben fast gleiche Strukturen, aber ihre Ziele und Wesen sind völlig unterschiedlich. Das Verhalten im Zustandsmodell ist parallel und nicht austauschbar, während das Verhalten im Strategiemodell unabhängig voneinander ist und miteinander austauschbar. Mit einem Satz ausgedrückt, packt das Zustandsmodell das Verhalten der Objekte in verschiedene Zustandsobjekte, und jedes Zustandsobjekt hat eine gemeinsame abstrakte Zustandsbasisklasse. Das Ziel des Zustandsmodells ist es, dass das Verhalten eines Objekts sich ändert, wenn sich sein internes Zustand ändert.
Zwei, Definition
Wenn der innere Zustand eines Objekts sich ändert, kann sein Verhalten geändert werden, so dass das Objekt so aussieht, als hätte es seine Klasse geändert.
Drei, Anwendungsszenario
(1)Das Verhalten eines Objekts hängt von seinem Zustand ab und muss im Laufzeit verhalten ändern, basierend auf dem Zustand.
(2)-Code enthält viele bedingte Anweisungen im Zusammenhang mit dem Zustand des Objekts, z.B. eine Operation enthält eine massive Zweigstruktur (if-else oder switch-(falls), und diese Zweige hängen vom Zustand des Objekts ab.
Das Zustandsmuster platziert jeden bedingten Zweig in einer unabhängigen Klasse, so dass Sie den Zustand des Objekts als ein Objekt verwenden können, das unabhängig von anderen Objekten unabhängig von anderen Objekten ändern kann, so dass durch Polymorphismus zu viele und wiederholte if-Abfragen entfernt werden können-else und andere Zweigstatements.
Vier, UML-Klassendiagramm des Zustandsmodells
UML-Klassendiagramm:
Rollenbeschreibung:
Context: Umgebungsklasse, definiert das für den Kunden interessante Interface, hält eine Instanz der Subklasse State, diese Instanz definiert den aktuellen Zustand des Objekts.
State: abstrakte Zustandsklasse oder Zustandsinterface, definiert ein oder mehrere Interfaces, die das Verhalten in diesem Zustand darstellen.
ConcreteStateA, ConcreteStateB: konkrete Zustandsklasse, jede konkrete Zustandsklasse implementiert das Interface, das in der abstrakten Klasse State definiert ist, um unterschiedliche Verhalten in verschiedenen Zuständen zu erreichen.
Vier, einfaches Beispiel
Lassen Sie uns die Implementierung des Zustandsmodells anhand eines Fernseher-Fernbedienungsbeispiels demonstrieren. Zunächst teilen wir den Zustand des Fernsehers einfach in Einschaltzustand und Ausschaltzustand ein. Im Einschaltzustand können Sie mit der Fernbedienung Kanäle wechseln und die Lautstärke anpassen, aber das mehrfache Drücken des Einschalttasters ist无效的;Im Ausschaltzustand sind Kanalumschaltung, Lautstärkeneinstellung und Ausschalten alle无效的操作,nur das Drücken des Einschalttasters ist wirksam. Das bedeutet, dass der interne Zustand des Fernsehers das Verhalten des Fernbedienungstasters bestimmt.
Zunächst ein einfaches Implementierungsbeispiel:
public class TVController { //Einschaltzustand private final static int POWER_ON = 1; //Ausschaltzustand private final static int POWER_OFF = 2; //Standardzustand private int mState = POWER_OFF; public void powerOn(){ if(mState == POWER_OFF){ System.out.println("Der Fernseher wurde eingeschaltet"); mState = POWER_ON; public void powerOff(){ if(mState ==POWER_ON){ System.out.println("Der Fernseher wurde ausgeschaltet"); mState = POWER_OFF; public void nextChannel(){ if(mState ==POWER_ON){ System.out.println("Nächster Kanal"); }else{ System.out.println("Kein Einschalten"); public void prevChannel(){ if(mState ==POWER_ON){ System.out.println("Vorheriger Kanal"); }else{ System.out.println("Kein Einschalten"); public void turnUp(){ if(mState ==POWER_ON){ System.out.println("Lautstärke erhöhen"); }else{ System.out.println("Kein Einschalten"); public void turnDown(){ if(mState ==POWER_ON){ System.out.println("Lautstärke verringern"); }else{ System.out.println("Kein Einschalten");
Man kann sehen, dass jeder Ausführung durch die Überprüfung des aktuellen Zustands vorgenommen wird, einige Teile des Codes sind wiederholend. Wenn der Zustand und die Funktionen zunehmen, wird es immer schwieriger zu warten. In diesem Fall kann der Zustandsstil verwendet werden, wie folgt:
Fernsehzustandsinterface:
/** * Fernsehzustandsinterface, definiert die Fernseheroperationen * **/ public interface TVState { public void nextChannel(); public void prevChannel(); public void turnUp(); public void turnDown();
Ausschaltzustand:
/** * * Ausschaltzustand, Operationen haben kein Ergebnis * * */ public class PowerOffState implements TVState{ @Override public void nextChannel() { @Override public void prevChannel() { @Override public void turnUp() { @Override public void turnDown() {
Einschaltzustand:
/** * * Einschaltzustand, Operationen sind gültig * * */ public class PowerOnState implements TVState{ @Override public void nextChannel() { System.out.println("Nächster Kanal"); @Override public void prevChannel() { System.out.println("Vorheriger Kanal"); @Override public void turnUp() { System.out.println("Lautstärke erhöhen"); @Override public void turnDown() { System.out.println("Lautstärke verringern");
Strombetriebsschnittstelle:
/** * Strombetriebsschnittstelle * * */ public interface PowerController { public void powerOn(); public void powerOff();
Fernseherfernbedienung:
/** * Fernseherfernbedienung * * */ 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("Einschalten"); @Override public void powerOff() { setTVState(new PowerOffState()); System.out.println("Ausschalten"); public void nextChannel(){ mTVState.nextChannel(); public void prevChannel(){ mTVState.prevChannel(); public void turnUp(){ mTVState.turnUp(); public void turnDown(){ mTVState.turnDown();
Aufruf:
public class Client { public static void main(String[] args) { TVController tvController = new TVController(); //Einschaltzustand einstellen tvController.powerOn(); //Nächster Kanal tvController.nextChannel(); //Lautstärke erhöhen tvController.turnUp(); //Ausschalten tvController.powerOff(); //Lautstärke verringern, dies wird nicht wirksam sein tvController.turnDown();
Das Ergebnis der Ausgabe ist wie folgt:
Einschalten Nächster Kanal Lautstärke erhöhen Ausschalten
In der beschriebenen Implementierung haben wir einen TVState-Interface abstrahiert, das alle Funktionen zum Betrieb des Fernsehers enthält. Dieses Interface hat zwei Implementierungsklassen, nämlich den Zustand des Einschaltens (PowerOnState) und den Zustand des Ausschaltens (PowerOffState). Im Zustand des Einschaltens ist nur die Funktion des Einschaltens无效, das bedeutet, dass wenn der Benutzer den Einschaltknopf gedrückt hat, wird keine Reaktion erzeugt; und im Zustand des Ausschaltens ist nur die Funktion des Einschaltens verfügbar, andere Funktionen funktionieren nicht. Die gleiche Operation, wie die Funktion zur Erhöhung der Lautstärke turnUp, ist im Zustand des Ausschaltens ineffektiv, im Zustand des Einschaltens erhöht sie jedoch die Lautstärke des Fernsehers, das bedeutet, dass der interne Zustand des Fernsehers das Verhalten des Fernsehrücksenders beeinflusst. Der Zustandsmodus packt diese Verhaltensweisen in Zustandsklassen ein und leitet diese Funktionen bei der Operation an den Zustandsobjekt weiter. Differentielle Zustände haben unterschiedliche Implementierungen, so dass durch Polymorphie Duplikate und Unordnung beseitigt werden.-else-Anweisung, das ist das Wesentliche des State-Patterns.
Sechster Teil: Anwendung im Android-Praxisbeispiel
1、Das Login-System, das die Verarbeitung der Ereignisse basierend auf dem Anmeldestatus des Benutzers bestimmt.
2、Wi-Fi-Verwaltung, die Verarbeitung der WiFi-Scan-Anfragen variiert in verschiedenen Zuständen.
Nachfolgend wird der Einsatz des State-Patterns in der Praxis an einem Beispiel des Login-Systems erläutert:
Bei der Android-Entwicklung ist es sehr häufig, auf die Login-Oberfläche zu stoßen, und das State-Design-Pattern wird in der Login-Oberfläche breit eingesetzt. Die Benutzerlogik ist unterschiedlich, wenn der Benutzer in einem angemeldeten Zustand ist oder nicht angemeldet ist. Zum Beispiel, wenn man sich in der Weibo-App befindet, kann der Benutzer nur Kommentare und Weiterleitungen zu Weibos hinterlassen, wenn er angemeldet ist; wenn der Benutzer in einem nicht angemeldeten Zustand ist und Kommentare oder Weiterleitungen zu Weibos ausführen möchte, muss er die Login-Oberfläche betreten und sich anmelden, bevor er Aktionen ausführen kann. Daher ist es am besten, diesen Fall mit dem State-Design-Pattern zu entwerfen, um diese beiden verschiedenen Zustände zu behandeln.
1、Zustandsbasisklasse
Wir haben bereits über die Prinzipien des State-Design-Patterns gesprochen, das im Wesentlichen Polymorphismus ist. Hier verwenden wir das UserState-Interface als Basisklasse, einschließlich der Zustände Weiterleitung und Kommentar, wie folgt:
public interface UserState { /** * Weiterleitungsoperation * @param context */ public void forword(Context context); /** * Kommentaroperation * @param context */ public void commit(Context context);
2、Es gibt zwei Implementierungsklassen für die beiden Zustände Login und Nicht-Login, nämlich LoginState und LogoutState; der Code ist wie folgt:
Im LoginState.java kann der Benutzer Weiterleitungs- und Kommentarfunktionen ausführen.
public class LoginState implements UserState{ @Override public void forword(Context context) { Toast.makeText(context, "Weiterleitung erfolgreich", Toast.LENGTH_SHORT).show(); @Override public void commit(Context context) { Toast.makeText(context, "Kommentarerfolg", Toast.LENGTH_SHORT).show();
Im LogoutState.java wird es dem Benutzer in einem nicht angemeldeten Zustand untersagt, Aktionen auszuführen, stattdessen sollte auf die Login-Oberfläche umgeschaltet werden, um sich nach der Anmeldung anmelden zu können, bevor Aktionen ausgeführt werden können.
public class LogoutState implements UserState{ /** * Springen Sie auf die Login-Oberfläche, um zu forwarden, nachdem Sie sich angemeldet haben */ @Override public void forword(Context context) { gotoLohinActivity(context); /** * Springen Sie auf die Login-Oberfläche, um zu kommentieren, nachdem Sie sich angemeldet haben */ @Override public void commit(Context context) { gotoLohinActivity(context); /** * Seitenübergangsvorgang * @param context */ private void gotoLohinActivity(Context context){ context.startActivity(new Intent(context, LoginActivity.class));
3und der Operationsrolle LoginContext
Hierbei handelt es sich um das LoginContext, das in der State-Pattern-Context-Rolle dient, ist das Benutzeroperationsobjekt und das Verwaltungsobjekt, das LoginContext die betreffenden Operationen an die Zustandsobjekte delegiert. Wenn sich der Zustand ändert, ändert sich auch das Verhalten von LoginContext. Der Code von LoginContext ist wie folgt:*Nachstehend:
Hinweis:
Hier verwenden wir das Singleton-Modell, um sicherzustellen, dass es weltweit nur einen LoginContext gibt, der den Benutzerzustand kontrolliert;
public class LoginContext { //Der Standardzustand des Benutzers ist der Zustand 'nicht angemeldet' UserState state = new LogoutState(); private LoginContext(){};//Privater Konstruktor, um zu verhindern, dass das Objekt über new aus dem Äußeren erreicht werden kann //Singleton-Modell public static LoginContext getInstance(){ return SingletonHolder.instance; /** *Statischer Codeblock */ private static class SingletonHolder{ private static final LoginContext instance = new LoginContext(); public void setState(UserState state){ this.state = state; //Forwarden public void forward(Context context){ state.forward(context); //Kommentare public void commit(Context context){ state.commit(context);
4、Oberflächendarstellung
LoginActivity.java, diese Oberfläche führt die Anmeldeoperation aus. Nach dem Anmelden wird LoginContext.getInstance().setState(new LoginState()); auf den Anmeldestatus gesetzt. In MainActivity wird der Anmeldestatus ausgeführt, d.h. es können Nachrichten weitergeleitet und kommentiert werden;
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(); /** * Ausführung der Anmeldeoperation * * @param username2 * @param password2 */ geschützte Methode 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 ("2"00".equals(t)) { //Zustand als angemeldet setzen LoginContext.getInstance().setState(new LoginState()); startActivity(new Intent(LoginActivity.this,MainActivity.class)); finish(); Toast.makeText(LoginActivity.this, "Login erfolgreich", 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, "Benutzername und Passwort dürfen nicht leer sein", Toast.LENGTH_SHORT).show(); return; sendLogin(username, password); break;
MainActivity.java, nach dem erfolgreichen Login des Benutzers wird auf "Weiterleiten" und "Kommentieren" geklickt, um die Operationen im Anmeldestatus auszuführen. Wenn der Benutzer abmeldet, setzen wir den Zustand von LoginContext auf den Anmeldezustand; LoginContext.getInstance().setState(new LogoutState()); Wenn auf die Operationen "Weiterleiten" und "Kommentieren" geklickt wird, wird zum Benutzeranmeldebildschirm weitergeleitet.
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() { //Weiterleitungsoperation btn_forward.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Aufruf der Weiterleitungsfunction in LoginContext LoginContext.getInstance().forward(MainActivity.this); //Kommentaroperation btn_commit.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //Aufruf der Weiterleitungsfunction in LoginContext LoginContext.getInstance().commit(MainActivity.this); //Abmeldeoperation btn_logout.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //设置为注销状态 LoginContext.getInstance().setState(new LogoutState());
七、总结
状态模式的关键点在于不同的状态下对于同一行为有不同的响应,这其实就是一个将if-在if-else或switch的情况都应该使用多态来实现的一个具体示例。-case形式下根据不同的状态进行判断,如果是状态A则执行方法A,状态B执行方法B,但这种实现使得逻辑耦合在一起,易于出错,通过状态模式能够很好地消除这类“丑陋”的逻辑处理,当然并不是任何出现if-else部分都应该通过状态模式重构,模式的运用一定要考虑所处的情景以及你要解决的问题,只有符合特定的场景才建议使用对应的模式。
优点:
将所有与一个特定状态相关的行为都放入一个状态对象中,它提供了一个更好的方法来组织与特定状态相关的代码,将繁琐的状态判断转换成结构清晰的状态类族,在避免代码膨胀的同时也保证了可扩展性与可维护性。
缺点:
状态模式的使用必然会增加系统类和对象的个数。
更多关于Android相关内容的读者,可查看本站专题:《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android基本组件用法总结》、《Android视图View技巧总结》、《Android布局layout技巧总结》及《Android控件用法总结》
希望本文所述对大家的Android程序设计有所帮助。
声明:本文内容来源于互联网,归原作者所有。内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未进行人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:notice#oldtoolbag.com(在发送邮件时,请将#替换为@进行举报,并提供相关证据。一经查实,本站将立即删除涉嫌侵权内容。)