设计模式之工厂模式

发布于:2024-04-20 ⋅ 阅读:(16) ⋅ 点赞:(0)

工厂模式是一种创建型设计模式,其核心思想是定义一个创建对象的接口(即工厂),让子类决定实例化哪一个类。工厂模式将对象的创建与使用分离,使代码更易于维护和扩展,尤其在需要根据条件动态创建不同类的实例时非常有用。

工厂模式主要有以下三种形态:

简单工厂模式(Simple Factory)

工厂方法模式(Factory Method)

抽象工厂模式(Abstract Factory)

1、简单工厂模式(Simple Factory)

  • 概念:定义一个工厂类,负责创建某一类对象,客户端通过调用工厂类的静态方法来获取所需对象。简单工厂模式封装了对象的创建过程,隐藏了具体产品的类名,使得客户只需关注产品的接口而非创建细节。
  • 优点:减少客户端与具体产品类之间的耦合,新增产品时只需修改工厂类即可。
  • 缺点:当产品种类较多时,工厂类可能会变得庞大且难以维护;违背开闭原则,增加新产品时需要修改工厂类代码。

Java代码示例:

// 定义产品接口
public interface Product {
    void use();
}

// 具体产品A
public class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("Using product A");
    }
}

// 具体产品B
public class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("Using product B");
    }
}

// 工厂类
public class SimpleFactory {
    public static Product createProduct(String type) {
        if ("A".equals(type)) {
            return new ConcreteProductA();
        } else if ("B".equals(type)) {
            return new ConcreteProductB();
        } else {
            throw new IllegalArgumentException("Invalid product type");
        }
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Product productA = SimpleFactory.createProduct("A");
        productA.use();  // 输出:Using product A

        Product productB = SimpleFactory.createProduct("B");
        productB.use();  // 输出:Using product B
    }
}

2、工厂方法模式(Factory Method)

  • 概念:定义一个用于创建对象的抽象工厂接口(或抽象类),并让子类决定实例化哪一个产品类。工厂方法模式将简单工厂的静态方法改为抽象方法,让子类去实现具体的对象创建。
  • 优点:符合开闭原则,新增产品时只需增加新的工厂子类,无需修改原有代码;支持多态,子类可以覆盖基类的方法以提供不同类型的对象。
  • 缺点:类的层级结构可能会变得复杂,特别是产品类型较多时。

Java代码示例:

// 定义产品接口
public interface Product {
    void use();
}

// 具体产品A
public class ConcreteProductA implements Product {
    @Override
    public void use() {
        System.out.println("Using product A");
    }
}

// 具体产品B
public class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("Using product B");
    }
}

// 抽象工厂接口
public interface Factory {
    Product createProduct();
}

// 具体工厂A
public class ConcreteFactoryA implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductA();
    }
}

// 具体工厂B
public class ConcreteFactoryB implements Factory {
    @Override
    public Product createProduct() {
        return new ConcreteProductB();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        Factory factoryA = new ConcreteFactoryA();
        Product productA = factoryA.createProduct();
        productA.use();  // 输出:Using product A

        Factory factoryB = new ConcreteFactoryB();
        Product productB = factoryB.createProduct();
        productB.use();  // 输出:Using product B
    }
}

3、抽象工厂模式(Abstract Factory)

  • 概念:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式为创建一组产品族提供了一个接口,而具体的产品族创建由具体工厂子类实现。
  • 优点:隔离了具体产品类的使用,使得客户端只需与抽象产品和抽象工厂交互;支持产品族的切换,只需改变具体工厂的实例即可。
  • 缺点:增加了系统的复杂性,尤其是产品族中产品类数量较多时;新增产品族时需要修改抽象工厂接口及其实现类。

Java代码示例:

// 定义产品接口
public interface ProductA {
    void useA();
}

public interface ProductB {
    void useB();
}

// 具体产品A1
public class ConcreteProductA1 implements ProductA {
    @Override
    public void useA() {
        System.out.println("Using product A1");
    }
}

// 具体产品B1
public class ConcreteProductB1 implements ProductB {
    @Override
    public void useB() {
        System.out.println("Using product B1");
    }
}

// 具体产品A2
public class ConcreteProductA2 implements ProductA {
    @Override
    public void useA() {
        System.out.println("Using product A2");
    }
}

// 具体产品B2
public class ConcreteProductB2 implements ProductB {
    @Override
    public void useB() {
        System.out.println("Using product B2");
    }
}

// 抽象工厂接口
public interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

// 具体工厂1
public class ConcreteFactory1 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB1();
    }
}

// 具体工厂2
public class ConcreteFactory2 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA2();
    }

    @Override
    public ProductB createProductB() {
        return new ConcreteProductB2();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        AbstractFactory factory1 = new ConcreteFactory1();
        ProductA productA1 = factory1.createProductA();
        productA1.useA();  // 输出:Using product A1
        ProductB productB1 = factory1.createProductB();
        productB1.useB();  // 输出:Using product B1

        AbstractFactory factory2 = new ConcreteFactory2();
        ProductA productA2 = factory2.createProductA();
        productA2.useA();  // 输出:Using product A2
        ProductB productB2 = factory2.createProductB();
        productB2.useB();  // 输出:Using product B2
    }
}

使用中需要注意的问题:

  1. 明确工厂职责

    • 确保工厂类或方法专注于对象创建,避免承担过多业务逻辑,保持其单一职责。如果工厂过于复杂,可能需要进一步抽象或拆分为多个工厂。
    • 对于工厂方法模式和抽象工厂模式,要清晰划分工厂接口与具体工厂类的责任,确保接口的通用性和具体工厂类的可扩展性。
  2. 遵守开闭原则

    • 工厂模式的设计应遵循开闭原则,即对扩展开放,对修改关闭。新增产品时,应通过增加新的工厂类(工厂方法模式)或新的具体工厂子类(抽象工厂模式),而不是修改已有工厂类的代码。
    • 对于简单工厂模式,虽然直接修改工厂类会导致违反开闭原则,但在简单场景下,或者产品种类较少、不易变动时,仍不失为一种实用的选择。如果预计产品种类会频繁增加或变动,建议使用工厂方法或抽象工厂模式。
  3. 处理依赖关系

    • 如果产品类之间存在复杂的依赖关系,或者需要一起创建一组相关对象,应考虑使用抽象工厂模式,它能更好地管理产品族的创建。
    • 对于简单工厂或工厂方法模式,如果产品类的依赖关系比较简单,可以直接在工厂中创建或注入依赖。如果依赖关系较复杂,可能需要配合依赖注入框架或服务定位器模式来管理。
  4. 控制类的个数

    • 工厂模式可能会增加类的数量,特别是在产品种类较多时。应合理规划类结构,避免类爆炸。可以通过模块化、分包等方式组织代码,提高可读性和可维护性。
    • 对于简单工厂模式,如果产品种类过多导致工厂类庞大,可以考虑使用策略模式或枚举类等方式替代部分简单工厂功能。
  5. 考虑使用场景

    • 工厂模式并非所有情况下的首选解决方案。对于简单对象的创建,直接使用构造函数或静态工厂方法可能更为简洁。只有在对象创建逻辑复杂、需要动态决定创建何种对象,或者希望将对象创建细节与使用分离时,才应考虑使用工厂模式。
  6. 测试与调试

    • 工厂模式可能会增加代码的复杂性,影响测试的便利性。应确保工厂类及其创建的产品类具有良好的可测试性,如提供适当的构造函数、依赖注入点或模拟接口,以便于单元测试。
    • 调试时,注意跟踪工厂创建对象的过程,特别是涉及多层工厂或工厂链时,确保对象的创建路径符合预期。
  7. 代码复用与扩展

    • 如果多个工厂有相似的创建逻辑,可以考虑提取公共部分到基类或工具类中,避免代码重复。
    • 在设计工厂接口时,要考虑未来的扩展性,确保接口定义能够适应潜在的新产品类型。
  8. 性能与资源管理

    • 如果工厂模式用于创建资源密集型对象(如数据库连接、文件句柄等),需要考虑资源的有效管理和回收,避免资源泄露。
    • 对于工厂方法模式,如果工厂类实例化成本较高,可以考虑使用单例模式或享元模式来优化。

总结来说,使用工厂模式时,应关注职责划分、开闭原则的遵循、依赖关系管理、类数量控制、使用场景选择、测试与调试的便利性、代码复用与扩展性以及性能与资源管理等方面的问题,以确保模式的有效运用和系统的健壮性。