什麼是抽象工廠
提供了一個用來創建一組相關或相互依賴對象的接口,而不需要指定具體的類。與工廠方法模式相比,抽象工廠模式不僅創建單一對象,還創建一組產品族(即多個相關的產品)。
主要特點:
- 產品族:抽象工廠模式是用來創建一組相關產品的工廠。每個具體工廠負責創建一組具體的產品。
- 解耦:抽象工廠模式能夠讓客戶端在不依賴具體類的情況下創建一組對象。
適用場景:當系統中有多個產品族,而客戶端需要根據具體情況創建不同的產品族時。
工廠 vs 抽象工廠
特徵 |
工廠模式 (Factory Method) |
抽象工廠模式 (Abstract Factory) |
目標 |
創建單一產品 |
創建一組相關的產品 |
重點 |
關注如何創建一個產品 |
關注如何創建一組互相關聯的產品 |
產品數量 |
只創建一個產品類型 |
同時創建多個相關的產品類型 |
擴展性 |
易於擴展單一產品的類型 |
易於擴展產品族,添加新的產品族 |
複雜性 |
相對簡單,適用於需要創建單一產品的情況 |
較複雜,適用於需要創建多個相關產品並且這些產品屬於同一族群的情況 |
客戶端 |
客戶端使用具體工廠來創建單一產品 |
客戶端使用抽象工廠來創建一組相關的產品 |
適用場景 |
當創建一個對象的過程需要封裝,且該對象是可擴展的 |
當系統中有多個產品族,且客戶端需要根據情況選擇創建一個產品族時 |
示例 |
創建不同類型的單一產品(如不同的交通工具:車、船、飛機) |
創建一組相關產品(如不同系列的家具:桌子、椅子、沙發) |
範例
家具有不同的功能,也有不同的風格
重構前
多層次equal 判斷
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| public class FurnitureService {
public void createFurniture(String furnitureType, String style) { if ("chair".equalsIgnoreCase(furnitureType)) { if ("modern".equalsIgnoreCase(style)) { System.out.println("創建現代風格椅子,功能:坐"); } else if ("classic".equalsIgnoreCase(style)) { System.out.println("創建古典風格椅子,功能:坐"); } else if ("industrial".equalsIgnoreCase(style)) { System.out.println("創建工業風格椅子,功能:坐"); } else { throw new RuntimeException("未知的椅子風格:" + style); } } else if ("sofa".equalsIgnoreCase(furnitureType)) { if ("modern".equalsIgnoreCase(style)) { System.out.println("創建現代風格沙發,功能:坐"); } else if ("classic".equalsIgnoreCase(style)) { System.out.println("創建古典風格沙發,功能:躺"); } else if ("industrial".equalsIgnoreCase(style)) { System.out.println("創建工業風格沙發,功能:坐"); } else { throw new RuntimeException("未知的沙發風格:" + style); } } else if ("coffeetable".equalsIgnoreCase(furnitureType)) { if ("modern".equalsIgnoreCase(style)) { System.out.println("創建現代風格咖啡桌,功能:放置咖啡"); } else if ("classic".equalsIgnoreCase(style)) { System.out.println("創建古典風格咖啡桌,功能:放置咖啡"); } else if ("industrial".equalsIgnoreCase(style)) { System.out.println("創建工業風格咖啡桌,功能:放置咖啡"); } else { throw new RuntimeException("未知的咖啡桌風格:" + style); } } else { throw new RuntimeException("未知的家具類型:" + furnitureType); } } }
|
重構開始
圖為 https://refactoring.guru/ 的示意圖,以下code有做變更
定義包含返回訊息的enum
將功能放入enum,並定義家具的enum
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| public enum FurnitureType { CHAIR("椅子", Collections.singletonList(FunctionalityType.SIT)), SOFA("沙發", Arrays.asList(FunctionalityType.SIT, FunctionalityType.LIE_DOWN)), COFFEE_TABLE("咖啡桌", Collections.singletonList(FunctionalityType.PLACE_COFFEE)); private final String name; private final List<FunctionalityType> functionalities; FurnitureType(String name, List<FunctionalityType> functionalities) { this.name = name; this.functionalities = functionalities; } public String getName() { return name; } public List<FunctionalityType> getFunctionalities() { return functionalities; } }
public enum StyleType { MODERN("現代"), CLASSIC("古典"), INDUSTRIAL("工業");
private final String name;
StyleType(String name) { this.name = name; }
public String getName() { return name; } }
public enum FunctionalityType { SIT("坐"), LIE_DOWN("躺"), PLACE_COFFEE("放置咖啡");
private final String action;
FunctionalityType(String action) { this.action = action; }
public String getAction() { return action; } }
|
定義家具、風格和功能的接口
1 2 3 4 5 6 7 8 9 10 11 12 13
| public interface Furniture { void describe(); }
public interface Style { String getStyleName(); }
public interface Functionality { String getFunctionality(); }
|
實現適配器,將家具、風格和功能組合:
由於功能與家具以做配對,使用適配器模式將風格和家具組合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class FurnitureAdapter implements Furniture { private Style style; private FurnitureType furnitureType; public FurnitureAdapter(FurnitureType furnitureType, Style style) { this.furnitureType = furnitureType; this.style = style; }
@Override public void describe() { System.out.print("創建" + style.getStyleName() + "風格的" + furnitureType.getName() + ",功能:"); List<FunctionalityType> functionalities = furnitureType.getFunctionalities(); System.out.println(functionalities.stream() .map(FunctionalityType::getAction) .collect(Collectors.joining("、"))); } }
|
實現風格
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class ModernStyle implements Style { @Override public String getStyleName() { return StyleType.MODERN.getName(); } }
public class ClassicStyle implements Style { @Override public String getStyleName() { return StyleType.CLASSIC.getName(); } }
public class IndustrialStyle implements Style { @Override public String getStyleName() { return StyleType.INDUSTRIAL.getName(); } }
|
實現功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| public class SitFunctionality implements Functionality { @Override public String getFunctionality() { return FunctionalityType.SIT.getAction(); } }
public class LieDownFunctionality implements Functionality { @Override public String getFunctionality() { return FunctionalityType.LIE_DOWN.getAction(); } }
public class PlaceCoffeeFunctionality implements Functionality { @Override public String getFunctionality() { return FunctionalityType.PLACE_COFFEE.getAction(); } }
|
定義抽象工廠
1 2 3 4 5 6 7 8 9 10 11
| public abstract class AbstractFurnitureFactory {
protected abstract Style createStyle();
public Furniture createFurniture(FurnitureType furnitureType) { Style style = createStyle(); return new FurnitureAdapter(furnitureType, style); } }
|
實現具體的工廠
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
|
public class ModernFurnitureFactory extends AbstractFurnitureFactory { @Override protected Style createStyle() { return new ModernStyle(); } }
public class ClassicFurnitureFactory extends AbstractFurnitureFactory { @Override protected Style createStyle() { return new ClassicStyle(); } }
public class IndustrialFurnitureFactory extends AbstractFurnitureFactory { @Override protected Style createStyle() { return new IndustrialStyle(); } }
|
test:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| public class ApiTest {
@Test public void testFurnitureAdapter() { Style style = new ModernStyle(); FurnitureType furnitureType = FurnitureType.CHAIR; Furniture furniture = new FurnitureAdapter(furnitureType, style); furniture.describe(); style = new ClassicStyle(); furnitureType = FurnitureType.SOFA; furniture = new FurnitureAdapter(furnitureType, style); furniture.describe(); style = new IndustrialStyle(); furnitureType = FurnitureType.COFFEE_TABLE; furniture = new FurnitureAdapter(furnitureType, style); furniture.describe(); } }
|
1 2 3
| 創建現代風格的椅子,功能:坐 創建古典風格的沙發,功能:躺 創建工業風格的咖啡桌,功能:放置咖啡
|
圖源/參考資料:
https://refactoring.guru/
重學java設計模式-小博哥