目錄
- 1 背景
- 1.1 題目描述
- 1.2 輸入描述
- 1.3 輸出描述
- 1.4 輸入示例
- 1.5 輸出示例
- 2 抽象工廠模式
- 3 思考
- 3.1 我的實現
- 3.2 什么時候用抽象工廠模式?(怎么用才是合適的?)
- 3.3 [更好的例子](https://refactoringguru.cn/design-patterns/abstract-factory/java/example)
- 3.3.1 背景
- 3.3.2 示例
1 背景
題目來源:【設計模式專題之抽象工廠模式】3. 家具工廠
1.1 題目描述
小明家新開了兩個工廠用來生產家具,一個生產現代風格的沙發和椅子,一個生產古典風格的沙發和椅子,現在工廠收到了一筆訂單,請你幫他設計一個系統,描述訂單需要生產家具的信息。
1.2 輸入描述
輸入的第一行是一個整數 N(1 ≤ N ≤ 100),表示訂單的數量。
接下來的 N 行,每行輸入一個字符串,字符串表示家具的類型。家具類型分為 “modern” 和 “classical” 兩種。
1.3 輸出描述
對于每筆訂單,輸出字符串表示該訂單需要生產家具的信息。
modern訂單會輸出下面兩行字符串
modern chair
modern sofa
classical訂單會輸出下面兩行字符串
classical chair
classical soft
1.4 輸入示例
3
modern
classical
modern
1.5 輸出示例
modern chair
modern sofa
classical chair
classical sofa
modern chair
modern sofa
2 抽象工廠模式
- 代碼示例:【來源】
import java.util.Scanner;// 抽象椅子接口
interface Chair {void showInfo();
}// 具體現代風格椅子
class ModernChair implements Chair {@Overridepublic void showInfo() {System.out.println("modern chair");}
}// 具體古典風格椅子
class ClassicalChair implements Chair {@Overridepublic void showInfo() {System.out.println("classical chair");}
}// 抽象沙發接口
interface Sofa {void displayInfo();
}// 具體現代風格沙發
class ModernSofa implements Sofa {@Overridepublic void displayInfo() {System.out.println("modern sofa");}
}// 具體古典風格沙發
class ClassicalSofa implements Sofa {@Overridepublic void displayInfo() {System.out.println("classical sofa");}
}// 抽象家居工廠接口
interface FurnitureFactory {Chair createChair();Sofa createSofa();
}// 具體現代風格家居工廠
class ModernFurnitureFactory implements FurnitureFactory {@Overridepublic Chair createChair() {return new ModernChair();}@Overridepublic Sofa createSofa() {return new ModernSofa();}
}// 具體古典風格家居工廠
class ClassicalFurnitureFactory implements FurnitureFactory {@Overridepublic Chair createChair() {return new ClassicalChair();}@Overridepublic Sofa createSofa() {return new ClassicalSofa();}
}public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);// 讀取訂單數量int N = scanner.nextInt();// 處理每個訂單for (int i = 0; i < N; i++) {// 讀取家具類型String furnitureType = scanner.next();// 創建相應風格的家居裝飾品工廠FurnitureFactory factory = null;if (furnitureType.equals("modern")) {factory = new ModernFurnitureFactory();} else if (furnitureType.equals("classical")) {factory = new ClassicalFurnitureFactory();}// 根據工廠生產椅子和沙發Chair chair = factory.createChair();Sofa sofa = factory.createSofa();// 輸出家具信息chair.showInfo();sofa.displayInfo();}}
}
3 思考
- 這真的是抽象工廠模式嗎?太不靈活了。
- 在上面的背景下,新增風格,新增家具,是很常見的需求。而按照上面的寫法,需要改動很多代碼才能實現。這不就完全沒體現抽象工廠模式的優勢了?
3.1 我的實現
public class Main {public static void main(String[] args) {FurnitureFactorySystem furnitureFactorySystem = FurnitureFactorySystem.getSingleton();Scanner scanner = new Scanner(System.in);int n = Integer.parseInt(scanner.nextLine());for (int i = 0; i < n; i ++) {String type = scanner.nextLine();furnitureFactorySystem.produce(type);}scanner.close();}
}/*** 生產限制:根據風格,成套出售。(對應現實生活,手機配色,對應整部手機)*/
class FurnitureFactorySystem {private static final Map<StyleEnum, FurnitureFactory> furnitureFactoryMap = new HashMap<>();private static FurnitureFactorySystem instance;private FurnitureFactorySystem() {furnitureFactoryMap.put(StyleEnum.MODERN, new ModernFurnitureFactory());furnitureFactoryMap.put(StyleEnum.CLASSIC, new ClassicFurnitureFactory());}public static FurnitureFactorySystem getSingleton() {if (instance == null) {synchronized (FurnitureFactorySystem.class) {if (instance == null) {instance = new FurnitureFactorySystem();}}}return instance;}public void produce(String type) {FurnitureFactory furnitureFactory = furnitureFactoryMap.get(StyleEnum.of(type));final List<Furniture> furnitures = Arrays.asList(new Chair(), new Sofa());furnitureFactory.createFurniture(furnitures);}
}interface FurnitureFactory {void createFurniture(List<Furniture> furnitures);
}class ModernFurnitureFactory implements FurnitureFactory {@Overridepublic void createFurniture(List<Furniture> furnitures) {for (Furniture furniture : furnitures) {furniture.create(StyleEnum.MODERN.getValue());}}
}class ClassicFurnitureFactory implements FurnitureFactory {@Overridepublic void createFurniture(List<Furniture> furnitures) {for (Furniture furniture : furnitures) {furniture.create(StyleEnum.CLASSIC.getValue());}}
}interface Furniture {void create(String type);
}class Chair implements Furniture {@Overridepublic void create(String type) {System.out.println(type + " " + FurnitureEnum.CHAIR.getValue());}
}class Sofa implements Furniture {@Overridepublic void create(String type) {System.out.println(type + " " + FurnitureEnum.SOFA.getValue());}
}@AllArgsConstructor
@Getter
enum StyleEnum {MODERN("modern"), CLASSIC("classical");private final String value;public static StyleEnum of(String value) {for (StyleEnum styleEnum : StyleEnum.values()) {if (styleEnum.getValue().equals(value)) {return styleEnum;}}// 如果沒有找到匹配的枚舉對象,可以拋出一個異常或返回nullthrow new IllegalArgumentException("Unknown StyleEnum: " + value);}
}/*** 家具*/
@AllArgsConstructor
@Getter
enum FurnitureEnum {CHAIR("chair"), SOFA("sofa");private final String value;public static FurnitureEnum of(String value) {for (FurnitureEnum furnitureEnum : FurnitureEnum.values()) {if (furnitureEnum.getValue().equals(value)) {return furnitureEnum;}}// 如果沒有找到匹配的枚舉對象,可以拋出一個異常或返回nullthrow new IllegalArgumentException("Unknown FurnitureEnum: " + value);}
}
- 假設要新增風格和新家具,需要改動的代碼:
// 新增語句
private FurnitureFactorySystem() {furnitureFactoryMap.put(StyleEnum.MODERN, new ModernFurnitureFactory());furnitureFactoryMap.put(StyleEnum.CLASSIC, new ClassicFurnitureFactory());furnitureFactoryMap.put(StyleEnum.xxx, new xxxFurnitureFactory());
}public void produce(String type) {FurnitureFactory furnitureFactory = furnitureFactoryMap.get(StyleEnum.of(type));final List<Furniture> furnitures = Arrays.asList(new Chair(), new Sofa(), yyy); // 新增家具 furnitureFactory.createFurniture(furnitures);
}// 新增類
class xxxFurnitureFactory implements FurnitureFactory {@Overridepublic void createFurniture(List<Furniture> furnitures) {for (Furniture furniture : furnitures) {furniture.create(StyleEnum.xxx.getValue());}}
}// 新增類
class yyy implements Furniture {@Overridepublic void create(String type) {System.out.println(type + " " + FurnitureEnum.yyy.getValue());}
}// 新增枚舉
enum StyleEnum {MODERN("modern"), CLASSIC("classical"), xxx;
}// 新增枚舉
enum FurnitureEnum {CHAIR("chair"), SOFA("sofa"), yyy;
}
3.2 什么時候用抽象工廠模式?(怎么用才是合適的?)
- 抽象工廠在實際的項目中相對也不常用,了解即可。
結論源自《設計模式之美》。
- “3.1 我的實現”看似更靈活,但實際上不符合實際需要。
class Chair implements Furniture {@Overridepublic void create(String type) {System.out.println(type + " " + FurnitureEnum.CHAIR.getValue());}
}class Sofa implements Furniture {@Overridepublic void create(String type) {System.out.println(type + " " + FurnitureEnum.SOFA.getValue());}
}
- 實際上,就是需要ModernChair、ModernSofa、ClassicalChair、ClassicalSofa這種對象。
3.3 更好的例子
3.3.1 背景
-
按鈕和復選框,macos和windows,現實就存在4種對象:
- macos按鈕、macos復選框
- windows按鈕、windows復選框
-
很顯然,代碼中就需要4個實體類與之對應。
-
對于應用層,需要操作按鈕和復選框,前提是獲取對應的對象。而不同操作系統,返回的對象應該不同。
- 此時,工廠不止生產一個對象,要同時能生產按鈕對象和復選框對象。由于工廠方法模式的特點是一個工廠就生產一個對象。因此,引入抽象工廠模式。
3.3.2 示例
public class Example {public static void main(String[] args) {GuiFactorySystem.paint("macos");GuiFactorySystem.paint("windows");}
}interface Button {void paint();
}class MacosButton implements Button {public void paint(){System.out.println("MacOS Button");}
}class WindowsButton implements Button {public void paint(){System.out.println("Windows Button");}
}interface Checkbox {void paint();
}class MacosCheckbox implements Checkbox {public void paint(){System.out.println("MacOS Checkbox");}
}class WindowsCheckbox implements Checkbox {public void paint(){System.out.println("Windows Checkbox");}
}// 抽象工廠
interface GuiFactory {Button createButton();Checkbox createCheckbox();
}class MacosGuiFactory implements GuiFactory {public Button createButton(){return new MacosButton();}public Checkbox createCheckbox(){return new MacosCheckbox();}
}class WindowsGuiFactory implements GuiFactory {public Button createButton(){return new WindowsButton();}public Checkbox createCheckbox(){return new WindowsCheckbox();}
}class GuiFactorySystem {private static final Map<String, GuiFactory> guiFactoryMap = new HashMap<>();static {guiFactoryMap.put("macos", new MacosGuiFactory());guiFactoryMap.put("windows", new WindowsGuiFactory());}private GuiFactorySystem() {}public static void paint(String type) {GuiFactory guiFactory = guiFactoryMap.get(type);Button button = guiFactory.createButton();Checkbox checkbox = guiFactory.createCheckbox();button.paint();checkbox.paint();}// 也可以單獨返回button對象,或者checkbox對象
}
- 與工廠方法模式,最大的差別:
// 抽象工廠
interface GuiFactory {Button createButton();Checkbox createCheckbox();
}
- 也不是不能用工廠方法模式實現,那就要4個工廠類(MacosButtonFactory, WindowsButtonFactory, MacosCheckboxFactory, WindowsCheckboxFactory)。而抽象工廠模式只用了2個工廠類(WindowsGuiFactory、MacosGuiFactory)。
- 實際開發中,簡單工廠模式、工廠方法模式用的更多。