1. 程式人生 > 實用技巧 >Selenium系列教程(十一)Selenium Grid 分散式測試

Selenium系列教程(十一)Selenium Grid 分散式測試

設計模式文章

中介者模式

代理模式

抽象工廠模式詳解 —— head first 設計模式

裝飾者模式

介面卡模式

策略模式

觀察者模式

建造者模式 (Builder)

概述

在現實生活中,某些類具有兩個或多個維度的變化,如圖形既可按形狀分,又可按顏色分。如何設計類似於 Photoshop 這樣的軟體,能畫不同形狀和不同顏色的圖形呢?如果用繼承方式,m 種形狀和 n 種顏色的圖形就有 m×n 種,不但對應的子類很多,而且擴充套件困難。

當然,這樣的例子還有很多,如不同顏色和字型的文字、不同品牌和功率的汽車、不同性別和職業的男女、支援不同平臺和不同檔案格式的媒體播放器等。如果用橋接模式就能很好地解決這些問題。

橋接模式的定義與特點

橋接(Bridge)模式的定義如下:將抽象與實現分離,使它們可以獨立變化。它是用組合關係代替繼承關係來實現,從而降低了抽象和實現這兩個可變維度的耦合度。

橋接(Bridge)模式的優點是:

  • 由於抽象與實現分離,所以擴充套件能力強;

  • 其實現細節對客戶透明。

缺點是:由於聚合關係建立在抽象層,要求開發者針對抽象化進行設計與程式設計,這增加了系統的理解與設計難度。

橋接模式的結構與實現

主要解決:在多維可能會變化的情況下,用繼承會造成類爆炸問題,擴充套件起來不靈活。

何時使用:實現系統可能有多個角度分類,每一種角度都可能變化。

如何解決:把這種多角度分類分離出來,讓它們獨立變化,減少它們之間耦合。

注意事項:對於兩個獨立變化的維度,使用橋接模式再適合不過了。

結構

可以將抽象化部分與實現化部分分開,取消二者的繼承關係,改用組合關係。

橋接(Bridge)模式包含以下主要角色。

  • 抽象化(Abstraction)角色:定義抽象類,幷包含一個對實現化物件的引用。

  • 擴充套件抽象化(Refined Abstraction)角色:是抽象化角色的子類,實現父類中的業務方法,並通過組合關係調用實現化角色中的業務方法。

  • 實現化(Implementor)角色:定義實現化角色的介面,供擴充套件抽象化角色呼叫。

  • 具體實現化(Concrete Implementor)角色:給出實現化角色介面的具體實現。

模式示例

1、定義實現類介面(Implementor)

public interface Implementor {

    public void operationImpl();
}

2、定義具體實現類(ConcreteImplementor)A和B

public class ConcreteImplementorA implements Implementor{
 
    @Override
    public void operationImpl() {
        System.out.println("型別A");
    }
}

public class ConcreteImplementorB implements Implementor{
 
    @Override
    public void operationImpl() {
        System.out.println("型別B");
    }
}

3、定義抽象類(Abstraction)

public abstract class Abstraction {
 
    public Implementor implementor;
    
    public void setImplementor(Implementor implementor) {
        this.implementor = implementor;
    }
 
    public abstract void operation();
}

4、定義擴充抽象類(Refined Abstraction)

public class RefinedAbstraction extends Abstraction{
 
    @Override
    public void operation() {
        implementor.operationImpl();
    }
}

5、測試程式碼如下:

public class TestMain {
 
    public static void main(String[] args) {
        Abstraction a = new RefinedAbstraction();
        a.setImplementor(new ConcreteImplementorA());
        a.operation();
    }
}

示例分析

我們都去買過手機,手機按照品牌分可以分為華為、小米、oppo、vivo 等品牌,如果這些手機按照記憶體分又可以分為 4G、6G、8G 等等。假如我們每一種手機都想要玩一下,至少需要 4*3 個。這對我們來說這些手機也太多了,竟然有 12 個,最主要的是手機品牌和記憶體是放在一起的。現在有這樣一種機制,手機牌品商是一個公司,做手機記憶體的是一個公司,想要做什麼手機我們只需要讓其兩者搭配起來即可。有點類似於全球貿易分工明確的思想,這就是橋接模式,把兩個不同維度的東西橋接起來。

1、定義實現類介面(Implementor),這裡定義手機記憶體介面:

/*Implementor:定義手機記憶體介面*/
public interface Memory {
    public void addMemory();
}

2、定義具體實現類(ConcreteImplementor),這裡指具體的記憶體,記憶體這裡定義了兩種一種是 6G,一種是 8G

/*ConcreteImplementor:具體實現類1*/
public class Memory6G implements Memory{
    @Override
    public void addMemory() {
        System.out.println("手機裝了6G記憶體");
    }
}

/*ConcreteImplementor:具體實現類2*/
public class Memory8G implements Memory{
    @Override
    public void addMemory() {
        System.out.println("手機裝了8G記憶體");
    }
}

3、定義抽象類(Abstraction),這裡指手機

/*Abstraction:手機抽象類*/
public abstract class Phone {
 
    public Memory memory;
    
    public void set(Memory memory) {
        this.memory = memory;
    }
    public abstract void buyPhone();
}

4、定義擴充抽象類(Refined Abstraction),這裡指具體的手機品牌,以華為和小米為例

public class HuaWei extends Phone{
 
    public void buyPhone() {
        memory.addMemory();
        System.out.println("購買華為手機");
    }
}

public class XiaoMi extends Phone{
 
    public void buyPhone() {
        memory.addMemory();
        System.out.println("購買小米手機");
    }
}

5、測試程式碼如下:

public class TestMain {
 
    public static void main(String[] args) {
        //讓華為搭配8G記憶體
        Phone huawei = new HuaWei();
        huawei.set(new Memory8G());
        huawei.buyPhone();
        
        //讓小米搭配6G記憶體
        Phone xiaomi = new XiaoMi();
        xiaomi.set(new Memory6G());
        xiaomi.buyPhone();
    }
}

6、輸出結果如下:

從程式碼就可以看出,購買手機的時候,品牌和記憶體兩個維度是分開的,這樣後續品牌和記憶體之間是可以獨立變化,而不會影響到另一個類。

橋接模式與策略模式的區別

橋接模式如下:

策略如下:

在橋接中,Abstraction 通過聚合方式引用 Implementor.

在策略中,Context 也通過聚合引用 Strategy.

橋接 (Bridge) 模式是結構型模式的一種,而策略 (strategy) 模式則屬於行為模式。

從他們的結構圖可知,在這兩種模式中,都存在一個物件使用聚合的方式引用另一個物件的抽象介面的情況,而且該抽象介面的實現可以有多種並且可以替換。可以說兩者在表象上都是呼叫者與被呼叫者之間的解耦,以及抽象介面與實現的分離。

那麼兩者的區別體現在什麼地方呢?

  1. 首先,在形式上,兩者還是有一定區別的,對比兩幅結構圖,我們可以發現,在橋接模式中不僅 Implementor 具有變化(ConcreateImplementior),而且 Abstraction 也可以發生變化(RefinedAbstraction),而且兩者的變化是完全獨立的,RefinedAbstraction 與 ConcreateImplementior 之間鬆散耦合,它們僅僅通過 Abstraction 與 Implementor 之間的關係聯絡起來。而在策略模式中,並不考慮Context的變化,只有演算法的可替代性。

  2. 其次在語意上,橋接模式強調 Implementor 介面僅提供基本操作,而 Abstraction 則基於這些基本操作定義更高層次的操作。而策略模式強調 Strategy 抽象介面的提供的是一種演算法,一般是無狀態、無資料的,而 Context 則簡單呼叫這些演算法完成其操作。

  3. 橋接模式中不僅定義 Implementor 的介面而且定義 Abstraction 的介面,Abstraction 的介面不僅僅是為了與 Implementor 通訊而存在的,這也反映了結構型模式的特點:通過繼承、聚合的方式組合類和物件以形成更大的結構。在策略模式中,Startegy 和 Context 的介面都是兩者之間的協作介面,並不涉及到其它的功能介面,所以它是行為模式的一種。行為模式的主要特點就是處理的是物件之間的通訊方式,往往是通過引入中介者物件將通訊雙方解耦,在這裡實際上就是將 Context 與實際的演算法提供者解耦。

所以相對策略模式,橋接模式要表達的內容要更多,結構也更加複雜。橋接模式表達的主要意義其實是介面隔離的原則,即把本質上並不內聚的兩種體系區別開來,使得它們可以鬆散的組合,而策略在解耦上還僅僅是某一個演算法的層次,沒有到體系這一層次。從結構圖中可以看到,策略的結構是包容在橋接結構中的,橋接中必然存在著策略模式,Abstraction 與 Implementor 之間就可以認為是策略模式,但是橋接模式一般 Implementor 將提供一系列的成體系的操作,而且 Implementor 是具有狀態和資料的靜態結構。而且橋接模式 Abstraction 也可以獨立變化。

參考文章

策略模式與橋接模式區別

橋接模式