概念

Java 中的工厂模式是设计模式的一种,主要用于管理对象的创建。它帮助将对象的实例化逻辑从使用对象的逻辑中分离出来,使得代码更加模块化,增加了代码的灵活性和可维护性。工厂模式主要有三种变体:简单工厂模式、工厂方法模式和抽象工厂模式。

一、简单工厂模式

简单工厂模式并不是一个真正的设计模式,但是它是工厂方法模式的一个简化版本。它有一个中心化的工厂类,负责创建其他类的实例。客户端通过传递类型信息给工厂,来获取所需的对象实例。简单工厂模式适合产品种类较少且不会频繁增加的场景,在这种情况下,可以通过简单工厂模式简化对象的创建过程,同时保持客户端和具体产品解耦。

1、角色和职责

角色

  1. 工厂类(Factory Class):这是简单工厂模式的核心,负责实现创建所有实例的内部逻辑。工厂类的职责是提供一个创建对象的方法,客户端通过调用这个方法来创建对象。这个方法通常根据传递给它的参数来决定创建哪种类型的对象。
  2. 抽象产品(Abstract Product):这是一个接口或抽象类,定义了产品的公共接口。在具体实现类中,这些接口或抽象类的方法将被实现。
  3. 具体产品(Concrete Product):继承或实现抽象产品的类,工厂类将创建并返回这些具体产品的实例。每个具体产品都会实现抽象产品定义的接口,表示不同类型的产品对象。

职责

  1. 工厂类的职责
  • 根据客户端的请求,决定创建哪种具体产品的实例。
  • 隐藏了创建产品的具体逻辑,客户端不需要知道具体产品类的名字,只需要知道工厂类。
  • 可能包含选择合适产品类并创建其实例的逻辑。
  1. 抽象产品的职责
  • 声明所有具体产品都应实现的接口,定义了产品的规范,确保所有具体产品的一致性。
  1. 抽象产品的职责
  • 实现或继承抽象产品的接口或类,定义具体产品的具体属性和方法。
  • 每个具体产品都将提供不同的实现,这些实现通过工厂类创建并返回给客户端。

2、优点

  • 帮助实现了代码的解耦,客户端不需要直接创建对象,而是通过工厂类来做这件事,减少了客户端和产品对象之间的依赖。
  • 添加新的产品类时,只需要扩展工厂类即可,无需修改已有的客户端代码,提高了系统的可维护性。

3、缺点

  • 当添加新产品时,需要修改工厂类,违反了开闭原则。
  • 工厂类的职责过重,随着产品种类的增加,工厂方法会变得越来越复杂。

4、适用场景

  • 创建的对象数量不多且不会频繁变化。
  • 客户端不关心对象的创建过程。

5、示例1

  1. 定义一个接口或者抽象类(Product):所有具体的产品实现这个接口或继承这个抽象类,这些产品在概念上是相关的,但实现细节不同。
  2. 创建具体的产品类(Concrete Products):实现或继承第一重定义接口或抽象类。每个具体产品都包含特定于产品的实现代码。
  3. 创建工厂(Factory):包含一个或多个方法,这些方法用于根据输入参数的不同,决定创建并返回哪一种具体产品的实例。客户端调用这个方法而不是直接创建产品对象。
  4. 客户端调用工厂类的方法:客户端使用工厂类,而不是直接实例化产品对象。客户端不需要知道如何创建这些对象,只需知道工厂方法的参数。
    假设我们有一个应用,需要根据用户的需求创建不同类型的日志记录器,比如文件日志记录器和数据库日志记录器。

第一步:定义产品接口

public interface Logger {
    void log(String message);
}

第二步:创建具体产品类

public class FileLogger implements Logger {
    public void log(String message) {
        System.out.println("Logging message to a file: " + message);
    }
}

public class DatabaseLogger implements Logger {
    public void log(String message) {
        System.out.println("Logging message to the database: " + message);
    }
}

第三步:创建工厂类

public class LoggerFactory {
    public static Logger getLogger(String type) {
        if (type.equalsIgnoreCase("file")) {
            return new FileLogger();
        } else if (type.equalsIgnoreCase("database")) {
            return new DatabaseLogger();
        } else {
            throw new IllegalArgumentException("Unknown logger type");
        }
    }
}

第四步:客户端调用工厂类的方法

public class Client {
    public static void main(String[] args) {
        Logger logger = LoggerFactory.getLogger("file");
        logger.log("This is a message.");

        logger = LoggerFactory.getLogger("database");
        logger.log("This is another message.");
    }
}

6、示例2

步骤1:创建产品接口和具体产品类,首先定义一个产品接口FileViewer和几个具体的产品类TextViewerImageViewerVideoViewer,每个产品类实现 FileViewer接口。

interface FileViewer {
    void display();
}

class TextViewer implements FileViewer {
    public void display(){
        System.out.println("Display text file...");
    }
}

class ImageViewer implements FileViewer {
    public void display(){
        System.out.println("Display image file...");
    }
}

class VideoViewer implements FileViewer {
    public void display(){
        System.out.println("Display video file...");
    }
}

步骤2:创建一个简单工厂类ViewerFactory,它有一个静态方法 createViewer,根据文件类型返回对应的视图对象。

class ViewerFactory {
    public static FileViewer createViewer(String fileType){
        switch(fileType){
            case: "text"
                return new TextViewer();
            case: "image"
                return new ImageViewer();
            case: "video"
                return new VideoViewer();
            default:
                throw new IllegalArgumentException("Unsupported file type:" + fileType);
        }
    }
}

步骤3:客户端使用工厂类

public class FactoryClient{
    public static void main(String[] args){
        FileViewer viewer = ViewerFactory.createViewer("text");
        viewer.display();
        viewer = ViewerFactory.createViewer("image");
        viewer.display();
    }
}

7、示例3

public interface ICar {
    void drive();
}

public class Bmw implements ICar {
    public void drive(){
        System.out.println("Bmw");
    }
}

public class Audi implements ICar{
    public void drive(){
        // audi
    }
}

public class Benz implements ICar{
    public void drive(){
        // benz
    }
}

public class SimpleFactory{
    private SimpleFactory(){ }

    public static ICar createCar(String carType){
        if("BMW".equalsIgnoreCase(carType)){
            return new Bmw();
        }else if ("Audi".equalsIgnoreCase(carType)){
            return new Audi();
        }else if ("Benz".equalsIgnoreCase(carType)){
            return new Benz();
        }else {
            return null;
        }
    }
}

public class FactoryClient{
    public static void main(String[] args){
        SimpleFactory.create("BMW").drive();
    }
}

8、示例4:枚举

public enum EnumCarFactory{
    BMW{
        @Override
        public ICar create(){
            return new Bmw();
        }
    },
    AUDI{
        @Override
        public ICar create(){
            return new Audi();
        }
    },
    BENZ{
        @Override
        public ICar create(){
            return new Benz();
        }
    }


    // abstarct 修饰,强制每个枚举实现该方法
    public abstarct ICar create();
}
public class FactoryClient{
    public static void main(String[] args){
        EnumCarFactory.AUDI.create().drive();
    }
}

9、示例5:反射

public class EnhancedSimpleFactory{
    private EnhancedSimpleFactory() {}

    public static <T> T create(Class<? extend T> clazz){
        T obj = null;
        try {
            obj = (T) Class.forName(clazz.getName()).newInstance();
        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e){
            e.printStackTrace();
        }
        return obj;
    }
}
public class FactoryClient{
    public static void main(String[] args){
        // 入参类路径也可来自配置文件、数据库等,因此更具灵活性。
       EnhancedSimpleFactory.create(Benz.class).drive()
    }
}

二、工厂方法模式

工厂方法模式是设计模式中的一种,属于创建型模式的一部分。它定义了一个创建对象的接口,但是让实现这个接口的类来决定实例化哪一个类。工厂方法让类的实例化延迟到其子类进行。

1、角色和职责

角色

  1. 抽象产品(Abstract Product):这是一个接口或抽象类,定义了产品的公共接口。所有的产品类都需要实现这个接口,这样的设计使得所有产品类在概念上是相同的。
  2. 具体产品(Concrete Product):实现或继承自抽象产品的类。每个具体产品都会定义具体的业务操作,这些是客户端所期望的功能。
  3. 抽象工厂(Creator):这是一个接口或抽象类,声明了工厂方法,这个方法返回一个抽象产品类型。抽象工厂使得创建对象的实现延迟到其子类中进行。
  4. 具体工厂(Concrete Creator):继承或实现抽象工厂的类。具体工厂负责创建一个或多个具体产品的实例,决定实例化哪个产品类。每个具体工厂都必须实现抽象工厂定义的工厂方法。

职责

  1. 抽象产品(Product)的职责
  • 定义所有具体产品必须实现的接口,确保所有产品类在概念上是一致的,提供了产品实例的通用属性和方法。
  1. 具体产品(Concrete Product)的职责
  • 实现抽象产品接口定义的操作,代表特定实现的产品对象。
  1. 抽象工厂(Creator)的职责
  • 声明工厂方法,返回一个产品实例。工厂方法通常是抽象的,需要子类实现。
  • 可以提供工厂方法的默认实现,返回一个默认的产品对象。
  1. 具体工厂(Concrete Creator)的职责
  • 重写或实现抽象工厂中定义的工厂方法,以创建和返回具体产品的实例。具体工厂知道应当实例化哪一个具体产品类。
  • 直接与客户端代码交互,提供创建产品的具体实现。

2、优点

  • 提供了代码解耦:在工厂方法模式中,客户端不需要知道它所创建实例的类的具体类名,只需知道对应的工厂即可。这有助于系统在不修改客户端代码的情况下引入新的类,提高了系统的灵活性。
  • 扩展性高:添加新的产品类不需要修改已有的工厂类,只需添加相应的具体产品类和对应的具体工厂即可,符合开闭原则。
  • 强制隔离了创建和使用代码:用户只关心产品的接口而不关心具体类的实现,工厂方法也使得用户代码和具体类的实现解耦。
  • 提供了一种扩展的策略,比简单工厂模式更有弹性。

3、缺点

  • 可能导致类的数量增加:每增加一个产品,都需要增加一个具体产品类和一个具体工厂类,这会导致系统类的数量成倍增加,增加了系统的复杂性。
  • 系统的抽象性和理解难度增加:需要引入抽象层,在客户端代码中既包含抽象也包含具体类的操作,对系统结构和抽象层的理解将会更加复杂。

4、使用场景

  • 当一个类不知道它所必须创建的对象的类的时候:在工厂方法模式中,类的实例化延迟到其子类。
  • 当一个类希望由它的子类来指定创建对象的时候:工厂方法使类的实例可以在运行时更加灵活地增加新的类型。
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪个帮助子类是委托者这一信息局部化时

5、示例1

考虑一个日志记录器的例子,我们需要根据不同的场景(例如文件日志记录和数据库日志记录)创建不同类型的日志记录器。

步骤 1: 定义产品接口

public interface Logger {
    void log(String message);
}

步骤 2: 创建具体产品类

public class FileLogger implements Logger {
    public void log(String message) {
        System.out.println("File logger: " + message);
    }
}

public class DatabaseLogger implements Logger {
    public void log(String message) {
        System.out.println("Database logger: " + message);
    }
}

步骤 3: 定义工厂接口

public interface LoggerFactory {
    Logger createLogger();
}

步骤 4: 创建具体工厂类

public class FileLoggerFactory implements LoggerFactory {
    public Logger createLogger() {
        // 可以在这里添加创建FileLogger前的逻辑,如配置加载等
        return new FileLogger();
    }
}

public class DatabaseLoggerFactory implements LoggerFactory {
    public Logger createLogger() {
        // 可以在这里添加创建DatabaseLogger前的逻辑,如配置加载等
        return new DatabaseLogger();
    }
}

步骤 5: 客户端代码

public class FactoryMethodDemo {
    public static void main(String[] args) {
        LoggerFactory factory;
        Logger logger;
        
        // 使用文件日志记录器
        factory = new FileLoggerFactory();
        logger = factory.createLogger();
        logger.log("This is a file log message.");

        // 使用数据库日志记录器
        factory = new DatabaseLoggerFactory();
        logger = factory.createLogger();
        logger.log("This is a database log message.");
    }
}

这个示例中,**LoggerFactory** 是工厂接口,**FileLoggerFactory** **DatabaseLoggerFactory**是具体的工厂类,它们负责创建对应的产品类实例。客户端代码仅与接口 **LoggerFactory** **Logger** 交互,具体使用哪个日志记录器类的决定权交给了具体的工厂类,这样就实现了创建逻辑与使用逻辑的分离,提高了代码的灵活性和扩展性。

三、抽象工厂模式

抽象工厂模式是一种创建型设计模式,它提供了一种方式,可以封装一组具有共同主题的单独的工厂而无需指定它们的具体类。这种模式是围绕一个超级工厂创建其他工厂的概念。该超级工厂又称为其他工厂的工厂。在抽象工厂模式中,接口是负责创建相关对象的工厂,不需要明确指定它们的类。

1、角色和职责

角色

  1. 抽象工厂(Abstract Factory):提供一个接口,用于创建一系列相关或相互依赖的对象,而不需要指定它们具体的类。
  2. 具体工厂(Concrete Factory):实现抽象工厂接口的具体类。每个具体工厂都能够按照工厂接口定义的方式生产一组具体产品。不同的具体工厂对应于不同的产品变体。
  3. 抽象产品(Abstract Product):为一系列产品对象声明一个接口。在抽象接口中声明了所有产品必须实现的操作。
  4. 具体产品(Concrete Product):抽象工厂模式所创建的实际产品对象,每个具体产品都是一个抽象产品的实现。
  5. 客户端(Client):仅使用由抽象工厂和抽象产品提供的接口声明的方法。客户端通过抽象接口操纵实例,从而使得与应用程序的具体类解耦。

职责

  1. 抽象工厂(Abstract Factory)的职责
  • 声明了一组用于创建一系列相关或相互依赖对象的方法,每个方法对应于一种产品。
  1. 具体工厂(Concrete Factory)的职责
  • 实现抽象工厂的方法来创建具体的产品对象。每个具体工厂对应于特定的产品变体,并创建具有特定实现的产品对象。
  1. 抽象产品(Abstract Product)的职责
  • 为一类产品声明接口。在该接口中声明了所有具体产品都必须实现的操作。
  1. 具体产品(Concrete Product)的职责
  • 实现抽象产品接口的具体类。定义了一个将被相应的具体工厂创建的产品对象。
  1. 客户端(Client)的职责
  • 仅调用从抽象工厂和抽象产品类中派生的接口。客户端通过这些接口与工厂创建的对象交互,从而使具体的工厂和产品类与客户端代码解耦。

2、优点

  • 分离接口和实现:客户端代码通过接口操作实例,从而解耦了客户端和具体类的依赖。
  • 增加新的具体工厂和产品族很方便:无需修改现有系统,符合开闭原则,系统的可扩展性好。
  • 强化了程序的抽象层次:抽象工厂模式通过使用产品族,能够更方便地进行替换产品系列,增加了程序的灵活性和可维护性。

3、缺点

  • 难以支持新种类的产品:如果需要添加新产品,则必须修改抽象工厂的接口,这将涉及到抽象工厂类及其所有子类的修改,违背了开闭原则。
  • 增加了系统的抽象性和复杂度:引入了多层接口和类,系统的学习和理解难度增加。

4、适用场景

  • 当系统中的产品有多于一个的产品族,而系统只消费其中某一族的产品时:抽象工厂可以为访问某一产品族的产品提供接口。
  • 当一个产品族中的多个对象被设计成一起工作时,它可以保证客户端始终只使用同一个产品族中的对象。
  • 系统提供一个产品类的库,所有的产品以同样的接口出现,从而使客户端不依赖于实现。

5、示例1

假设我们正在开发一个跨平台的UI库,我们需要为不同的操作系统(如Windows和Mac)创建不同风格的UI元素(如按钮和复选框)。我们将使用抽象工厂模式来解决这个问题。

步骤 1: 创建抽象产品类和具体产品类

public interface Button {
    void paint();
}

public class WinButton implements Button {
    public void paint() {
        System.out.println("Render a button in Windows style");
    }
}

public class MacButton implements Button {
    public void paint() {
        System.out.println("Render a button in MacOS style");
    }
}

public interface Checkbox {
    void paint();
}

public class WinCheckbox implements Checkbox {
    public void paint() {
        System.out.println("Render a checkbox in Windows style");
    }
}

public class MacCheckbox implements Checkbox {
    public void paint() {
        System.out.println("Render a checkbox in MacOS style");
    }
}

步骤 2: 创建抽象工厂类和具体工厂类

public interface GUIFactory {
    Button createButton();
    Checkbox createCheckbox();
}

public class WinFactory implements GUIFactory {
    public Button createButton() {
        return new WinButton();
    }
    
    public Checkbox createCheckbox() {
        return new WinCheckbox();
    }
}

public class MacFactory implements GUIFactory {
    public Button createButton() {
        return new MacButton();
    }
    
    public Checkbox createCheckbox() {
        return new MacCheckbox();
    }
}


步骤 3: 客户端代码

public class Application {
    private Button button;
    private Checkbox checkbox;

    public Application(GUIFactory factory) {
        button = factory.createButton();
        checkbox = factory.createCheckbox();
    }

    public void paint() {
        button.paint();
        checkbox.paint();
    }
}

public class Demo {
    public static void main(String[] args) {
        GUIFactory factory = new WinFactory();
        Application app = new Application(factory);
        app.paint();

        factory = new MacFactory();
        app = new Application(factory);
        app.paint();
    }
}

在这个例子中,GUIFactory是一个抽象工厂,负责定义创建UI元素的接口。WinFactoryMacFactory是具体的工厂,实现了GUIFactory接口,分别创建Windows风格和Mac风格的UI元素。客户端代码依赖于抽象工厂和抽象产品的接口,从而可以在不关心具体产品细节的情况下,使用不同的产品族。这就是抽象工厂模式的强大之处。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐