对抽象工厂模式的理解

文章讲述了如何使用抽象工厂模式解决家具工厂订单问题,比较了不同实现方式的灵活性,并讨论了何时以及如何合适地使用抽象工厂模式,以支持风格扩展和新家具的添加。
摘要由CSDN通过智能技术生成

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 {
    @Override
    public void showInfo() {
        System.out.println("modern chair");
    }
}

// 具体古典风格椅子
class ClassicalChair implements Chair {
    @Override
    public void showInfo() {
        System.out.println("classical chair");
    }
}

// 抽象沙发接口
interface Sofa {
    void displayInfo();
}

// 具体现代风格沙发
class ModernSofa implements Sofa {
    @Override
    public void displayInfo() {
        System.out.println("modern sofa");
    }
}

// 具体古典风格沙发
class ClassicalSofa implements Sofa {
    @Override
    public void displayInfo() {
        System.out.println("classical sofa");
    }
}

// 抽象家居工厂接口
interface FurnitureFactory {
    Chair createChair();
    Sofa createSofa();
}

// 具体现代风格家居工厂
class ModernFurnitureFactory implements FurnitureFactory {
    @Override
    public Chair createChair() {
        return new ModernChair();
    }

    @Override
    public Sofa createSofa() {
        return new ModernSofa();
    }
}

// 具体古典风格家居工厂
class ClassicalFurnitureFactory implements FurnitureFactory {
    @Override
    public Chair createChair() {
        return new ClassicalChair();
    }

    @Override
    public 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 {
    @Override
    public void createFurniture(List<Furniture> furnitures) {
        for (Furniture furniture : furnitures) {
            furniture.create(StyleEnum.MODERN.getValue());
        }
    }
}

class ClassicFurnitureFactory implements FurnitureFactory {
    @Override
    public void createFurniture(List<Furniture> furnitures) {
        for (Furniture furniture : furnitures) {
            furniture.create(StyleEnum.CLASSIC.getValue());
        }
    }
}

interface Furniture {
    void create(String type);
}

class Chair implements Furniture {

    @Override
    public void create(String type) {
        System.out.println(type + " " + FurnitureEnum.CHAIR.getValue());
    }
}

class Sofa implements Furniture {

    @Override
    public 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;
            }
        }
        // 如果没有找到匹配的枚举对象,可以抛出一个异常或返回null
        throw 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;
            }
        }
        // 如果没有找到匹配的枚举对象,可以抛出一个异常或返回null
        throw 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 {
    @Override
    public void createFurniture(List<Furniture> furnitures) {
        for (Furniture furniture : furnitures) {
            furniture.create(StyleEnum.xxx.getValue());
        }
    }
}

// 新增类
class yyy implements Furniture {

    @Override
    public 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 {

    @Override
    public void create(String type) {
        System.out.println(type + " " + FurnitureEnum.CHAIR.getValue());
    }
}

class Sofa implements Furniture {

    @Override
    public 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)。
  • 实际开发中,简单工厂模式、工厂方法模式用的更多。
  • 30
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值