MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Java工厂模式的使用场景与实例

2022-08-204.1k 阅读

什么是工厂模式

在软件开发中,对象的创建是一个基础且关键的环节。随着项目规模的扩大,对象创建的逻辑可能变得复杂,而且可能会频繁变化。如果在代码中到处直接使用 new 关键字来创建对象,会导致代码的耦合度增加,维护和扩展变得困难。

工厂模式(Factory Pattern)应运而生,它是一种创建型设计模式,提供了一种创建对象的方式,将对象的创建和使用分离。通过使用工厂模式,我们把对象创建的逻辑封装在一个工厂类中,而不是在客户端代码中直接实例化对象。这样,当对象创建的逻辑发生变化时,只需要修改工厂类,而不需要修改所有使用该对象的客户端代码,从而提高了代码的可维护性和可扩展性。

工厂模式主要有三种类型:简单工厂模式(Simple Factory Pattern)、工厂方法模式(Factory Method Pattern)和抽象工厂模式(Abstract Factory Pattern)。接下来我们分别详细介绍这三种模式及其使用场景,并通过代码示例来加深理解。

简单工厂模式

简单工厂模式并不属于GoF(Gang of Four,即设计模式经典书籍《设计模式 - 可复用的面向对象软件元素》的四位作者)定义的23种设计模式之一,但它是工厂模式的基础。简单工厂模式定义了一个工厂类,用于创建产品对象。工厂类有一个创建产品对象的方法,该方法根据传入的参数决定创建哪种具体的产品对象。

简单工厂模式的结构

  • 工厂类(Creator):负责创建具体产品对象的类,包含创建产品对象的静态方法。
  • 抽象产品类(Product):定义了产品的公共接口,所有具体产品类都必须实现这个接口。
  • 具体产品类(Concrete Product):实现了抽象产品类定义的接口,是工厂类创建的具体对象。

简单工厂模式示例代码

假设我们正在开发一个图形绘制的程序,需要绘制不同类型的图形,如圆形、矩形等。我们可以使用简单工厂模式来创建这些图形对象。

首先,定义抽象产品类 Shape

// 抽象产品类 Shape
public interface Shape {
    void draw();
}

然后,定义具体产品类 CircleRectangle

// 具体产品类 Circle
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

// 具体产品类 Rectangle
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}

接着,定义工厂类 ShapeFactory

// 工厂类 ShapeFactory
public class ShapeFactory {
    public static Shape createShape(String shapeType) {
        if ("circle".equalsIgnoreCase(shapeType)) {
            return new Circle();
        } else if ("rectangle".equalsIgnoreCase(shapeType)) {
            return new Rectangle();
        }
        return null;
    }
}

最后,在客户端代码中使用工厂类来创建图形对象:

public class Client {
    public static void main(String[] args) {
        Shape circle = ShapeFactory.createShape("circle");
        if (circle != null) {
            circle.draw();
        }

        Shape rectangle = ShapeFactory.createShape("rectangle");
        if (rectangle != null) {
            rectangle.draw();
        }
    }
}

上述代码中,ShapeFactory 类的 createShape 方法根据传入的参数创建不同类型的图形对象。客户端代码只需要调用工厂类的方法,而不需要关心具体图形对象的创建细节。

简单工厂模式的使用场景

  1. 对象创建逻辑简单:当创建对象的逻辑比较简单,且创建对象的类型相对固定时,简单工厂模式是一个不错的选择。例如,上述图形绘制的例子中,创建图形的逻辑只是根据类型返回不同的具体图形对象,没有复杂的业务逻辑。
  2. 解耦对象创建和使用:通过将对象创建逻辑封装在工厂类中,使得客户端代码只需要关心如何使用对象,而不需要关心对象的创建过程,降低了代码的耦合度。
  3. 便于集中管理对象创建:如果项目中有多个地方需要创建相同类型的对象,使用简单工厂模式可以将创建逻辑集中在一个工厂类中,便于维护和修改。

简单工厂模式的优缺点

  • 优点
    • 实现简单:简单工厂模式的实现相对简单,对于对象创建逻辑简单的场景,能够快速实现对象的创建和管理。
    • 解耦创建和使用:有效地将对象的创建和使用分离,使得客户端代码更加简洁,同时提高了代码的可维护性。
  • 缺点
    • 不符合开闭原则:如果需要添加新的产品类型,就需要修改工厂类的 createShape 方法,在方法中添加新的判断逻辑。这违背了开闭原则(Open - Closed Principle),即软件实体应该对扩展开放,对修改关闭。
    • 工厂类职责过重:随着产品类型的增加,工厂类的 createShape 方法会变得越来越复杂,包含大量的条件判断,这使得工厂类的维护变得困难。

工厂方法模式

为了解决简单工厂模式不符合开闭原则的问题,工厂方法模式应运而生。工厂方法模式定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个。工厂方法让类把实例化推迟到子类。

工厂方法模式的结构

  • 抽象工厂类(Creator):定义了创建产品对象的抽象方法,由具体工厂子类实现。
  • 抽象产品类(Product):定义了产品的公共接口,所有具体产品类都必须实现这个接口。
  • 具体产品类(Concrete Product):实现了抽象产品类定义的接口,是具体工厂子类创建的对象。
  • 具体工厂类(Concrete Creator):继承自抽象工厂类,实现了创建具体产品对象的方法。

工厂方法模式示例代码

还是以图形绘制程序为例,使用工厂方法模式来实现。

首先,定义抽象产品类 Shape,与简单工厂模式中的定义相同:

// 抽象产品类 Shape
public interface Shape {
    void draw();
}

具体产品类 CircleRectangle 也与简单工厂模式中的定义相同:

// 具体产品类 Circle
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

// 具体产品类 Rectangle
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}

接着,定义抽象工厂类 ShapeFactory

// 抽象工厂类 ShapeFactory
public abstract class ShapeFactory {
    public abstract Shape createShape();
}

然后,定义具体工厂类 CircleFactoryRectangleFactory

// 具体工厂类 CircleFactory
public class CircleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Circle();
    }
}

// 具体工厂类 RectangleFactory
public class RectangleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Rectangle();
    }
}

最后,在客户端代码中使用具体工厂类来创建图形对象:

public class Client {
    public static void main(String[] args) {
        ShapeFactory circleFactory = new CircleFactory();
        Shape circle = circleFactory.createShape();
        if (circle != null) {
            circle.draw();
        }

        ShapeFactory rectangleFactory = new RectangleFactory();
        Shape rectangle = rectangleFactory.createShape();
        if (rectangle != null) {
            rectangle.draw();
        }
    }
}

在上述代码中,ShapeFactory 是抽象工厂类,定义了抽象的 createShape 方法。CircleFactoryRectangleFactory 是具体工厂类,分别实现了创建 CircleRectangle 对象的逻辑。客户端代码通过具体工厂类来创建相应的图形对象。

工厂方法模式的使用场景

  1. 对象创建逻辑复杂且多变:当对象的创建逻辑比较复杂,并且可能会根据不同的业务需求频繁变化时,工厂方法模式可以通过子类来实现不同的创建逻辑,而不需要修改抽象工厂类和客户端代码。例如,在一个电商系统中,创建订单对象的逻辑可能会因为促销活动、会员等级等因素而发生变化,使用工厂方法模式可以很方便地通过子类来实现不同的订单创建逻辑。
  2. 需要根据不同条件创建不同类型对象:如果需要根据不同的条件(如用户角色、系统配置等)创建不同类型的对象,工厂方法模式可以通过不同的具体工厂类来实现。比如,在一个权限管理系统中,根据用户的角色不同,需要创建不同权限的用户对象,就可以使用工厂方法模式来实现。
  3. 遵循开闭原则:当需要添加新的产品类型时,只需要创建一个新的具体工厂类,继承抽象工厂类并实现创建新产品对象的方法,而不需要修改现有的代码。这符合开闭原则,使得系统具有更好的扩展性。

工厂方法模式的优缺点

  • 优点
    • 符合开闭原则:通过引入抽象工厂类和具体工厂子类,当需要添加新的产品类型时,只需要创建新的具体工厂类,而不需要修改现有代码,提高了系统的扩展性。
    • 解耦对象创建和使用:与简单工厂模式一样,工厂方法模式也将对象的创建和使用分离,降低了代码的耦合度,使得客户端代码更加简洁和易于维护。
    • 灵活性高:可以根据不同的业务需求创建不同的具体工厂类,实现不同的对象创建逻辑,具有较高的灵活性。
  • 缺点
    • 工厂子类过多:随着产品类型的增加,需要创建大量的具体工厂类,这会导致代码量增加,系统变得复杂。例如,在一个大型的游戏开发项目中,如果有大量不同类型的游戏角色需要创建,使用工厂方法模式可能会导致具体工厂类的数量过多,增加了代码的管理难度。
    • 增加系统复杂度:相比简单工厂模式,工厂方法模式引入了抽象工厂类和多个具体工厂类,增加了系统的复杂度,对于简单的对象创建场景,可能会显得过于复杂。

抽象工厂模式

抽象工厂模式提供了一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。抽象工厂模式将对象的创建逻辑进一步抽象化,使得工厂可以创建多种类型的相关产品。

抽象工厂模式的结构

  • 抽象工厂类(Abstract Factory):定义了创建一系列产品对象的抽象方法。
  • 具体工厂类(Concrete Factory):实现了抽象工厂类定义的方法,创建具体的产品对象。
  • 抽象产品类(Abstract Product):定义了产品的公共接口,所有具体产品类都必须实现这个接口。
  • 具体产品类(Concrete Product):实现了抽象产品类定义的接口,是具体工厂类创建的对象。

抽象工厂模式示例代码

假设我们正在开发一个跨平台的UI组件库,需要创建不同平台(如Windows和Mac)的按钮(Button)和文本框(TextBox)组件。我们可以使用抽象工厂模式来实现。

首先,定义抽象产品类 ButtonTextBox

// 抽象产品类 Button
public interface Button {
    void render();
}

// 抽象产品类 TextBox
public interface TextBox {
    void render();
}

然后,定义具体产品类 WindowsButtonWindowsTextBoxMacButtonMacTextBox

// 具体产品类 WindowsButton
public class WindowsButton implements Button {
    @Override
    public void render() {
        System.out.println("渲染Windows风格的按钮");
    }
}

// 具体产品类 WindowsTextBox
public class WindowsTextBox implements TextBox {
    @Override
    public void render() {
        System.out.println("渲染Windows风格的文本框");
    }
}

// 具体产品类 MacButton
public class MacButton implements Button {
    @Override
    public void render() {
        System.out.println("渲染Mac风格的按钮");
    }
}

// 具体产品类 MacTextBox
public class MacTextBox implements TextBox {
    @Override
    public void render() {
        System.out.println("渲染Mac风格的文本框");
    }
}

接着,定义抽象工厂类 UIFactory

// 抽象工厂类 UIFactory
public abstract class UIFactory {
    public abstract Button createButton();
    public abstract TextBox createTextBox();
}

再定义具体工厂类 WindowsUIFactoryMacUIFactory

// 具体工厂类 WindowsUIFactory
public class WindowsUIFactory extends UIFactory {
    @Override
    public Button createButton() {
        return new WindowsButton();
    }

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

// 具体工厂类 MacUIFactory
public class MacUIFactory extends UIFactory {
    @Override
    public Button createButton() {
        return new MacButton();
    }

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

最后,在客户端代码中使用抽象工厂模式创建UI组件:

public class Client {
    public static void main(String[] args) {
        // 创建Windows平台的UI组件
        UIFactory windowsFactory = new WindowsUIFactory();
        Button windowsButton = windowsFactory.createButton();
        TextBox windowsTextBox = windowsFactory.createTextBox();
        windowsButton.render();
        windowsTextBox.render();

        // 创建Mac平台的UI组件
        UIFactory macFactory = new MacUIFactory();
        Button macButton = macFactory.createButton();
        TextBox macTextBox = macFactory.createTextBox();
        macButton.render();
        macTextBox.render();
    }
}

在上述代码中,UIFactory 是抽象工厂类,定义了创建 ButtonTextBox 的抽象方法。WindowsUIFactoryMacUIFactory 是具体工厂类,分别创建Windows和Mac平台的 ButtonTextBox 对象。客户端代码通过具体工厂类来创建不同平台的UI组件。

抽象工厂模式的使用场景

  1. 创建一系列相关产品:当需要创建一系列相关或相互依赖的产品对象时,抽象工厂模式非常适用。例如,在开发一个跨平台的应用程序时,需要创建不同平台的UI组件,这些组件之间可能存在一定的关联性,使用抽象工厂模式可以方便地创建出符合特定平台风格的一组UI组件。
  2. 隔离具体产品类:抽象工厂模式将具体产品类的创建封装在具体工厂类中,客户端只需要与抽象工厂类和抽象产品类交互,不需要了解具体产品类的实现细节。这有助于提高代码的可维护性和可扩展性,同时也降低了不同模块之间的耦合度。
  3. 产品族的切换:如果系统需要支持不同的产品族(如不同操作系统的UI组件族),并且可以在运行时切换产品族,抽象工厂模式可以很方便地实现这一需求。通过创建不同的具体工厂类,系统可以根据运行时的条件创建不同产品族的对象。

抽象工厂模式的优缺点

  • 优点
    • 创建相关产品:能够方便地创建一系列相关的产品对象,保证产品之间的兼容性和一致性。
    • 隔离具体产品类:客户端与具体产品类解耦,只依赖抽象工厂类和抽象产品类,提高了代码的可维护性和可扩展性。
    • 支持产品族切换:在运行时可以很容易地切换产品族,通过更换具体工厂类来创建不同风格的产品对象。
  • 缺点
    • 实现复杂:抽象工厂模式的实现相对复杂,需要定义抽象工厂类、具体工厂类、抽象产品类和具体产品类等多个层次的类结构,增加了代码的编写和理解难度。
    • 不符合开闭原则:当需要添加新的产品类型时,不仅需要创建新的具体产品类,还需要修改抽象工厂类和所有具体工厂类,添加创建新产品的方法。这违背了开闭原则,使得系统在添加新功能时不够灵活。

三种工厂模式的比较

  1. 简单工厂模式
    • 优点:实现简单,解耦对象创建和使用,便于集中管理对象创建。
    • 缺点:不符合开闭原则,工厂类职责过重。
    • 适用场景:对象创建逻辑简单,需要解耦对象创建和使用,便于集中管理对象创建的场景。
  2. 工厂方法模式
    • 优点:符合开闭原则,解耦对象创建和使用,灵活性高。
    • 缺点:工厂子类过多,增加系统复杂度。
    • 适用场景:对象创建逻辑复杂且多变,需要根据不同条件创建不同类型对象,遵循开闭原则的场景。
  3. 抽象工厂模式
    • 优点:创建相关产品,隔离具体产品类,支持产品族切换。
    • 缺点:实现复杂,不符合开闭原则。
    • 适用场景:创建一系列相关产品,需要隔离具体产品类,支持产品族切换的场景。

在实际开发中,需要根据具体的业务需求和场景来选择合适的工厂模式。如果对象创建逻辑简单,简单工厂模式可能就足够了;如果对象创建逻辑复杂且需要遵循开闭原则,工厂方法模式可能更合适;如果需要创建一系列相关的产品对象,抽象工厂模式可能是最佳选择。通过合理使用工厂模式,可以提高代码的可维护性、可扩展性和可复用性,使软件系统更加健壮和灵活。