主頁 > 軟體設計 > 【硬核】23種設計模式娓娓道來,助你優雅的撰寫出漂亮代碼!

【硬核】23種設計模式娓娓道來,助你優雅的撰寫出漂亮代碼!

2021-04-09 06:58:13 軟體設計

大家好,我是小羽,

我們平時使用的每一個技術堆疊的原理或者原始碼都或多或少與設計模式的理念有關聯,也可以這么說,只有更好的掌握了設計模式,我們的代碼撰寫才能更規范、簡潔,效率更高,

其次,設計模式大多都是經過我們的前輩的經驗反復總結而成,站在巨人的肩膀上,吸收他們的經驗教訓,我們的編碼之路才會走的更長久,

同時,在我們的面試程序中也是加分的選項,你如果將設計模式能跟面試官娓娓道來,面試官肯定會對你刮目相看的,作業中,擁有良好的設計模式思想,對于專案的開發也會有很大的幫助,

接下來,跟著小羽一起來看看我們需要了解的設計模式都有哪些呢~

前言

總體來說設計模式分為三大類:

創建型模式:工廠方法模式、抽象工廠模式、單例模式、建造者模式、原型模式,

結構型模式:配接器模式、裝飾器模式、代理模式、外觀模式、橋接模式、組合模式、享元模式,

行為型模式:策略模式、模板方法模式、觀察者模式、迭代子模式、責任鏈模式、命令模式、備忘錄模式、狀態模式、訪問者模式、中介者模式、解釋器模式,

單例模式

概念

確保某一個類只有一個實體,而且自行實體化并向整個系統提供這個實體,

使用場景

  • 要求生成唯一序列號的環境;

  • 在整個專案中需要一個共享訪問點或共享資料,例如一個Web頁面上的計數器,可以不用把每次重繪都記錄到資料庫中,使用單例模式保持計數器的值,并確保是執行緒安全的;

  • 創建一個物件需要消耗的資源過多,如要訪問IO和資料庫等資源;

  • 需要定義大量的靜態常量和靜態方法(如工具類)的環境,可以采用單例模式(當然,也可以直接宣告為static的方式),

代碼示例

執行緒安全:

public class Singleton {
    private static final Singleton singleton = new Singleton();
    //限制產生多個物件
    private Singleton(){
    }
    //通過該方法獲得實體物件
    public static Singleton getSingleton(){
        return singleton;
    }
    //類中其他方法,盡量是 static
    public static void doSomething(){
    }
}

執行緒不安全:

public class Singleton {
    private static Singleton singleton = null;
    //限制產生多個物件
    private Singleton(){
    }
    //通過該方法獲得實體物件
    public static Singleton getSingleton(){
        if(singleton == null){
            singleton = new Singleton();
        }
        return singleton;
    }
}

針對執行緒不安全:
在 getSingleton 方法前加 synchronized 關鍵字,也可以在 getSingleton 方法內增加synchronized 來實作,

工廠模式

概念

定義一個用于創建物件的介面,讓子類決定實體化哪一個類,工廠方法使一個類的實體化延遲到其子類,

使用場景

jdbc 連接資料庫,硬體訪問,降低物件的產生和銷毀

結構

簡單工廠模式:一個模塊僅需要一個工廠類,沒有必要把它產生出來,使用靜態的方法

多個工廠類:每個人種(具體的產品類)都對應了一個創建者,每個創建者獨立負責創建對應的產品物件,非常符合單一職責原則

代替單例模式:單例模式的核心要求就是在記憶體中只有一個物件,通過工廠方法模式也可以只在記憶體中生產一個物件

延遲初始化:ProductFactory 負責產品類物件的創建作業,并且通過 prMap 變數產生一個快取,對需要再次被重用的物件保留

代碼示例

Product 為抽象產品類負責定義產品的共性,實作對事物最抽象的定義;

Creator 為抽象創建類,也就是抽象工廠,具體如何創建產品類是由具體的實作工廠 ConcreteCreator 完成的,

public class ConcreteCreator extends Creator {
    public <T extends Product> T createProduct(Class<T> c){
        Product product=null;
        try {
            product =
                    (Product)Class.forName(c.getName()).newInstance();
        } catch (Exception e) {
        //例外處理
        }
        return (T)product;
    }
}

抽象工廠模式

概念

為創建一組相關或相互依賴的物件提供一個介面,而且無須指定它們的具體類,

使用場景

一個物件族(或是一組沒有任何關系的物件)都有相同的約束,

涉及不同作業系統的時候,都可以考慮使用抽象工廠模式,

代碼示例

public abstract class AbstractCreator {
    //創建 A 產品家族
    public abstract AbstractProductA createProductA();
    //創建 B 產品家族
    public abstract AbstractProductB createProductB();
}

模板方法模式

概念

定義一個操作中的演算法的框架,而將一些步驟延遲到子類中,使得子類可以不改變一個演算法的結構即可重定義該演算法的某些特定步驟,

使用場景

  • 多個子類有公有的方法,并且邏輯基本相同時,

  • 重要、復雜的演算法,可以把核心演算法設計為模板方法,周邊的相關細節功能則由各個子類實作,

  • 重構時,模板方法模式是一個經常使用的模式,把相同的代碼抽取到父類中,然后通過鉤子函式(見“模板方法模式的擴展”)約束其行為,

結構

抽象模板:AbstractClass 為抽象模板,它的方法分為兩類:

1、基本方法:也叫做基本操作,是由子類實作的方法,并且在模板方法被呼叫,

2、模板方法:可以有一個或幾個,一般是一個具體方法,也就是一個框架,實作對基本方法的調度,完成固定的邏輯,

注意: 為了防止惡意的操作,一般模板方法都加上 final 關鍵字,不允許被覆寫

具體模板:實作父類所定義的一個或多個抽象方法,也就是父類定義的基本方法在子類中得以實作,

代碼示例

package templateMethod;
public class TemplateMethodPattern
{
    public static void main(String[] args)
    {
        AbstractClass tm=new ConcreteClass();
        tm.TemplateMethod();
    }
}

//抽象類
abstract class AbstractClass
{
    public void TemplateMethod() //模板方法
    {
        SpecificMethod();
        abstractMethod1();
        abstractMethod2();
    }
    public void SpecificMethod() //具體方法
    {
        System.out.println("抽象類中的具體方法被呼叫...");
    }
    public abstract void abstractMethod1(); //抽象方法1
    public abstract void abstractMethod2(); //抽象方法2
}

//具體子類
class ConcreteClass extends AbstractClass
{
    public void abstractMethod1()
    {
        System.out.println("抽象方法1的實作被呼叫...");
    }
    public void abstractMethod2()
    {
        System.out.println("抽象方法2的實作被呼叫...");
    }
}

建造者模式

概念

將一個復雜物件的構建與它的表示分離,使得同樣的構建程序可以創建不同的表示,

使用場景

  • 相同的方法,不同的執行順序,產生不同的事件結果時,可以采用建造者模式,

  • 多個部件或零件,都可以裝配到一個物件中,但是產生的運行結果又不相同時,則可以使用該模式,

  • 產品類非常復雜,或者產品類中的呼叫順序不同產生了不同的效能,這個時候使用建造者模式非常合適,

結構

Product 產品類:通常是實作了模板方法模式,也就是有模板方法和基本方法,

Builder 抽象建造者:規范產品的組建,一般是由子類實作,

ConcreteBuilder 具體建造者:實作抽象類定義的所有方法,并且回傳一個組建好的物件,

Director 導演類:負責安排已有模塊的順序,然后告訴 Builder 開始建造

代碼示例

public class ConcreteProduct extends Builder {
     private Product product = new Product();
     //設定產品零件
     public void setPart(){
             /*
              * 產品類內的邏輯處理
              */
     }  
     //組建一個產品
     public Product buildProduct() {
             return product;
     }
}

代理模式

概念

為其他物件提供一種代理以控制對這個物件的訪問,

結構

Subject 抽象主題角色:抽象主題類可以是抽象類也可以是介面,是一個最普通的業務型別定義,無特殊要求,

RealSubject 具體主題角色:也叫做被委托角色、被代理角色,它才是冤大頭,是業務邏輯的具體執行者,

Proxy 代理主題角色:也叫做委托類、代理類,它負責對真實角色的應用,把所有抽象主題類定義的方法、限制委托給真實主題角色實作,并且在真實主題角色處理完畢前后做預處理和善后處理作業,

分類

普通代理:在該模式下,呼叫者只知代理而不用知道真實的角色是誰,屏蔽了真實角色的變更對高層模塊的影響,真實的主題角色想怎么修改就怎么修改,對高層次的模塊沒有任何的影響,只要你實作了介面所對應的方法,該模式非常適合對擴展性要求較高的場合,

強制代理:強制代理的概念就是要從真實角色查找到代理角色,不允許直接訪問真實角色,高層模塊只要呼叫 getProxy 就可以訪問真實角色的所有方法,它根本就不需要產生一個代理出來,代理的管理已經由真實角色自己完成,

  • 區別:普通代理就是我們要知道代理的存在,也就是類似的 GamePlayerProxy 這個類的存在,然后才能訪問;強制代理則是呼叫者直接呼叫真實角色,而不用關心代理是否存在,其代理的產生是由真實角色決定的,

動態代理:根據被代理的介面生成所有的方法,也就是說給定一個介面,動態代理會宣稱“我
已經實作該介面下的所有方法了”,兩條獨立發展的線路,動態代理實作代理的職責,業務邏輯 Subject 實作相關的邏輯功能,兩者之間沒有必然的相互耦合的關系,通知 Advice 從另一個切面切入,最終在高層模塊也就是 Client 進行耦合,完成邏輯的封裝任務,

  • 意圖:橫切面編程,在不改變我們已有代碼結構的情況下增強或控制物件的行為,

  • 首要條件:被代理的類必須要實作一個介面,

代碼示例

public Object getProxy(@Nullable ClassLoader classLoader) {
	if (logger.isTraceEnabled()) {
		logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
	}
	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

原型模式

概念

用原型實體指定創建物件的種類,并且通過拷貝這些原型創建新的物件,

使用場景

資源優化場景:類初始化需要消化非常多的資源,這個資源包括資料、硬體資源等,

性能和安全要求的場景:通過 new 產生一個物件需要非常繁瑣的資料準備或訪問權限,則可以使用原型模式,

一個物件多個修改者的場景:一個物件需要提供給其他物件訪問,而且各個呼叫者可能都需要修改其值時,可以、考慮使用原型模式拷貝多個物件供呼叫者使用,

優點

原型模式實際上就是實作 Cloneable 介面,重寫 clone()方法,

性能優良:原型模式是在記憶體二進制流的拷貝,要比直接 new 一個物件性能好很多,特別是要在一個回圈體內產生大量的物件時,原型模式可以更好地體現其優點,

逃避建構式的約束:這既是它的優點也是缺點,直接在記憶體中拷貝,建構式是不會執行的,

代碼示例

public class PrototypeClass implements Cloneable{
    //覆寫父類 Object 方法
    @Override
    public PrototypeClass clone(){
        PrototypeClass prototypeClass = null;
        try {
            prototypeClass = (PrototypeClass)super.clone();
        } catch (CloneNotSupportedException e) {
        //例外處理
        }
        return prototypeClass;
    }
}

中介者模式

概念

用一個中介物件封裝一系列的物件互動,中介者使各物件不需要顯示地相互作用,從而使其耦合松散,而且可以獨立地改變它們之間的互動,

使用場景

中介者模式適用于多個物件之間緊密耦合的情況,緊密耦合的標準是:在類圖中出現了蜘蛛網狀結構,即每個類都與其他的類有直接的聯系,

結構

Mediator 抽象中介者角色:抽象中介者角色定義統一的介面,用于各同事角色之間的通信,

Concrete Mediator 具體中介者角色:具體中介者角色通過協調各同事角色實作協作行為,因此它必須依賴于各個同事角色,

Colleague 同事角色:每一個同事角色都知道中介者角色,而且與其他的同事角色通信的時候,一定要通過中介者角色協作,每個同事類的行為分為兩種:一種是同事本身的行為,比如改變物件本身的狀態,處理自己的行為等,這種行為叫做自發行為(SelfMethod),與其他的同事類或中介者沒有任何的依賴;第二種是必須依賴中介者才能完成的行為,叫做依賴方法(Dep-Method),

示例代碼

public abstract class Mediator {
    //定義同事類
    protected ConcreteColleague1 c1;
    protected ConcreteColleague2 c2;
    //通過 getter/setter 方法把同事類注入進來
    public ConcreteColleague1 getC1() {
        return c1;
    }
    public void setC1(ConcreteColleague1 c1) {
        this.c1 = c1;
    }
    public ConcreteColleague2 getC2() {
        return c2;
    }
    public void setC2(ConcreteColleague2 c2) {
        this.c2 = c2;
    }
    //中介者模式的業務邏輯
    public abstract void doSomething1();
    public abstract void doSomething2();
}

命令模式

概念

將一個請求封裝成一個物件,從而讓你使用不同的請求把客戶端引數化,對請求排隊或者記錄請求日志,可以提供命令的撤銷和恢復功能,

使用場景

認為是命令的地方就可以采用命令模式,例如,在 GUI 開發中,一個按鈕的點擊是一個命令,可以采用命令模式;模擬 DOS 命令的時候,當然也要采用命令模式;觸發-反饋機制的處理等,

結構

Receive 接收者角色:該角色就是干活的角色,命令傳遞到這里是應該被執行的,具體到我們上面的例子中就是 Group 的三個實作類(需求組,美工組,代碼組),

Command 命令角色:需要執行的所有命令都在這里宣告,

Invoker 呼叫者角色:接收到命令,并執行命令,在例子中,我(專案經理)就是這個角色,

代碼示例

public class Invoker {
	private Command command;
	
	// 設值注入
	public void setCommand(Command command) {
		this.command = command;
	}

	// 執行命令
	public void action() {
		this.command.execute();
	}
}

責任鏈模式

概念

使多個物件都有機會處理請求,從而避免了請求的發送者和接受者之間的耦合關系,將這些物件連成一條鏈,并沿著這條鏈傳遞該請求,直到有物件處理它為止,

職責

抽象的處理者實作三個職責:

1、定義一個請求的處理方法 handleMessage,唯一對外開放的方法;

2、定義一個鏈的編排方法 setNext,設定下一個處理者;

3、定義了具體的請求者必須實作的兩個方法:定義自己能夠處理的級別getHandlerLevel 和具體的處理任務 echo,

代碼示例

public abstract class Handler {
    private Handler nextHandler;
    //每個處理者都必須對請求做出處理
    public final Response handleMessage(Request request){
        Response response = null;
        //判斷是否是自己的處理級別
        if(this.getHandlerLevel().equals(request.getRequestLevel())){
            response = this.echo(request);
        }else{ //不屬于自己的處理級別
            //判斷是否有下一個處理者
            if(this.nextHandler != null){
                response =
                        this.nextHandler.handleMessage(request);
            }else{
            //沒有適當的處理者,業務自行處理
            } }
        return response;
    }
    //設定下一個處理者是誰
    public void setNext(Handler _handler){
        this.nextHandler = _handler;
    }
    //每個處理者都有一個處理級別
    protected abstract Level getHandlerLevel();
    //每個處理者都必須實作處理任務
    protected abstract Response echo(Request request);
}

注意事項

鏈中節點數量需要控制,避免出現超長鏈的情況,一般的做法是在 Handler 中設定一個最大節點數量,在 setNext 方法中判斷是否已經是超過其閾值,超過則不允許該鏈建立,避免無意識地破壞系統性能,

裝飾模式

概念

動態地給一個物件添加一些額外的職責,就增加功能來說,裝飾模式相比生成子類更為靈活,

使用場景

  • 需要擴展一個類的功能,或給一個類增加附加功能,

  • 需要動態地給一個物件增加功能,這些功能可以再動態地撤銷,

  • 需要為一批的兄弟類進行改裝或加裝功能,當然是首選裝飾模式,

結構

Component 抽象構件:Component 是一個介面或者是抽象類,就是定義我們最核心的物件,也就是最原始的物件,在裝飾模式中,必然有一個最基本、最核心、最原始的介面或抽象類充當Component 抽象構件,

ConcreteComponent 具體構件:ConcreteComponent 是最核心、最原始、最基本的介面或抽象類的實作,你要裝飾的就是它,

Decorator 裝飾角色:一般是一個抽象類,做什么用呢?實作介面或者抽象方法,它里面可不一定有抽象的方法呀,在它的屬性里必然有一個 private 變數指向 Component 抽象構件,

具體裝飾角色:ConcreteDecoratorA 和 ConcreteDecoratorB 是兩個具體的裝飾類,你要把你最核心的、最原始的、最基本的東西裝飾成其他東西,上面的例子就是把一個比較平庸的成績單裝飾成家長認可的成績單,

代碼示例

/**
 * 裝飾角色
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
@Log
class BufferedReader implements Reader{

    private  Reader reader;
    @Override
    public void read() {
        reader.read();
    }

    public void readLine(){
        read();
        log.info("并且僅僅讀取一行");
    }
}

策略模式

概念

定義一組演算法,將每個演算法都封裝起來,并且使它們之間可以互換,

使用場景

  • 多個類只有在演算法或行為上稍有不同的場景,

  • 演算法需要自由切換的場景,

  • 需要屏蔽演算法規則的場景,

  • 具體策略數量超過 4 個,則需要考慮使用混合模式

結構

Context 封裝角色:它也叫做背景關系角色,起承上啟下封裝作用,屏蔽高層模塊對策略、演算法的直接訪問,封裝可能存在的變化,

Strategy 抽象策略角色:策略、演算法家族的抽象,通常為介面,定義每個策略或演算法必須具有的方法和屬性,

ConcreteStrategy 具體策略角色:實作抽象策略中的操作,該類含有具體的演算法,

代碼示例

public enum Calculator {
    //加法運算
    ADD("+"){
        public int exec(int a,int b){
            return a+b;
        }
    },
    //減法運算
    SUB("-"){
        public int exec(int a,int b){
            return a - b;
        }
    };
    String valuehttps://www.cnblogs.com/qianyueric/archive/2021/04/08/= "";
    //定義成員值型別
    private Calculator(String _value){
        this.value = https://www.cnblogs.com/qianyueric/archive/2021/04/08/_value;
    }
    //獲得列舉成員的值
    public String getValue(){
        return this.value;
    }
    //宣告一個抽象函式
    public abstract int exec(int a,int b);
}

配接器模式

概念

將一個類的介面變換成客戶端所期待的另一種介面,從而使原本因介面不匹配而無法在一起作業的兩個類能夠在一起作業,

使用場景

你有動機修改一個已經投產中的介面時,配接器模式可能是最適合你的模式,比如系統擴展了,需要使用一個已有或新建立的類,但這個類又不符合系統的介面,怎么辦?詳細設計階段不要考慮使用配接器模式,使用主要場景為擴展應用中,

類配接器

Target 目標角色:該角色定義把其他類轉換為何種介面,也就是我們的期望介面,

Adaptee 源角色:你想把誰轉換成目標角色,這個“誰”就是源角色,它是已經存在的、運行良好的類或物件,經過配接器角色的包裝,它會成為一個嶄新、靚麗的角色,

Adapter 配接器角色:配接器模式的核心角色,其他兩個角色都是已經存在的角色,而配接器角色是需要新建立的,它的職責非常簡單:把源角色轉換為目標角色,怎么轉換?通過繼承或是類關聯的方式,

物件配接器

不使用多繼承或繼承的方式,而是使用直接關聯,或者稱為委托的方式,

物件配接器和類配接器的區別:

類配接器是類間繼承,物件配接器是物件的合成關系,也可以說是類的關聯關系,這是兩者的根本區別,實際專案中物件配接器使用到的場景相對比較多,

代碼示例

public class Adapter extends Target
{
	private Adaptee adaptee;
	
	public Adapter(Adaptee adaptee)
	{
		this.adaptee=adaptee;
	}
	
	public void request()
	{
		adaptee.specificRequest();
	}
} 

迭代器模式

概念

它提供一種方法訪問一個容器物件中各個元素,而又不需暴露該物件的內部細節,

結構

Iterator 抽象迭代器:抽象迭代器負責定義訪問和遍歷元素的介面,而且基本上是有固定的 3 個方法:first()獲得第一個元素,next()訪問下一個元素,isDone()是否已經訪問到底部(Java 叫做 hasNext()方法),

ConcreteIterator 具體迭代器:具體迭代器角色要實作迭代器介面,完成容器元素的遍歷,

Aggregate 抽象容器:容器角色負責提供創建具體迭代器角色的介面,必然提供一個類似createIterator()這樣的方法,在 Java 中一般是 iterator()方法,

Concrete Aggregate 具體容器:具體容器實作容器介面定義的方法,創建出容納迭代器的物件,

代碼示例

/**
 * 具體迭代器
 */
public class ConcreteIterator<T> implements Iterator<T> {

    private List<T> list = new ArrayList<>();

    private int cursor = 0;

    public boolean hasNext() {
        return cursor != list.size();
    }

    public T next() {
        T obj = null;
        if (this.hasNext()) {
            obj = this.list.get(cursor++);
        }
        return obj;
    }

}

組合模式

概念

將物件組合成樹形結構以表示“部分-整體”的層次結構,使得用戶對單個物件和組合物件的使用具有一致性,

使用場景

  • 維護和展示部分-整體關系的場景,如樹形選單、檔案和檔案夾管理,

  • 從一個整體中能夠獨立出部分模塊或功能的場景,

  • 只要是樹形結構,就考慮使用組合模式,

結構

Component 抽象構件角色:定義參加組合物件的共有方法和屬性,可以定義一些默認的行為或屬性,

Leaf 葉子構件:葉子物件,其下再也沒有其他的分支,也就是遍歷的最小單位,

Composite 樹枝構件:樹枝物件,它的作用是組合樹枝節點和葉子節點形成一個樹形結構,

代碼示例

public class Composite extends Component {
    //構件容器
    private ArrayList<Component> componentArrayList = new
            ArrayList<Component>();
    //增加一個葉子構件或樹枝構件
    public void add(Component component){
        this.componentArrayList.add(component);
    }
    //洗掉一個葉子構件或樹枝構件
    public void remove(Component component){
        this.componentArrayList.remove(component);
    }
    //獲得分支下的所有葉子構件和樹枝構件
    public ArrayList<Component> getChildren(){
        return this.componentArrayList;
    } 
}

觀察者模式

概念

定義物件間一種一對多的依賴關系,使得每當一個物件改變狀態,則所有依賴于它的物件都會得到通知并被自動更新,

使用場景

  • 關聯行為場景,需要注意的是,關聯行為是可拆分的,而不是“組合”關系,

  • 事件多級觸發場景,

  • 跨系統的訊息交換場景,如訊息佇列的處理機制,

結構

Subject 被觀察者:定義被觀察者必須實作的職責,它必須能夠動態地增加、取消觀察者,它一般是抽象類或者是實作類,僅僅完成作為被觀察者必須實作的職責:管理觀察者并通知觀察者,

Observer 觀察者:觀察者接收到訊息后,即進行 update(更新方法)操作,對接收到的資訊進行處理,

ConcreteSubject 具體的被觀察者:定義被觀察者自己的業務邏輯,同時定義對哪些事件進行通知,

ConcreteObserver 具體的觀察者:每個觀察在接收到訊息后的處理反應是不同,各個觀察者有自己的處理邏輯,

代碼示例

public abstract class Subject {
    //定義一個觀察者陣列
    private Vector<Observer> obsVector = new Vector<Observer>();
    //增加一個觀察者
    public void addObserver(Observer o){
        this.obsVector.add(o);
    }
    //洗掉一個觀察者
    public void delObserver(Observer o){
        this.obsVector.remove(o);
    }
    //通知所有觀察者
    public void notifyObservers(){
        for(Observer o:this.obsVector){
            o.update();
        }
    } 
}

門面模式

概念

要求一個子系統的外部與其內部的通信必須通過一個統一的物件進行,門面模式提供一個高層次的介面,使得子系統更易于使用,

使用場景

  • 為一個復雜的模塊或子系統提供一個供外界訪問的介面

  • 子系統相對獨立——外界對子系統的訪問只要黑箱操作即可

  • 預防低水平人員帶來的風險擴散

結構

Facade 門面角色:客戶端可以呼叫這個角色的方法,此角色知曉子系統的所有功能和責任,一般情況下,本角色會將所有從客戶端發來的請求委派到相應的子系統去,也就說該角色沒有實際的業務邏輯,只是一個委托類,

subsystem 子系統角色:可以同時有一個或者多個子系統,每一個子系統都不是一個單獨的類,而是一個類的集合,子系統并不知道門面的存在,對于子系統而言,門面僅僅是另外一個客戶端而已,

代碼模式

public class Client {
	//委托的子系統物件
    private A a= new A();
    private B b= new B();
    private C c= new C();
    //提供外部訪問的方法
    public void methodA(){
    	this.a.doSomething();
	}
	public void methodB(){
    	this.b.doSomething();
	}
	public void methodC(){
    	this.c.doSomething();
	}
}

備忘錄模式

概念

在不破壞封裝性的前提下,捕獲一個物件的內部狀態,并在該物件之外保存這個狀態,這樣以后就可將該物件恢復到原先保存的狀態,

使用場景

  • 需要保存和恢復資料的相關狀態場景,

  • 提供一個可回滾(rollback)的操作,

  • 需要監控的副本場景中,

  • 資料庫連接的事務管理就是用的備忘錄模式,

結構

Originator 發起人角色:記錄當前時刻的內部狀態,負責定義哪些屬于備份范圍的狀態,負責創建和恢復備忘錄資料,

Memento 備忘錄角色:負責存盤 Originator 發起人物件的內部狀態,在需要的時候提供發起人需要的內部狀態,

Caretaker 備忘錄管理員角色:對備忘錄進行管理、保存和提供備忘錄,

代碼示例

public class BeanUtils {
    //把 bean 的所有屬性及數值放入到 Hashmap 中
    public static HashMap<String,Object> backupProp(Object bean){
        HashMap<String,Object> result = new
                HashMap<String,Object>();
        try {
            //獲得 Bean 描述
            BeanInfo
                    beanInfo=Introspector.getBeanInfo(bean.getClass());
            //獲得屬性描述
            PropertyDescriptor[]
                    descriptors=beanInfo.getPropertyDescriptors();
            //遍歷所有屬性
            for(PropertyDescriptor des:descriptors){
                //屬性名稱
                String fieldName = des.getName();
                //讀取屬性的方法
                Method getter = des.getReadMethod();
                //讀取屬性值
                Object fieldValue=https://www.cnblogs.com/qianyueric/archive/2021/04/08/getter.invoke(bean,new
                        Object[]{});
                if(!fieldName.equalsIgnoreCase("class")){
                    result.put(fieldName, fieldValue);
                } } } catch (Exception e) {
                //例外處理
        }
        return result;
    }
    //把 HashMap 的值回傳到 bean 中
    public static void restoreProp(Object bean,HashMap<String,Object>
            propMap){
        try {
            //獲得 Bean 描述
            BeanInfo beanInfo =
                    Introspector.getBeanInfo(bean.getClass());
            //獲得屬性描述
            PropertyDescriptor[] descriptors =
                    beanInfo.getPropertyDescriptors();
            //遍歷所有屬性
            for(PropertyDescriptor des:descriptors){
                //屬性名稱
                String fieldName = des.getName();
                //如果有這個屬性
                if(propMap.containsKey(fieldName)){
                    //寫屬性的方法
                    Method setter = des.getWriteMethod();
                    setter.invoke(bean, new
                            Object[]{propMap.get(fieldName)});
                } } } catch (Exception e) {
            //例外處理
            System.out.println("shit");
            e.printStackTrace();
        }
    }
}

訪問者模式

概念

封裝一些作用于某種資料結構中的各元素的操作,它可以在不改變資料結構的前提下定義作用于這些元素的新的操作,

使用場景

  • 一個物件結構包含很多類物件,它們有不同的介面,而你想對這些物件實施一些依賴于其具體類的操作,也就說是用迭代器模式已經不能勝任的情景,

  • 需要對一個物件結構中的物件進行很多不同并且不相關的操作,而你想避免讓這些操作“污染”這些物件的類,

結構

Visitor——抽象訪問者:抽象類或者介面,宣告訪問者可以訪問哪些元素,具體到程式中就是 visit 方法的引數定義哪些物件是可以被訪問的,

ConcreteVisitor——具體訪問者:它影響訪問者訪問到一個類后該怎么干,要做什么事情,

Element——抽象元素:介面或者抽象類,宣告接受哪一類訪問者訪問,程式上是通過 accept 方法中的引數來定義的,

ConcreteElement——具體元素:實作 accept 方法,通常是 visitor.visit(this),基本上都形成了一種模式了,

ObjectStruture——結構物件:元素產生者,一般容納在多個不同類、不同介面的容器,如 List、Set、Map 等,在專案中,一般很少抽象出這個角色,

代碼示例

public class CompensationVisitor implements Visitor {
 
	@Override
	public void Visit(Element element) {
		// TODO Auto-generated method stub
		Employee employee = ((Employee) element);
 
		System.out.println(
				employee.getName() + "'s Compensation is " + (employee.getDegree() * employee.getVacationDays() * 10));
	}
 
}

狀態模式

概念

當一個物件內在狀態改變時允許其改變行為,這個物件看起來像改變了其類,

使用場景

  • 行為隨狀態改變而改變的場景,這也是狀態模式的根本出發點,例如權限設計,人員的狀態不同即使執行相同的行為結果也會不同,在這種情況下需要考慮使用狀態模式,

  • 條件、分支判斷陳述句的替代者

結構

State——抽象狀態角色:介面或抽象類,負責物件狀態定義,并且封裝環境角色以實作狀態切換,

ConcreteState——具體狀態角色:每一個具體狀態必須完成兩個職責:本狀態的行為管理以及趨向狀態處理,通俗地說,就是本狀態下要做的事情,以及本狀態如何過渡到其他狀態,

Context——環境角色:定義客戶端需要的介面,并且負責具體狀態的切換,

代碼示例

//抽象狀態角色
public abstract class State {
     //定義一個環境角色,提供子類訪問
     protected Context context;
     //設定環境角色
     public void setContext(Context _context){
             this.context = _context;
     }
     //行為1
     public abstract void handle1();
     //行為2
     public abstract void handle2();
}

解釋器模式

概念

給定一門語言,定義它的文法的一種表示,并定義一個解釋器,該解釋器使用該表示來解釋語言中的句子,

使用場景

  • 重復發生的問題可以使用解釋器模式

  • 一個簡單語法需要解釋的場景

結構

AbstractExpression——抽象解釋器:具體的解釋任務由各個實作類完成,具體的解釋器分別由TerminalExpression 和 Non-terminalExpression 完成,

TerminalExpression——終結符運算式:實作與文法中的元素相關聯的解釋操作,通常一個解釋器模式中只有一個終結符運算式,但有多個實體,對應不同的終結符,具體到我們例子就是 VarExpression類,運算式中的每個終結符都在堆疊中產生了一個 VarExpression 物件,

NonterminalExpression——非終結符運算式:文法中的每條規則對應于一個非終結運算式,具體到我們的例子就是加減法規則分別對應到 AddExpression 和 SubExpression 兩個類,非終結符運算式根據邏輯的復雜程度而增加,原則上每個文法規則都對應一個非終結符運算式,

Context——環境角色:具體到我們的例子中是采用 HashMap 代替,

代碼示例

/**
 * 終結符運算式
 */
public class TerminalExpression extends AbstractExpression {
 
	@Override
	public void interpret(Context ctx) {
		// 實作與語法規則中的終結符相關聯的解釋操作
 
	}
 
}

/**
 * 非終結符運算式
 */
public class NonterminalExpression extends AbstractExpression {
 
	@Override
	public void interpret(Context ctx) {
		// 實作與語法規則中的非終結符相關聯的解釋操作
 
	}
 
}

享元模式

概念

使用共享物件可有效地支持大量的細粒度的物件,

物件的資訊分為兩個部分:內部狀態(intrinsic)與外部狀態(extrinsic),

內部狀態:內部狀態是物件可共享出來的資訊,存盤在享元物件內部并且不會隨環境改變而改變,

外部狀態:外部狀態是物件得以依賴的一個標記,是隨環境改變而改變的、不可以共享的狀態,

使用場景

  • 系統中存在大量的相似物件,

  • 細粒度的物件都具備較接近的外部狀態,而且內部狀態與環境無關,也就是說物件沒有特定身份,

  • 需要緩沖池的場景,

結構

Flyweight——抽象享元角色:它簡單地說就是一個產品的抽象類,同時定義出物件的外部狀態和內部狀態的介面或實作,

ConcreteFlyweight——具體享元角色:具體的一個產品類,實作抽象角色定義的業務,該角色中需要注意的是內部狀態處理應該與環境無關,不應該出現一個操作改變了內部狀態,同時修改了外部狀態,這是絕對不允許的,

unsharedConcreteFlyweight——不可共享的享元角色:不存在外部狀態或者安全要求(如執行緒安全)不能夠使用共享技術的物件,該物件一般不會出現在享元工廠中,

FlyweightFactory——享元工廠:職責非常簡單,就是構造一個池容器,同時提供從池中獲得物件的方法,

代碼示例

public class FlyweightFactory {
    //定義一個池容器
    private static HashMap<String,Flyweight> pool= new
            HashMap<String,Flyweight>();
    //享元工廠
    public static Flyweight getFlyweight(String Extrinsic){
        //需要回傳的物件
        Flyweight flyweight = null;
        //在池中沒有該物件
        if(pool.containsKey(Extrinsic)){
            flyweight = pool.get(Extrinsic);
        }else{
            //根據外部狀態創建享元物件
            flyweight = new ConcreteFlyweight1(Extrinsic);
            //放置到池中
            pool.put(Extrinsic, flyweight);
        }
        return flyweight;
    }
}

橋梁模式

概念

抽象和實作解耦,使得兩者可以獨立地變化,

使用場景

  • 不希望或不適用使用繼承的場景

  • 介面或抽象類不穩定的場景

  • 重用性要求較高的場景

結構

Abstraction——抽象化角色:它的主要職責是定義出該角色的行為,同時保存一個對實作化角色的參考,該角色一般是抽象類,

Implementor——實作化角色:它是介面或者抽象類,定義角色必需的行為和屬性,

RefinedAbstraction——修正抽象化角色:它參考實作化角色對抽象化角色進行修正,

ConcreteImplementor——具體實作化角色:它實作介面或抽象類定義的方法和屬性,

代碼示例

public abstract class Abstraction {
    //定義對實作化角色的參考
    private Implementor imp;
    //約束子類必須實作該建構式
    public Abstraction(Implementor _imp){
        this.imp = _imp;
    }
    //自身的行為和屬性
    public void request(){
        this.imp.doSomething();
    }
    //獲得實作化角色
    public Implementor getImp(){
        return imp;
    }
}

總結

大家在學習設計模式的時候,不要把它看得有多難,歸根結底,都是一些概論性的總結,需要我們在平時的不斷學習和作業中,慢慢去理解它的深層原理,這樣才能靈活應用每一種設計模式,

設計模式是在前人的總結上,對一些場景的問題的進行解決的一種方案,設計模式不是公式,沒必要去死記硬背每一種模式,更重要的是了解它的抽象思想,以及應用設計模式怎么更好的解決問題,可以達成什么效果,理論雖多,但是我們要把它掌握的話,對于我們的實際開發來說會解決不少的問題,

當然后面還會繼續為大家更新關于設計原則的內容,方便大家進一步理解設計模式,

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/274009.html

標籤:其他

上一篇:Java單例模式實作,一次性學完整,面試加分項

下一篇:別再面向 for 回圈編程了,Spring 自帶的觀察者模式就很香!

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more