.NetCore中IdentityServer使用nginx-proxy的一次排錯經歷
阿新 • • 發佈:2022-04-06
狀態模式又稱狀態物件模式,屬於行為型模式;狀態模式允許一個物件在其內部狀態改變的時候改變其行為,這個物件看上去就像是改變了它的類一樣。狀態模式把所研究的物件的行為包裝在不同的狀態物件裡,每一個狀態物件都屬於一個抽象狀態類的子類,狀態模式的意圖是讓一個物件在其內部狀態改變的時候,其行為也隨之改變。
對這種有狀態的物件程式設計,傳統的解決方案是:將這些所有可能發生的情況全都考慮到,然後使用 if-else 或 switch-case 語句來做狀態判斷,再進行不同情況的處理。但是顯然這種做法對複雜的狀態判斷存在天然弊端,條件判斷語句會過於臃腫,可讀性差,且不具備擴充套件性,維護難度也大。且增加新的狀態時要新增新的 if-else 語句,這違背了“開閉原則”,不利於程式的擴充套件。
以上問題如果採用狀態模式就能很好地得到解決。狀態模式把相關“判斷邏輯”提取出來,用各個不同的類進行表示,系統處於哪種情況,直接使用相應的狀態類物件進行處理,這樣能把原來複雜的邏輯判斷簡單化,消除了 if-else、switch-case 等冗餘語句,程式碼更有層次性,並且具備良好的擴充套件力。
狀態模式的UML類圖如下:
如上圖所示,狀態模式涉及到抽象狀態角色、具體狀態角色、環境角色三種角色:
- 抽象狀態角色:定義一個介面,用以封裝環境物件的一個特定的狀態所對應的行為
- 具體狀態角色:每一個具體狀態類都實現了環境的一個狀態所對應的行為
- 環境角色:定義客戶端所感興趣的介面,並且保留一個具體狀態類的例項,並負責具體狀態的切換
java多執行緒狀態轉換的例子
我們都知道在java中,多執行緒有5種狀態,分別為新建狀態、就緒狀態、執行狀態、阻塞狀態和死亡狀態。如下圖所示:
例子的UML類圖如下:
抽象狀態角色:
package com.charon.state; /** * @className: ThreadState * @description: 抽象狀態類 * @author: charon * @create: 2022-04-09 21:20 */ public abstract class ThreadState { /** * 執行緒狀態名稱 */ protected String stateName; }
具體狀態角色:
package com.charon.state;
/**
* @className: New
* @description:
* @author: charon
* @create: 2022-04-09 21:30
*/
public class New extends ThreadState{
public New() {
stateName = "New";
System.out.println("當前狀態處於:新建狀態.");
}
public void start(ThreadContext context){
System.out.print("呼叫start()方法-->");
if("New".equalsIgnoreCase(stateName)){
context.setState(new Runnable());
}else{
System.out.println("當前狀態不是新建狀態,不能呼叫start()方法");
}
}
}
package com.charon.state;
/**
* @className: Runnable
* @description:
* @author: charon
* @create: 2022-04-09 21:34
*/
public class Runnable extends ThreadState {
public Runnable() {
stateName = "Runnable";
System.out.println("當前執行緒處於:就緒狀態.");
}
public void getCPU(ThreadContext hj) {
System.out.print("獲得CPU時間-->");
if (stateName.equals("Runnable")) {
hj.setState(new Running());
} else {
System.out.println("當前執行緒不是就緒狀態,不能獲取CPU.");
}
}
}
package com.charon.state;
/**
* @className: Running
* @description:
* @author: charon
* @create: 2022-04-09 21:34
*/
public class Running extends ThreadState {
public Running() {
stateName = "Running";
System.out.println("當前執行緒處於:執行狀態.");
}
public void suspend(ThreadContext hj) {
System.out.print("呼叫suspend()方法-->");
if (stateName.equals("Running")) {
hj.setState(new Blocked());
} else {
System.out.println("當前執行緒不是執行狀態,不能呼叫suspend()方法.");
}
}
public void stop(ThreadContext hj) {
System.out.print("呼叫stop()方法-->");
if (stateName.equals("Running")) {
hj.setState(new Dead());
} else {
System.out.println("當前執行緒不是執行狀態,不能呼叫stop()方法.");
}
}
}
package com.charon.state;
/**
* @className: Blocked
* @description:
* @author: charon
* @create: 2022-04-09 21:35
*/
public class Blocked extends ThreadState {
public Blocked() {
stateName = "Blocked";
System.out.println("當前執行緒處於:阻塞狀態.");
}
public void resume(ThreadContext hj) {
System.out.print("呼叫resume()方法-->");
if (stateName.equals("Blocked")) {
hj.setState(new Runnable());
} else {
System.out.println("當前執行緒不是阻塞狀態,不能呼叫resume()方法.");
}
}
}
package com.charon.state;
/**
* @className: Dead
* @description:
* @author: charon
* @create: 2022-04-09 21:35
*/
public class Dead extends ThreadState {
public Dead() {
stateName = "Dead";
System.out.println("當前執行緒處於:死亡狀態.");
}
}
環境角色:
package com.charon.state;
/**
* @className: ThreadContext
* @description:
* @author: charon
* @create: 2022-04-09 21:32
*/
public class ThreadContext {
private ThreadState state;
ThreadContext() {
state = new New();
}
public void setState(ThreadState state) {
this.state = state;
}
public ThreadState getState() {
return state;
}
public void start() {
((New) state).start(this);
}
public void getCPU() {
((Runnable) state).getCPU(this);
}
public void suspend() {
((Running) state).suspend(this);
}
public void stop() {
((Running) state).stop(this);
}
public void resume() {
((Blocked) state).resume(this);
}
}
客戶端測試:
package com.charon.state;
/**
* @className: Client
* @description:
* @author: charon
* @create: 2022-04-09 21:51
*/
public class Client {
public static void main(String[] args) {
ThreadContext context = new ThreadContext();
context.start();
context.getCPU();
context.suspend();
context.resume();
context.getCPU();
context.stop();
}
}
列印:
當前狀態處於:新建狀態.
呼叫start()方法-->當前執行緒處於:就緒狀態.
獲得CPU時間-->當前執行緒處於:執行狀態.
呼叫suspend()方法-->當前執行緒處於:阻塞狀態.
呼叫resume()方法-->當前執行緒處於:就緒狀態.
獲得CPU時間-->當前執行緒處於:執行狀態.
呼叫stop()方法-->當前執行緒處於:死亡狀態.
狀態模式的主要優點如下:
- 結構清晰,狀態模式將與特定狀態相關的行為區域性化到一個狀態中,並且將不同狀態的行為分割開來,滿足“單一職責原則”。
- 將狀態轉換顯示化,減少物件間的相互依賴。將不同的狀態引入獨立的物件中會使得狀態轉換變得更加明確,且減少物件間的相互依賴。
- 狀態類職責明確,有利於程式的擴充套件。通過定義新的子類很容易地增加新的狀態和轉換。
狀態模式的主要缺點如下:
- 狀態模式的使用必然會增加系統的類與物件的個數。
- 狀態模式的結構與實現都較為複雜,如果使用不當會導致程式結構和程式碼的混亂。
- 狀態模式對開閉原則的支援並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的原始碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需要修改對應類的原始碼。
狀態模式的使用場景
- 一個物件的行為依賴於它所處的狀態,並且物件的行為必須隨著其狀態的改變而改變
- 物件在某個方法裡依賴於一重或多重的條件轉移語句,其中有大量的程式碼,狀態模式把條件轉移語句的每一個分支都包裝到一個單獨的類裡,這使得這些條件轉移分支能夠以類的方式獨立存在和演化,維護這些獨立的類也就不在影響到系統的其他部分。