设计模式之结构型设计模式(二):工厂模式 & 抽象工厂模式 & 建造者模式

工厂模式 Factory

1、什么是工厂模式

工厂模式旨在提供一种统一的接口来创建对象,而将具体的对象实例化的过程延迟到子类或者具体实现中。有助于降低客户端代码与被创建对象之间的耦合度,提高代码的灵活性和可维护性。

定义了一个创建对象的接口,但不负责具体对象的实例化。而是将实例化的责任交给它的子类或者具体实现,这种模式包括抽象工厂、工厂方法和简单工厂等不同形式。

2、为什么使用工厂模式

  1. 降低耦合度:工厂模式将客户端代码与具体的类实现分离,降低它们之间的耦合度,客户端只需要知道工厂接口或者抽象类,而无需关心具体的实现细节。
  2. 可扩展性:当需要添加新的产品类时,只需要创建一个新的具体工厂类和产品类,而不需要修改已有的代码,有助于系统的可扩展性,符合开闭原则。
  3. 隐藏实现细节:工厂模式将对象的创建过程封装在工厂类中,客户端无需知道对象的具体创建细节,有助于隐藏实现细节,提高系统的安全性。

3、如何实现工厂模式

简单工厂模式

简单工厂模式是工厂模式的一种简化形式,包含了一个具体工厂类,负责创建产品的对象。

java 复制代码
// 抽象产品类
interface Product {
    void display();
}

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

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

// 简单工厂类
class SimpleFactory {
    public static Product createProduct(String type) {
        switch (type) {
            case "A":
                return new ConcreteProductA();
            case "B":
                return new ConcreteProductB();
            default:
                throw new IllegalArgumentException("Invalid product type");
        }
    }
}

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

        Product productB = SimpleFactory.createProduct("B");
        productB.display();  // Output: Product B
    }
}
工厂方法模式

工厂方法模式定义了一个创建产品的接口,具体的产品创建由其子类负责实现。

java 复制代码
// 抽象产品类
interface Product {
    void display();
}

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

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

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

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

// 具体工厂类B
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.display();  // Output: Product A

        Factory factoryB = new ConcreteFactoryB();
        Product productB = factoryB.createProduct();
        productB.display();  // Output: Product B
    }
}

4、是否存在缺陷和不足

  1. 类爆炸:随着产品类的增加,工厂类的数量也会呈现指数级增长,导致类的爆炸,不利于系统的维护。
  2. 违背开闭原则:每次添加新产品都需要修改工厂类,违背了开闭原则,当有新产品加入时,必须修改所有工厂类。

5、如何缓解缺陷与不足

  1. 使用抽象工厂模式:抽象工厂模式将一组相关的产品封装在一起,形成一个产品族,每个具体工厂负责创建一族产品,缓解了类的爆炸问题,
  2. 依赖注入:将工厂的创建过程交给外部来管理,通过依赖注入的方式,避免了工厂类的频繁修改。
  3. 使用反射:可以使用反射机制,动态地创建产品对象,从而减少了工厂类的数量。

通过以上缓解措施,可以在一定程度上提高工厂模式的灵活性和可维护性,使其更好地适应变化。在实际应用中,根据具体场景选择合适的工厂的模式,并结合其他设计模式,以达到代码的清晰和可扩展。

抽象工厂模式 Abstract Factory

1、什么是抽象工厂模式

抽象工厂模式提供了一个接口,用于创建与产品家族相关的对象,无需制定具体类,有助于确保创建的对象能够相互配合使用,而无需指定具体的类。

抽象工厂模式提供了一种将一组相关的产品组合成一个家族的方式,而不必指定具体的类,通过引入抽象工厂接口和一组具体工厂类,为每个产品提供一个独立的工厂,从而使系统更具灵活性。

2、为什么用抽象工厂模式

  1. 产品家族一致性:抽象工厂模式确保创建的对象相互之间是兼容的,属于同一产品家族,有助于保持系统的一致性。
  2. 易于替换:由于客户端只依赖于抽象接口,而不直接依赖具体类,因此可以轻松替换整个产品家族的实现,而无需修改客户端代码。
  3. 隐藏实现细节:客户端无需知道具体产品的实现细节,只需要了解抽象工厂接口,从降低了系统的复杂度。

3、如何实现抽象工厂模式

示例:图形界面库的抽象工厂模式,包含图形界面库中的按钮和文本框。

java 复制代码
// 抽象按钮接口
interface Button {
    void display();
}

// 具体按钮A
class ButtonA implements Button {
    @Override
    public void display() {
        System.out.println("Button A");
    }
}

// 具体按钮B
class ButtonB implements Button {
    @Override
    public void display() {
        System.out.println("Button B");
    }
}

// 抽象文本框接口
interface TextBox {
    void display();
}

// 具体文本框A
class TextBoxA implements TextBox {
    @Override
    public void display() {
        System.out.println("TextBox A");
    }
}

// 具体文本框B
class TextBoxB implements TextBox {
    @Override
    public void display() {
        System.out.println("TextBox B");
    }
}

// 抽象工厂接口
interface GUIFactory {
    Button createButton();
    TextBox createTextBox();
}

// 具体工厂A
class GUIFactoryA implements GUIFactory {
    @Override
    public Button createButton() {
        return new ButtonA();
    }

    @Override
    public TextBox createTextBox() {
        return new TextBoxA();
    }
}

// 具体工厂B
class GUIFactoryB implements GUIFactory {
    @Override
    public Button createButton() {
        return new ButtonB();
    }

    @Override
    public TextBox createTextBox() {
        return new TextBoxB();
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 使用工厂A创建按钮和文本框
        GUIFactory factoryA = new GUIFactoryA();
        Button buttonA = factoryA.createButton();
        TextBox textBoxA = factoryA.createTextBox();

        buttonA.display();  // Output: Button A
        textBoxA.display(); // Output: TextBox A

        // 使用工厂B创建按钮和文本框
        GUIFactory factoryB = new GUIFactoryB();
        Button buttonB = factoryB.createButton();
        TextBox textBoxB = factoryB.createTextBox();

        buttonB.display();  // Output: Button B
        textBoxB.display(); // Output: TextBox B
    }
}

4、是否存在缺陷和不足

  1. 不易扩展新的产品家族:当需要添加新的产品家族时,需要修改抽象工厂接口及其所有的实现类,违背了开闭原则,使得系统扩展性受限。
  2. 复杂性增加:随着产品家族的增加,抽象工厂模式的类和接口数量可能呈现指数级增长,导致系统复杂性增加。

5、如何缓解缺陷和不足

  1. 使用依赖注入:将工厂的创建过程交给外部来管理,通过依赖注入的方式,避免了工厂类的频繁修改。
  2. 使用反射:可以使用反射机制,动态地创建产品对象,从而减少了工厂类和产品类的数量。
  3. 使用配置文件:将产品家族的配置信息放置在配置文件中,通过读取配置文件的方式动态创建工厂和产品对象,提高了系统的灵活性。

建造者模式 Builder

1、什么是建造者模式

建造者模式旨在通过将复杂对象的构造过程分离成多个简单的步骤,使得同样的创建过程可以创建不同的表示,有助于客户端代码能够根据需求选择构建过程的不同组合,以创建不同属性的对象。

将一个复杂对象的构建与其表示分离,使得同样的创建过程可以创建不同的表示,主要包含以下角色:

  • 产品
  • 抽象建造者
  • 具体建造者
  • 指挥者

2、为什么用建造者模式

  1. 分布创建:建造者模式允许按照步骤构建一个复杂对象,使得客户端代码可以选择性地构建对象的不同部分,灵活性更高。
  2. 隔离复杂性:将构建过程在具体的建造者中,客户端无需关心构建的细节,从而降低了系统的复杂性。
  3. 可扩展性:可以通过增加新的具体的建造者类来扩展系统,而不影响已有的客户端代码。

3、如何实现建造者模式

设计实现一个电脑组装的建造者模式

java 复制代码
// 产品类
class Computer {
    private String cpu;
    private String memory;
    private String storage;

    public Computer(String cpu, String memory, String storage) {
        this.cpu = cpu;
        this.memory = memory;
        this.storage = storage;
    }

    // Getters...
    
    public void display() {
        System.out.println("Computer Specs: CPU-" + cpu + ", Memory-" + memory + ", Storage-" + storage);
    }
}

// 抽象建造者
interface ComputerBuilder {
    void buildCPU(String cpu);
    void buildMemory(String memory);
    void buildStorage(String storage);
    Computer getResult();
}

// 具体建造者A
class ConcreteBuilderA implements ComputerBuilder {
    private Computer computer;

    public ConcreteBuilderA() {
        this.computer = new Computer("", "", "");
    }

    @Override
    public void buildCPU(String cpu) {
        computer = new Computer(cpu, computer.getMemory(), computer.getStorage());
    }

    @Override
    public void buildMemory(String memory) {
        computer = new Computer(computer.getCpu(), memory, computer.getStorage());
    }

    @Override
    public void buildStorage(String storage) {
        computer = new Computer(computer.getCpu(), computer.getMemory(), storage);
    }

    @Override
    public Computer getResult() {
        return computer;
    }
}

// 具体建造者B
class ConcreteBuilderB implements ComputerBuilder {
    private Computer computer;

    public ConcreteBuilderB() {
        this.computer = new Computer("", "", "");
    }

    @Override
    public void buildCPU(String cpu) {
        computer = new Computer(cpu, computer.getMemory(), computer.getStorage());
    }

    @Override
    public void buildMemory(String memory) {
        computer = new Computer(computer.getCpu(), memory, computer.getStorage());
    }

    @Override
    public void buildStorage(String storage) {
        computer = new Computer(computer.getCpu(), computer.getMemory(), storage);
    }

    @Override
    public Computer getResult() {
        return computer;
    }
}

// 指挥者
class Director {
    public void construct(ComputerBuilder builder) {
        builder.buildCPU("Intel i5");
        builder.buildMemory("8GB");
        builder.buildStorage("256GB SSD");
    }
}

// 客户端代码
public class Client {
    public static void main(String[] args) {
        // 使用建造者A构建电脑
        ComputerBuilder builderA = new ConcreteBuilderA();
        Director director = new Director();
        director.construct(builderA);
        Computer computerA = builderA.getResult();
        computerA.display();

        // 使用建造者B构建电脑
        ComputerBuilder builderB = new ConcreteBuilderB();
        director.construct(builderB);
        Computer computerB = builderB.getResult();
        computerB.display();
    }
}

4、是否存在缺陷和不足

  1. 指挥者的变动:如果产品的构建步骤发生变化,指挥者类的代码也需要修改,违背了开闭原则。
  2. 不够灵活:当产品的构建步骤很多且相互关联时,建造者模式可能变得复杂且不够灵活。

5、如何缓解缺陷和不足

  1. 使用链式调用:在具体建造者中使用链式调用,使得客户端代码更加简洁,且不容易受到构建步骤变动的影响。
  2. 增加产品的变种:当产品的构建步骤较为复杂时,可以考虑增加产品的变种,以适应不同的构建需求。
  3. 使用反射和配置文件:可以通过反射机制和配置文件来动态配置产品的构建过程,提高系统的灵活性。
相关推荐
李广坤11 小时前
状态模式(State Pattern)
设计模式
李广坤13 小时前
观察者模式(Observer Pattern)
设计模式
李广坤13 小时前
中介者模式(Mediator Pattern)
设计模式
李广坤14 小时前
迭代器模式(Iterator Pattern)
设计模式
李广坤14 小时前
解释器模式(Interpreter Pattern)
设计模式
阿无,17 小时前
java23种设计模式之前言
设计模式
Asort18 小时前
JavaScript设计模式(八):组合模式(Composite)——构建灵活可扩展的树形对象结构
前端·javascript·设计模式
数据智能老司机18 小时前
数据工程设计模式——数据基础
大数据·设计模式·架构
笨手笨脚の20 小时前
设计模式-代理模式
设计模式·代理模式·aop·动态代理·结构型设计模式
Overboom1 天前
[C++] --- 常用设计模式
开发语言·c++·设计模式