Java设计模式:工厂模式之简单工厂、工厂方法、抽象工厂(三)_设计模式


码到三十五 :

个人主页



心中有诗画,指尖舞代码,目光览世界,步履越千山,人间尽值得 !



本文将详细介绍Java中工厂模式的多种实现方式,包括简单工厂模式、工厂方法模式和抽象工厂模式。我们将通过示例代码和解释来阐述每种工厂模式的特点、使用场景以及优缺点,帮助读者更好地理解和应用这些设计模式。

[参见]:

Java设计模式:核心概述(一)

Java设计模式:单例模式之六种实现方式详解(二)


目录

  • 一、核心概念
  • 工厂模式的优点
  • Java中工厂模式主要三种形态
  • 二、简单工厂模式
  • 优缺点分析
  • 三、工厂方法模式
  • 优缺点分析
  • 四、抽象工厂模式
  • 总结


一、核心概念

在软件设计中,工厂模式是一种常见的设计模式,它提供了一种创建对象的最佳方式。通过工厂模式,我们可以将对象的创建逻辑与使用逻辑分离,降低代码的耦合度,提高系统的可扩展性和可维护性。

Java中的工厂模式是一种创建型设计模式,它提供了一种创建对象的最佳方式。在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

工厂模式的优点

  1. 解耦:将对象的创建和使用过程分开,使得客户端代码无需知道具体对象的创建过程,只需通过工厂获取对象即可。
  2. 降低代码重复:多个地方需要使用对象时,只需调用工厂方法即可,避免了代码的重复。
  3. 减少错误:由于对象的创建过程由工厂统一管理,因此可以减少因客户端错误创建对象而导致的错误。

Java中工厂模式主要三种形态

  1. 简单工厂模式(又称静态工厂方法):定义一个类来负责其他类的实例化,被创建的实例通常具有共同的父类或接口。客户端只需要传入相应的参数,即可获得所需的对象,而无需知道其具体类名。但是,当系统需要引入新的产品时,可能需要修改工厂类的代码,这违反了开闭原则。
  2. 工厂方法模式:定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类。客户端针对抽象工厂及抽象产品编程,产品的具体工厂和产品由具体工厂子类实现。这样,当系统需要引入新的产品时,只需增加相应的具体工厂和产品类即可,符合开闭原则。
  3. 抽象工厂模式:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。客户端使用一个抽象的工厂接口来创建产品,而具体的工厂类负责生成具体的产品。这样可以将简单工厂模式和工厂方法模式进行整合,使得产品的创建更加灵活和可扩展。

二、简单工厂模式

简单工厂模式是最简单的工厂模式,它通过一个专门的类来负责创建其他类的实例,而不需要明确指定具体将要创建的类。这个专门的类称为“简单工厂类”或“工厂类”。

在Java中,简单工厂模式是一种创建型设计模式,它由一个单独的类(称为工厂类)负责创建其他类的实例,而无需将这些类的实例化逻辑暴露给客户端。

简单工厂模式通常用于创建具有共同父类或接口的对象的场景。

下面是一个简单工厂模式的Java实现示例:

首先,定义一个产品接口或抽象类:

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

然后,实现具体产品类:

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

// 具体产品B
public class ConcreteProductB implements Product {
    @Override
    public void use() {
        System.out.println("使用具体产品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("不支持的产品类型: " + type);
        }
    }
}

最后,客户端代码可以这样使用简单工厂:

public class Client {
    public static void main(String[] args) {
        // 通过简单工厂创建产品A
        Product productA = SimpleFactory.createProduct("A");
        productA.use(); // 输出:使用具体产品A

        // 通过简单工厂创建产品B
        Product productB = SimpleFactory.createProduct("B");
        productB.use(); // 输出:使用具体产品B

        // 尝试创建不存在的产品类型将抛出异常
        // Product productC = SimpleFactory.createProduct("C");
    }
}

SimpleFactory类包含一个静态方法createProduct,它根据传入的类型字符串创建并返回相应的产品实例。客户端代码通过调用这个静态工厂方法来获取产品实例,而无需直接与具体的产品类交互。

优缺点分析

  • 简单工厂模式的优点在于它将对象的创建逻辑集中在一个单独的工厂类中,从而减少了客户端与具体类之间的耦合;
  • 同时简单易懂,易于实现。
  • 然而,它的一个缺点是当需要引入新的产品类型时,可能需要修改工厂类的代码,这违反了开闭原则(即对扩展开放,对修改封闭)
  • 此外,简单工厂模式还可能导致系统结构变得复杂和难以维护。

在实际应用中,如果需要频繁添加新的产品类型,可能会更倾向于使用抽象工厂模式或其他更灵活的工厂模式。

三、工厂方法模式

工厂方法模式是对简单工厂模式的进一步抽象化。它将对象的创建延迟到子类中进行,从而实现了更好的扩展性。在工厂方法模式中,抽象工厂类负责定义创建产品对象的接口,而具体工厂类则负责实现具体的产品创建逻辑。

下面是一个工厂方法模式的Java实现示例:

首先,定义一个产品接口:

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

然后,实现具体产品类:

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

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

接下来,创建抽象工厂类,并定义工厂方法:

// 抽象工厂类
public abstract class Creator {
    // 工厂方法,声明为抽象方法,由子类实现
    public abstract Product factoryMethod();

    // 一个通用的方法,使用工厂方法来创建产品
    public final Product createProduct() {
        Product product = factoryMethod();
        // 这里可以添加一些额外的逻辑,比如初始化产品等
        return product;
    }
}

然后,实现具体的工厂子类:

// 具体工厂子类A,用于创建ConcreteProductA
public class ConcreteCreatorA extends Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductA();
    }
}

// 具体工厂子类B,用于创建ConcreteProductB
public class ConcreteCreatorB extends Creator {
    @Override
    public Product factoryMethod() {
        return new ConcreteProductB();
    }
}

最后,客户端代码可以这样使用工厂方法模式:

public class Client {
    public static void main(String[] args) {
        // 使用具体工厂子类A来创建产品A
        Creator creatorA = new ConcreteCreatorA();
        Product productA = creatorA.createProduct();
        productA.use(); // 输出:使用具体产品A

        // 使用具体工厂子类B来创建产品B
        Creator creatorB = new ConcreteCreatorB();
        Product productB = creatorB.createProduct();
        productB.use(); // 输出:使用具体产品B
    }
}

例子中,Creator是一个抽象工厂类,它声明了一个抽象方法factoryMethod(),这个方法由具体的工厂子类(ConcreteCreatorAConcreteCreatorB)来实现。客户端代码通过创建具体工厂类的实例并调用createProduct()方法来创建产品。每个具体工厂类都知道如何创建和返回一种具体的产品。

优缺点分析

  • 工厂方法模式的优点在于它允许系统在不修改具体工厂类的情况下引进新的产品,这符合开闭原则。
  • 此外,它还提供了一种将产品类与客户端代码解耦的机制,因为客户端代码只依赖于抽象产品和抽象工厂,而不是具体实现。
  • 同时,它也降低了代码的耦合度,提高了系统的可扩展性和可维护性。
  • 但是,与简单工厂模式相比,工厂方法模式需要定义更多的类,增加了系统的复杂度。

四、抽象工厂模式

抽象工厂模式是对工厂方法模式的进一步抽象化。它提供了一组用于创建一系列相关或互相依赖对象的接口,而不需要指定它们具体的类。在抽象工厂模式中,抽象工厂类负责定义创建产品家族的接口,而具体工厂类则负责实现具体的产品家族创建逻辑。产品家族是指位于不同产品等级结构中,功能相关联的产品组成的家族。

以下是Java中实现抽象工厂模式的一种方式:

首先,定义产品接口:

// 产品A的接口
public interface ProductA {
    void use();
}

// 产品B的接口
public interface ProductB {
    void use();
}

然后,实现具体产品:

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

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

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

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

接下来,定义抽象工厂接口:

public interface AbstractFactory {
    ProductA createProductA();
    ProductB createProductB();
}

然后,实现具体工厂:

// 具体工厂1,负责创建产品A1和产品B1
public class ConcreteFactory1 implements AbstractFactory {
    @Override
    public ProductA createProductA() {
        return new ConcreteProductA1();
    }

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

// 具体工厂2,负责创建产品A2和产品B2
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) {
        // 使用具体工厂1来创建产品
        AbstractFactory factory1 = new ConcreteFactory1();
        ProductA productA1 = factory1.createProductA();
        ProductB productB1 = factory1.createProductB();
        productA1.use(); // 输出:使用具体产品A1
        productB1.use(); // 输出:使用具体产品B1

        // 使用具体工厂2来创建产品
        AbstractFactory factory2 = new ConcreteFactory2();
        ProductA productA2 = factory2.createProductA();
        ProductB productB2 = factory2.createProductB();
        productA2.use(); // 输出:使用具体产品A2
        productB2.use(); // 输出:使用具体产品B2
    }
}

例子中,AbstractFactory接口定义了两个创建产品的方法,createProductA()createProductB()ConcreteFactory1ConcreteFactory2分别实现了这些方法以创建不同的具体产品。客户端代码通过具体的工厂来创建和使用产品,而不需要直接与具体产品类打交道。这种方式提供了更好的灵活性和可扩展性,因为你可以在不修改客户端代码的情况下增加新的工厂和产品。

总结

在实际开发中,抽象工厂模式的实现可能更加复杂和庞大。为了保持代码的清晰和可维护性,我们需要合理地划分产品等级结构和产品家族,并遵循单一职责原则和开闭原则进行设计。同时,在实际应用中,我们可以根据具体需求选择使用简单工厂模式、工厂方法模式或抽象工厂模式中的任意一种或多种组合使用以满足需求。例如,在某些情况下,我们可以将简单工厂模式与工厂方法模式结合使用以简化代码结构并提高灵活性;而在其他情况下,我们可能需要使用抽象工厂模式来处理更复杂的对象创建场景。