設計模式---工廠方法模式

2022-10-01 06:02:01

簡述

  • 型別:建立型

  • 目的:實現對使用者端中物件的平替

我們藉以下案例來說說如何使用工廠方法模式平替物件。

優化案例

最初版

public interface OS {
    public void start();
    public void sleep();
    public void restart();
    public void stop();
}
public class Linux implements OS {
    public void start() {
        System.out.println("啟動Linux系統!");
    }
    public void sleep() {
        System.out.println("睡眠Linux系統!");
    }
    public void restart() {
        System.out.println("重啟Linux系統!");
    }
    public void stop() {
        System.out.println("停止Linux系統!");
    }
}
public class Windows implements OS {
    public void start() {
        System.out.println("啟動Windows系統!");
    }
    public void sleep() {
        System.out.println("睡眠Windows系統!");
    }
    public void restart() {
        System.out.println("重啟Windows系統!");
    }
    public void stop() {
        System.out.println("停止Windows系統!");
    }
}
public class Unix implements OS {
    public void start() {
        System.out.println("啟動Unix系統!");
    }
    public void sleep() {
        System.out.println("睡眠Unix系統!");
    }
    public void restart() {
        System.out.println("重啟Unix系統!");
    }
    public void stop() {
        System.out.println("停止Unix系統!");
    }
}

使用者端呼叫如下。

public class Client {
    public static void main(String[] args) {
        OS os1 = new Linux();
        OS os2 = new Windows();
        OS os3 = new Unix();
    }
}

傳統是new建立物件的方式有著寫死的問題。當我們需要把所有Linux物件改為Unix物件時,就必須在專案中檢索所有的Linux一一修改為Unix。這無疑增加了大量的無意義的工作。

修改版v1(簡單工廠模式)

增加一個工廠類,其他不變。

public class OSFactory {
    static OS instance(String arg) {
        if (arg.equals("Linux")) {
            return new Linux();
        } else if (arg.equals("Unix")) {
            return new Unix();
        } else if (arg.equals("Windows")) {
            return new Windows();
        }
        throw new Exception("輸入的引數錯誤");
    }
}

修改後,使用者端的程式碼呼叫。

public class Client {
    public static void main(String[] args) {
        OS os1 = OSFactory.instance("Linux");
        OS os2 = OSFactory.instance("Windows");
        OS os3 = OSFactory.instance("Unix");
    }
}

在一定程度上解決了使用者端寫死問題。並且當我們需要把所有Linux物件改為Unix物件時,只需要在OS中將new Linux() → new Unix()即可。這無疑節省了很多的時間,也無需為寫死帶來的大量改修而苦惱。

但是目前這個優化方案依然有至少兩個問題,一是OSFactory.instance方法中耦合了所有的OS實現類,這可能有礙於未來的專案維護,二是new Linux() → new Unix()這種修改方式會導致程式碼變得不明確,既然不論是Linux還是Unix都直接生成Unix物件,就沒有必要定義Linux了呀。實際上是因為使用者端程式碼中還有使用OSFactory.instance("Linux")來建立的物件,為了不修改使用者端程式碼,強行做如上修改。

修改版v2(工廠方法模式)

將原本的工廠類抽象化,並定義一系列不同的實現類,其餘不變。

public interface OSFactory {
   	OS create();
}
public class LinuxFactory {
    public OS create() {
        return new Linux();
    }
}
public class WindowsFactory {
    public OS create() {
        return new Windows();
    }
}
public class UnixFactory {
    public OS create() {
        return new Unix();
    }
}

修改後,使用者端的程式碼呼叫。

public class Client {
    public static void main(String[] args) {
        OSFactory factory = new LinuxFactory();
        OS os1 = factory.create();
    }
}

將原本OSFactory類中臃腫的邏輯分散到各個子類中,提高了系統的可維護性,不用再每次都修改Factory類了。

那麼,問題來了,這樣的結構對於我們的專案有什麼幫助嗎?幾乎沒有,我們只是將物件的建立統一管理了而已,這也只是工廠方法模式的一個很小的功能。實際上需求是快速的將系統中的物件平替。而為了實現這個需求,我們需要結合Java反射這項技術。請看下面的程式碼。

修改版v3(工廠方法+反射)

只修改使用者端的呼叫方式,其他位置不做修改。

public class Client {
    public static void main(String[] args) {
        // 實際專案中一般定義成特定的類(專門用來載入各種設定)中的靜態變數
        Properties prop = new Properties();
        FileReader fileReader = new FileReader("src/resource/props/config.prop");
        // 使用properties檔案來儲存當前呼叫類的資訊
        prop.load(fileReader);
        fileReader.close();
        OSFactory factory = (OSFactory) Class.forName(prop.getProperty("FACTORY"))
                                             .getDeclaredConstructor().newInstance();
        OS os1 = factory.create();
    }
}

增加一個properties檔案檔案,定義如下。

#當前使用的工廠類
FACTORY=design.factorymethod.demo02.LinuxFactory

當系統需要將範例的LinuxFactory類轉化為其他的實現類時,只需要更改上述組態檔即可。

總結

優點

  • 輕鬆做到類的平替。

缺點

  • 類數量倍增,系統複雜度增加。

應用場景

  • 根據需求,需要全面替換系統中的某個類時。