【工厂方法模式】

70 阅读5分钟

简介

  1. 工厂方法模式是一种创建对象的设计模式
  2. 定义了一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到其子类
  3. 符合开闭原则

工厂方法模式包含以下几个主要角色:

  1. 抽象工厂(Creator) :提供一个创建产品对象的接口,由具体工厂子类实现。
  2. 具体工厂(Concrete Creator) :实现抽象工厂中的抽象方法,负责创建具体的产品对象。
  3. 抽象产品(Product) :定义产品的公共接口,所有具体产品类都实现这个接口。
  4. 具体产品(Concrete Product) :实现抽象产品接口的具体类,代表不同类型的产品对象。

总结

工厂方法模式适用于以下场景:

  • 当一个类不知道它所必须创建的对象的类的时候。
  • 当一个类希望由它的子类来指定它所创建的对象的时候。
  • 当类将创建对象的职责委托给多个帮助子类中的某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候。

业务

假设我们正在开发一个图形绘制软件,其中有不同类型的图形,如圆形、矩形和三角形。我们可以使用工厂方法模式来创建这些图形对象。

类图

classDiagram
    class Shape {
    <<interface>>
        +draw()
    }
    class Circle {
        +draw()
    }
    class Rectangle {
        +draw()
    }
    class Triangle {
        +draw()
    }
    class ShapeFactory {
    <<Abstract>>
        +createShape(): Shape
    }
    Shape <|.. Circle
    Shape <|.. Rectangle
    Shape <|.. Triangle
    CircleFactory --|> ShapeFactory
    RectangleFactory --|> ShapeFactory
    TriangleFactory --|> ShapeFactory
    Circle <-- CircleFactory
    Rectangle <-- RectangleFactory
    Triangle <-- TriangleFactory

代码

首先,定义一个图形接口:

public interface Shape {
    void draw();
}

然后,实现不同的图形类:

public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制圆形");
    }
}

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制矩形");
    }
}

public class Triangle implements Shape {
    @Override
    public void draw() {
        System.out.println("绘制三角形");
    }
}

接下来,定义一个抽象工厂类:

public abstract class ShapeFactory {
    public abstract Shape createShape();
}

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

public class CircleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Circle();
    }
}

public class RectangleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Rectangle();
    }
}

public class TriangleFactory extends ShapeFactory {
    @Override
    public Shape createShape() {
        return new Triangle();
    }
}

在业务代码中,可以使用工厂方法来创建图形对象:

public class Main {
    public static void main(String[] args) {
        ShapeFactory circleFactory = new CircleFactory();
        Shape circle = circleFactory.createShape();
        circle.draw();

        ShapeFactory rectangleFactory = new RectangleFactory();
        Shape rectangle = rectangleFactory.createShape();
        rectangle.draw();

        ShapeFactory triangleFactory = new TriangleFactory();
        Shape triangle = triangleFactory.createShape();
        triangle.draw();
    }
}

在简单工厂的基础上,加了一层抽象工程,使用里氏替换原则,保证了开闭原则

优点

  1. 符合开闭原则。如果需要添加新的图形类型,只需要创建一个新的具体图形类和对应的具体工厂类,而不需要修改现有代码。
  2. 代码结构更加清晰。将图形的创建和使用分离,使得代码的职责更加明确。

缺点

  1. 增加了系统的复杂性。由于引入了抽象工厂和具体工厂,代码结构相对复杂一些。

在框架中的使用

java

Java 的 JDBC(Java Database Connectivity)中,DriverManager类就使用了工厂方法模式来创建数据库连接。

以下是一个简单的示例代码:

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class JDBCTest {
    public static void main(String[] args) {
        try {
            // 使用工厂方法模式创建数据库连接
            Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydb", "username", "password");
            // 使用连接进行数据库操作
            //...
            connection.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,DriverManagergetConnection方法就是一个工厂方法,它根据不同的数据库 URL、用户名和密码等参数,创建不同数据库的连接对象。不同的数据库厂商实现了java.sql.Driver接口,这些实现类就相当于具体的工厂,负责创建特定数据库的连接对象。

工厂方法模式在这个场景中的优点:

  • 解耦了应用程序和具体的数据库实现。应用程序只需要通过标准的接口来获取数据库连接,而不需要关心具体的数据库实现细节。
  • 易于扩展。如果要支持新的数据库,只需要实现Driver接口,并在应用程序中注册该驱动,就可以通过DriverManager来获取连接,而不需要修改应用程序的核心代码。

spring

例如,在创建 Bean 的过程中,Spring 允许通过自定义的工厂方法来创建对象。假设你有一个复杂的对象创建过程,不想直接在 XML 配置文件中实例化对象,而是通过一个特定的方法来创建。可以这样做:

  1. 定义一个接口和实现类:
public interface MyService {
    void doSomething();
}

public class MyServiceImpl implements MyService {
    @Override
    public void doSomething() {
        System.out.println("执行 MyService 的方法。");
    }
}
  1. 创建一个工厂类:
public class MyServiceFactory {
    public static MyService createMyService() {
        return new MyServiceImpl();
    }
}
  1. 在 Spring 的配置文件中使用工厂方法创建 Bean:
<bean id="myService" factory-method="createMyService" class="com.example.MyServiceFactory"/>

在这个例子中,MyServiceFactorycreateMyService方法就是一个工厂方法,Spring 通过调用这个方法来创建MyService的实例。

工厂方法模式在 Spring 中的优点:

  • 提供了一种灵活的方式来创建对象,可以处理复杂的对象创建逻辑。
  • 可以将对象的创建逻辑封装在工厂类中,使得配置文件更加简洁和易于维护。
  • 符合开闭原则,当需要创建新的对象类型时,可以通过添加新的工厂方法来实现,而不需要修改现有的代码。

mybatis

例如,在创建SqlSession对象时,SqlSessionFactory接口定义了创建SqlSession的方法,而不同的SqlSessionFactory实现类(如DefaultSqlSessionFactory)就是具体的工厂,负责创建SqlSession对象。

以下是一个简单的示例代码展示其使用:

import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;

public class MyBatisFactoryMethodExample {
    public static void main(String[] args) {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

            // 使用工厂方法创建 SqlSession 对象
            SqlSession sqlSession = sqlSessionFactory.openSession();

            // 使用 SqlSession 进行数据库操作
            //...

            sqlSession.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,SqlSessionFactory就像是一个抽象工厂,不同的实现类作为具体工厂,通过openSession()方法这个工厂方法来创建SqlSession对象。

工厂方法模式在这个场景中的优点:

  • 解耦了SqlSession的创建和使用,使用者不需要了解SqlSession的具体创建过程,只需要从工厂获取即可。
  • 方便进行扩展和定制,如果需要对SqlSession的创建过程进行特殊处理,可以通过实现自己的SqlSessionFactory来实现,而不影响现有的代码。

总结

总的来说,工厂方法模式适用于创建对象的逻辑比较复杂,且需要满足开闭原则的场景。