1. 程式人生 > >淺談Java設計模式——單例項、簡單工廠、抽象工廠、觀察者

淺談Java設計模式——單例項、簡單工廠、抽象工廠、觀察者

最近的專案裡面涉及到一些Java設計模式,在此簡單談一下自己的看法,以下示例一部分參考同行,大部分自己設計。

1.單例模式
如果一個類始終只能建立一個例項,則這個類成為單例類,這種設計模式稱為單例模式。

class Singleton
{
    // 使用一個類變數快取建立的例項
    private static Singleton instance;
    // 隱藏構造器
    private Singleton(){

    }
    // 提供一個靜態方法,用於返回Singleton例項
    public static Singleton getInstance
() { // 如果instance為null,表明還不曾建立Singleton物件 // 如果instance不為null,則表明已經建立了Singleton物件,將不會執行該方法 if (instance == null) { // 建立一個Singleton物件,並將其快取起來 instance = new Singleton(); } //如果instance已經建立過,直接返回 return instance; } }

寫個測試程式

public class SingletonTest
{
    public static void main(String[] args)
    {
        // 建立兩個Singleton 物件
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        // 將輸出true
        System.out.println(s1 == s2);
    }
}

Spring容器所管理的Bean例項預設是單例,可以通過scope來修改其行為方式。
使用單例模式的優勢:
(1)減少系統開銷(不用每次都建立新的例項)
(2)便於系統跟蹤單個例項的生命週期與例項狀態等。

2.簡單工廠
通過工廠來建立物件的設計模式稱為簡單工廠模式。
這裡寫圖片描述
當需要建立一個物件的時候不通過new 來建立,而是通過向工廠下訂單來建立。
先定義一個房子(House),房子裡面有張桌子(Table),還可以獲得桌子的相關資訊(getTableMaterial()獲得桌子的材料)。

/**
 * 
 * 一個House裡面有張桌子table(這個table是抽象的,需要工廠根據使用者的實際要求具體實現)
 * table通過工廠tableFactory來生產
 * tableFactory根據使用者所要求的材料material來定製table
 */
public class House {
    private Table table;

    public House(Table table){
        this.table = table;
    }
    //獲得table 的material(所用材料)
    public void getTableMaterial(){
        table.getMaterial();
    }

    public static void main(String[] args){
        TableFactory tableFactory = new TableFactory();
        //通過工廠生產Table物件並傳遞給構造器
        House house = new House(tableFactory.getTable("wood"));
        house.getTableMaterial();
    }
}

下面是Table介面,擁有Table的一般行為。

public interface Table {
    public void getMaterial();
}

下面是三個具體的實現類(三種材質的桌子)。

/**
 * 木桌
 */
public class WoodTable implements Table {

    @Override
    public void getMaterial() {
        // TODO Auto-generated method stub
        System.out.println("Material of this Table is Wood!");
    }

}
/**
 * 鐵桌
 */
public class SteelTable implements Table {

    @Override
    public void getMaterial() {
        // TODO Auto-generated method stub
        System.out.println("Material of this Table is Steel!");
    }

}
/**
 * 塑料桌
 */
public class PlasticTable implements Table {

    @Override
    public void getMaterial() {
        // TODO Auto-generated method stub
        System.out.println("Material of this Table is Plastic!");
    }

}

下面是桌子工廠,可以根據使用者的需求生產相應的桌子。

public class TableFactory {

    public Table getTable(String material){
        switch(material){
        case "wood":
            return new WoodTable();
        case "steel":
            return new SteelTable();
        default :
            return new PlasticTable();
        }
    }
}

下面進行測試:

public static void main(String[] args){
        TableFactory tableFactory = new TableFactory();
        //工廠根據使用者的需求生產Table物件並傳遞給構造器
        House house = new House(tableFactory.getTable("wood"));//使用者要求木桌(woodTable)
        house.getTableMaterial();
    }

測試結果:
這裡寫圖片描述
使用者可以向工廠提出自己的需求(woodTable ,steelTable還是plasticTable),由工廠來生產Table(建立Table物件);

簡單工廠的優勢:物件的呼叫者與物件的建立過程分離,避免物件的呼叫與實現以硬編碼的方式耦合,提高系統的可維護性和可拓展性。

3.抽象工廠模式
抽象工廠簡單地說是工廠的工廠,抽象工廠可以建立具體工廠,由具體工廠來產生具體產品。
這裡寫圖片描述
來看個示例。
有一個汽車商店(CarShop),裡面有一輛汽車(car),還可以獲得汽車的相關資訊。

public class CarShop {
    private Car car;

    public CarShop(Car car){
        this.car = car;
    }
    //獲得汽車的相關資訊
    public void getCarInfo(){
        car.getCarInfo();
    }
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //通過抽象工廠獲得具體工廠
        CarFactory cf = CarFactoryFactory.getCarFactory("A");
        //通過具體工廠建立具體car物件並傳遞給構造器
        CarShop cs = new CarShop(cf.createCar("BMW"));
        cs.getCarInfo();
    }
}

有一個抽象汽車工廠,用來根據使用者需求建立具體汽車工廠。

public class CarFactoryFactory {
    //根據carType建立具體工廠
    public static CarFactory getCarFactory(String carType){
        switch(carType){
        case "A":
            return new ACarFactory();
        case "B":
            return new BCarFactory();
        default :
            return new CCarFactory();
        }
    }
}

有A、B、C三個具體汽車工廠,用來生產三種類型的汽車。

/**
 * A工廠
 * 根據使用者要求的品牌(carBrand)
 * 生產A型別的汽車
 */
public class ACarFactory implements CarFactory {

    @Override
    public Car createCar(String carBrand) {
        // TODO Auto-generated method stub
        switch(carBrand){
        case "BMW":
            return new BMWCarA();
        case "Benz":
            return new BenzCarA();
        default :
            return new AudiCarA();
        }
    }

}
/**
* B工廠
 * 根據使用者要求的品牌(carBrand)
 * 生產B型別的汽車
 */
public class BCarFactory implements CarFactory {

    /* (non-Javadoc)
     * @see com.abstructFactory.CarFactory#createCar(java.lang.String)
     */
    @Override
    public Car createCar(String carBrand) {
        switch(carBrand){
        case "BMW":
            return new BMWCarB();
        case "Benz":
            return new BenzCarB();
        default :
            return new AudiCarB();
        }
    }

}
/**
 * C工廠
 * 根據使用者要求的品牌(carBrand)
 * 生產C型別的汽車
 */
public class CCarFactory implements CarFactory {

    @Override
    public Car createCar(String carBrand) {
        switch(carBrand){
        case "BMW":
            return new BMWCarC();
        case "Benz":
            return new BenzCarC();
        default :
            return new AudiCarC();
        }
    }
}
這三個具體工廠類都實現了同一個汽車工廠介面,該介面擁有一般汽車工廠的行為。
public interface CarFactory {
    public Car createCar(String carBrand);
}

A、B、C三個具體汽車工廠分別生產三種品牌(BMW,Benz和Audi)的汽車。

/**
 * A工廠生產的Audi汽車
 */
public class AudiCarA implements Car {

    @Override
    public void getCarInfo() {
        System.out.println("Audi car from ACarFactory");
    }
}
/**
 * A工廠生產的Benz汽車
 */
public class BenzCarA implements Car {

    @Override
    public void getCarInfo() {
        System.out.println("Benz car from ACarFactory");
    }
}
/**
 * A工廠生產的BMW汽車
 */
public class BMWCarA implements Car {

    @Override
    public void getCarInfo() {
        System.out.println("BMW car from ACarFactory");
    }
}
/**
 * B工廠生產的Audi汽車
 */
public class AudiCarB implements Car {

    @Override
    public void getCarInfo() {
        System.out.println("Audi car from BCarFactory");
    }
}
/**
 * B工廠生產的Benz汽車
 */
public class BenzCarB implements Car {

    @Override
    public void getCarInfo() {
        System.out.println("Benz car from BCarFactory");
    }
}
/**
 * B工廠生產的BMW汽車
 */
public class BMWCarB implements Car {

    @Override
    public void getCarInfo() {
        System.out.println("BMW car from BCarFactory");
    }
}
/**
 * C工廠生產的Audi汽車
 */
public class AudiCarC implements Car {

    @Override
    public void getCarInfo() {
        System.out.println("BMW car from CCarFactory");
    }
}
/**
 * C工廠生產的Benz汽車
 */
public class BenzCarC implements Car {

    @Override
    public void getCarInfo() {
        System.out.println("BMW car from CCarFactory");
    }
}
/**
 * C工廠生產的BMW汽車
 */
public class BMWCarC implements Car {

    @Override
    public void getCarInfo() {
        System.out.println("BMW car from CCarFactory");
    }
}

這些具體的汽車產品都實現了同一個介面(Car),該介面擁有汽車的一般行為。

public interface Car {
    public void getCarInfo();
}

下面來做個測試,使用者要求一輛A工廠生產的BMW汽車:

public static void main(String[] args) {
        // TODO Auto-generated method stub
        //通過抽象工廠獲得具體工廠
        CarFactory cf = CarFactoryFactory.getCarFactory("A");
        //通過具體工廠建立具體car物件並傳遞給構造器
        CarShop cs = new CarShop(cf.createCar("BMW"));
        cs.getCarInfo();
    }

測試結果:
這裡寫圖片描述
抽象工廠可以根據使用者的需求,通過不同的具體工廠生產不同的產品。
那麼抽象工廠模式有什麼好處呢?假如採用簡單工廠模式,假如只有A工廠,當用戶要求一輛B型別的BMW時,就要修改A工廠程式碼來生產BMWCarB物件。所以,抽象工廠模式的優勢是:物件的呼叫者與物件的實現類以及具體的工廠分離,程式碼的耦合性更低,系統可維護性以及可拓展性更高。
但是抽象工廠模式有個缺陷,就是當用戶需求改變的時候,需要修改程式碼,然後需要重新編譯,最好是將使用者需求(可以看做使用者訂單)放在一個配置檔案裡面,由程式碼根據配置檔案來建立相應的工廠以及例項,這樣當用戶需求發生改變的時候,只需要修改配置檔案(產品訂單)即可。
Spring IOC容器是一個抽象工廠,既可以管理Bean例項,還可以管理工廠例項。ApplicationContext.xml可以看作是系統的訂單,容器根據這個訂單生產相應的Bean。

4.觀察者模式
觀察者模式又稱釋出訂閱模式,定義了一種一對多依賴關係,讓一個或多個觀察者物件觀察一個主題物件,當主題物件的狀態發生改變時,系統會通知所有的依賴於此物件的觀察者,從而使得觀察者物件能夠自動更新。
看一個示例:
有一個任務列表TaskList,裡面的任務需要兩個程式設計師去完成,TaskList可以看作是被觀察的主題物件,兩個程式設計師可以看作是依賴於此主題物件的觀察者,當任務列表裡面的任務更新時,要通知這兩個程式設計師。
下面是TaskList:

public class TaskList extends Observable{
    private List<String> allTask;

    private static TaskList instance;
    //私有構造器
    private TaskList(){

    }

    public static TaskList getInstance(){
        if(instance == null){
            instance = new TaskList();
            instance.allTask = new ArrayList<String>();
        }
        return instance;
    }
    //新增觀察者
    public void addTaskObserver(Observer observer){
        this.addObserver(observer);
    }
    //任務列表發生改變
    public void addNewTask(String newTask){
        allTask.add("new Task");
        System.out.println("系統添加了新任務");
        //被觀察物件發生改變
        this.setChanged();
        //通知觀察者並傳遞新任務
        this.notifyObservers(newTask);

    }
}
  下面是兩個程式設計師:
public class ProgrammerA implements Observer{
    //自動更新
    @Override
    public void update(Observable o, Object task) {
        String newTask = (String)task;
        System.out.println("ProgrammerA 您有新的任務: " +newTask);
    }
}
public class ProgrammerB implements Observer{
    //自動更新
    @Override
    public void update(Observable o, Object task) {
        String newTask = (String)task;
        System.out.println("ProgrammerB 您有新的任務: " +newTask);        
    }
}

下面做個測試:

public class ObserverTest {

    public static void main(String[] args) {
        //被觀察的任務列表
        TaskList observable = TaskList.getInstance();
        //觀察者
        ProgrammerA pa = new ProgrammerA();
        ProgrammerB pb = new ProgrammerB();
        //新增觀察者
        observable.addObserver(pa);
        observable.addObserver(pb);
        //任務列表發生改變
        observable.addNewTask("new Task");
    }
}

測試結果:
這裡寫圖片描述

Java EE 應用中,主題/訂閱模式下的JMS就是觀察者模式的應用。

設計模式是對處於特定環境下,經常出現的某類軟體開發問題的一種相對成熟的設計方案,是軟體設計師集體經驗的體現。優雅的程式碼沒必要刻意使用哪種設計模式,最好是它本身就體現出設計模式。