1. 程式人生 > 其它 >.NetCore中IdentityServer使用nginx-proxy的一次排錯經歷

.NetCore中IdentityServer使用nginx-proxy的一次排錯經歷

狀態模式又稱狀態物件模式,屬於行為型模式;狀態模式允許一個物件在其內部狀態改變的時候改變其行為,這個物件看上去就像是改變了它的類一樣。狀態模式把所研究的物件的行為包裝在不同的狀態物件裡,每一個狀態物件都屬於一個抽象狀態類的子類,狀態模式的意圖是讓一個物件在其內部狀態改變的時候,其行為也隨之改變。

對這種有狀態的物件程式設計,傳統的解決方案是:將這些所有可能發生的情況全都考慮到,然後使用 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()方法-->當前執行緒處於:死亡狀態.

狀態模式的主要優點如下:

  1. 結構清晰,狀態模式將與特定狀態相關的行為區域性化到一個狀態中,並且將不同狀態的行為分割開來,滿足“單一職責原則”。
  2. 將狀態轉換顯示化,減少物件間的相互依賴。將不同的狀態引入獨立的物件中會使得狀態轉換變得更加明確,且減少物件間的相互依賴。
  3. 狀態類職責明確,有利於程式的擴充套件。通過定義新的子類很容易地增加新的狀態和轉換。

狀態模式的主要缺點如下:

  1. 狀態模式的使用必然會增加系統的類與物件的個數。
  2. 狀態模式的結構與實現都較為複雜,如果使用不當會導致程式結構和程式碼的混亂。
  3. 狀態模式對開閉原則的支援並不太好,對於可以切換狀態的狀態模式,增加新的狀態類需要修改那些負責狀態轉換的原始碼,否則無法切換到新增狀態,而且修改某個狀態類的行為也需要修改對應類的原始碼。

狀態模式的使用場景

  1. 一個物件的行為依賴於它所處的狀態,並且物件的行為必須隨著其狀態的改變而改變
  2. 物件在某個方法裡依賴於一重或多重的條件轉移語句,其中有大量的程式碼,狀態模式把條件轉移語句的每一個分支都包裝到一個單獨的類裡,這使得這些條件轉移分支能夠以類的方式獨立存在和演化,維護這些獨立的類也就不在影響到系統的其他部分。