設計模式學習筆記之八:外觀模式


面試時面試官問了我一個問題,事務有哪些基本特性?我不假思索地回答:原子性,一致性,隔離性和持久性,並對這四個特性的概念做了描述:

原子性:是指每個事務都是一個不可分割的最小單元,事務要么成功提交,要么失敗回滾,不存在半成功半失敗的情況。

一致性:跟原子性密切相關,是指事務的執行結果應該使數據庫從一種一致性狀態到另一種一致性狀態。

隔離性:事務與事務之前是隔離開的,一個事務的執行不能被其他事務干擾。

持久性:事務一旦成功提交,它執行的結果應該能夠持久化到數據庫中,接下來的其他操作及故障不應該對它的執行結果有任何影響。

面試官再問:能舉出生活中所看到的事務的例子嗎?

我:這個...嗯...這個...

看來不仔細領會概念的內容而死記硬背是過不了關的,其實生活之中事務的例子很多,拿復雜一點的例子來說,銀行轉賬就是一個典型的事務實例,當你轉賬的時候,錢從你賬戶划出到對方賬戶收到錢應該是在一個事務里面,要么轉賬成功要么轉賬失敗,不存在說你的錢從你的賬戶轉出后對對方賬號沒收到錢,ATM卻告訴你轉賬成功了。這個轉賬的過程就體現了事務的四個特征。如果從簡單的例子來說,我們每天開車,從打開車門到車子開始走這也是一個完整的事務,少了任何一個步驟例如忘記啟動發動機或沒有掛擋都無法使這個事務成功提交。

以上扯遠了,但是今天我們要說的設計模式的例子確實跟開車有關。

試想這樣一種場景,我們每天都要開車上下班,為了使車子走起來我們要做些什么操作?假設我們開的是手動擋的車,我們需要按這個步驟來操作:

1. 用遙控鑰匙解鎖車門

2. 打開車門

3. 關上車門

4. 啟動發動機

5. 掛擋

6. 松手剎

7. 松離合器

8. 車子開始走

如果用java代碼來描述這一系列的動作該怎樣設計呢?很顯然,我們需要幾個類,而每個類都有自己的一些行為,我先用UML類圖來描述一下這些類以及它們的行為。

為了更深入地探討這種設計模式能夠解決的問題,我把設計好的每一個類的代碼貼出來,雖然有點繁瑣,但是代碼也會很直觀。

汽車鑰匙:

public class CarKeys {
    public void unlock() {
        System.out.println("Key unlock car.");
    }
    
    public void lock() {
        System.out.println("Key lock car.");
    }
}

車門:

public class CarDoors {
    public void open() {
        System.out.println("Open the car door.");
    }
    
    public void close() {
        System.out.println("Close the car door.");
    }
}

發動機:

public class Engine {
    public void start() {
        System.out.println("Car Engine started.");
    }
    
    public void stop() {
        System.out.println("Car Engine stopped.");
    }
}

變速箱:

public class Gear {
    public void setGear(int gear) {
        System.out.println(String.format("Change to gear %s", gear));
    }
}

手剎:

public class ParkingBrake {
    public void release() {
        System.out.println("Release parking brake.");
    }
    
    public void hold() {
        System.out.println("Hold parking brake.");
    }
}

離合器:

public class Clutch {
    public void release() {
        System.out.println("Release clutch slowly.");
    }
    
    public void push() {
        System.out.println("Push clutch quickly");
    }
}

汽車:

public class Car {
    public void go() {
        System.out.println("Car go.");
    }
    
    public void stop() {
        System.out.println("Car Stop");
    }
}

測試類:

import org.junit.Before;
import org.junit.Test;

public class MyTest2 {
    private CarKeys carKeys;
    private CarDoors carDoors;
    private Engine engine;
    private Gear gear;
    private ParkingBrake parkingBrake;
    private Clutch clutch;
    private Car car;
    private DriveCarFacade facade;
    
    @Before
    public void setup() {
        carKeys = new CarKeys();
        carDoors = new CarDoors();
        engine = new Engine();
        gear = new Gear();
        parkingBrake = new ParkingBrake();
        clutch = new Clutch();
        car = new Car();
        facade = new DriveCarFacade();
        facade.setCarKeys(carKeys);
        facade.setCarDoors(carDoors);
        facade.setEngine(engine);
        facade.setGear(gear);
        facade.setParkingBrake(parkingBrake);
        facade.setClutch(clutch);
        facade.setCar(car);
    }
    
    @Test
    public void typicalWay4Go() {
        carKeys.unlock();
        carDoors.open();
        engine.start();
        gear.setGear(1);
        parkingBrake.release();
        clutch.release();
        car.go();
    }    
}

測試結果:

從以上UML類圖及代碼可以看到,我創建了7個類,每個類都有自己的行為,為了使車子走起來,我們有一個客戶端類來驅動這7個類及它們的行為,但是從這種設計里面我們看到一些弊端:

1. 車子的啟動順序由客戶決定,但是萬一客戶弄錯了呢?例如還沒有按遙控鑰匙就去開車門或是發動機還沒有啟動就掛擋。或者更糟糕的情況,客戶根本就沒有調用松離合器的方法。

2. 現在7個組件跟客戶都是聚合關系,如果這7個組件類里面有關於行為方法的改動(例如參數列表的變化)就勢必要更改客戶端的調用代碼。

從以上兩個缺點來看,我覺得這種設計是緊耦合並且是脆弱的。試想有一天,客戶希望他的汽車能夠自動駕駛,他不想要每天重復這么多繁瑣的動作,他想要的就是按一下汽車鑰匙的go按鈕然后汽車把剩下的所有步驟都完成,他只要下車去辦公室就好了。

那么如何解決這種問題?

為了解決這種問題,我們需要引入外觀模式,也有的稱之為門面模式。門面模式的定義是這樣的:外觀模式提供了一個統一的接口,用來訪問子系統中的一群接口。外觀定義了一個高層接口,讓子系統更容易使用。

外觀模式也詮釋了面向對象設計原則中的最少知識原則,最少知識意指一個類應該只跟它需要打交道的類溝通,這樣可以有效的保證松散耦合。

那么我們開始來實現外觀模式吧!還是先上UML類圖來個鳥瞰全局:

外觀類:

public class DriveCarFacade {
    private CarKeys carKeys;
    private CarDoors carDoors;
    private Engine engine;
    private Gear gear;
    private ParkingBrake parkingBrake;
    private Clutch clutch;
    private Car car;
    
    public void setCarKeys(CarKeys carKeys) {
        this.carKeys = carKeys;
    }
    public void setCarDoors(CarDoors carDoors) {
        this.carDoors = carDoors;
    }
    public void setEngine(Engine engine) {
        this.engine = engine;
    }
    public void setGear(Gear gear) {
        this.gear = gear;
    }
    public void setParkingBrake(ParkingBrake parkingBrake) {
        this.parkingBrake = parkingBrake;
    }
    public void setClutch(Clutch clutch) {
        this.clutch = clutch;
    }
    public void setCar(Car car) {
        this.car = car;
    }

    public void go() {
        this.carKeys.unlock();
        this.carDoors.open();
        this.engine.start();
        this.gear.setGear(1);
        this.parkingBrake.release();
        this.clutch.release();
        this.car.go();
    }
    
    public void stop() {
        this.clutch.push();
        this.gear.setGear(0);
        this.parkingBrake.hold();
        this.engine.stop();
        this.car.stop();
        this.carDoors.close();
        this.carKeys.lock();
    }
}

完整測試類:

import org.junit.Before;
import org.junit.Test;

public class MyTest {
    
    private CarKeys carKeys;
    private CarDoors carDoors;
    private Engine engine;
    private Gear gear;
    private ParkingBrake parkingBrake;
    private Clutch clutch;
    private Car car;
    private DriveCarFacade facade;
    
    @Before
    public void setup() {
        carKeys = new CarKeys();
        carDoors = new CarDoors();
        engine = new Engine();
        gear = new Gear();
        parkingBrake = new ParkingBrake();
        clutch = new Clutch();
        car = new Car();
        facade = new DriveCarFacade();
        facade.setCarKeys(carKeys);
        facade.setCarDoors(carDoors);
        facade.setEngine(engine);
        facade.setGear(gear);
        facade.setParkingBrake(parkingBrake);
        facade.setClutch(clutch);
        facade.setCar(car);
    }
    
    @Test
    public void typicalWay4Go() {
        carKeys.unlock();
        carDoors.open();
        engine.start();
        gear.setGear(1);
        parkingBrake.release();
        clutch.release();
        car.go();
    }
    
    @Test
    public void typicalWay4Stop() {
        clutch.push();
        gear.setGear(0);
        parkingBrake.hold();
        engine.stop();
        car.stop();
        carDoors.close();
        carKeys.lock();
        
    }
    
    @Test
    public void facadeWay4Go() {
        facade.go();
    }
    
    @Test
    public void facedeWay4Stop() {
        facade.stop();
    }

}

從類圖你能夠看到,7個組件類現在跟外觀類是聚合關系,客戶類現在不需要跟7個組件類打交道,它只需要跟掮客外觀類打交道就好了,這種設計的好處是顯而易見的:

1. 客戶端跟子系統松散耦合,子系統的改變不會造成客戶端調用者的改變。

2. 外觀類可以根據需要更改算法(操作的順序),客戶端對這些改變是不知情的。

3. 增加子系統或者替換子系統,對客戶沒有影響。

4. 實踐了最小知識原則。

各位看官看到這里,有沒有覺得其實我們可以使用另一種設計模式來達到同樣的效果?是的,其實我們可以使用命令模式來做到同樣的效果,我們可以把每一個操作定義為子命令,然后用一個宏來集合一群子命令,宏里面是可以定義操作順序的,最后客戶端只需要調用這個宏就可以了,一樣可以達到客戶端跟組件類松耦合的目的,只不過架構方式不一樣而已,關於具體實現,我會另開一篇來詳細闡述,在這里我就拋磚引玉,各位有何看法,歡迎拍磚:)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM