🔥 核心
一个工厂能够根据需要产生不同的产品。
如果是根据参数生产产品,则称为简单工厂模式;定义一个创建对象的工厂接口,其子类自己已经决定好了生产哪一种产品,则称为工厂模式;这个子类工厂能生产一套产品。则称为抽象工厂模式。
🙁 问题场景
还记得吗,在上一篇文章里,你开放了一款物流管理系统。
由于这款应用极其受欢迎,陆地运输公司、海路运输公司、航空运输公司都想要与你合作。
于是,你创建了一个运输工厂,工厂根据不同的订单,生产并开始一个个 卡车运输
、 轮船运输
、飞机运输
。
这时,突然有人站出来,说这压根不是严格定义的工厂模式…
🙂 解决方案
这叫作简单工厂模式,并不是严格的工厂模式。
你现在的运输工厂,是根据不同的订单在进行生产,订单即传入的参数。而如果是严格定义的工厂模式,你应该将这个 运输工厂
视为抽象,再创立若干个子工厂—— 卡车运输工厂
、轮船运输工厂
、飞机运输工厂
,每个工厂完成自己固定的工作。
这时,太空公司又打来了电话。
你又从容的按下了接听键。因为你知道,物流管理系统的代码几乎不需要什么大费周章的扩展,你只需要再创立一个 火箭运输工厂
即可!
🌈 有趣的例子
圆形(Circle)
、正方形(Square)
、三角形(Triangle)
均实现了 图形(Shape)
接口,它们是三种产品。
图形工厂(ShapeFactory)
是一个接口,它有三个子类 圆形工厂(CircleFactory)
、正方形工厂(SquareFactory)
、三角形工厂(TriangleFactory)
,它们分别可以生产出自己固定的产品。
三种图形的接口
interface Shape {
void draw();
}
圆形
class Circle implements Shape {
@Override
public void draw() {
System.out.println("我是一个圆形,我正在画出自己 >_<");
}
}
正方形
class Square implements Shape {
@Override
public void draw() {
System.out.println("我是一个正方形,我正在画出自己 >_<");
}
}
三角形
class Triangle implements Shape {
@Override
public void draw() {
System.out.println("我是一个三角形,我正在画出自己 >_<");
}
}
图形工厂接口
interface ShapeFactory {
Shape getShape();
}
圆形工厂
class CircleFactory implements ShapeFactory {
@Override
public Shape getShape() {
return new Circle();
}
}
正方形工厂
class SquareFactory implements ShapeFactory {
@Override
public Shape getShape() {
return new Square();
}
}
三角形工厂
class TriangleFactory implements ShapeFactory {
@Override
public Shape getShape() {
return new Triangle();
}
}
public class FactoryPatternDemo {
public static void main(String[] args) {
// 创建一个圆形工厂
ShapeFactory circleFactory = new CircleFactory();
// 获取圆形
Shape circle1 = circleFactory.getShape();
Shape circle2 = circleFactory.getShape();
Shape circle3 = circleFactory.getShape();
// 创建一个正方形工厂
ShapeFactory squareFactory = new SquareFactory();
// 获取圆形
Shape square1 = squareFactory.getShape();
Shape square2 = squareFactory.getShape();
Shape square3 = squareFactory.getShape();
// 创建一个三角形工厂
ShapeFactory triangleFactory = new TriangleFactory();
// 获取圆形
Shape triangle1 = triangleFactory.getShape();
Shape triangle2 = triangleFactory.getShape();
Shape triangle3 = triangleFactory.getShape();
// 试一试能不能用
new CircleFactory().getShape().draw();
new SquareFactory().getShape().draw();
new TriangleFactory().getShape().draw();
}
}
我是一个圆形,我正在画出自己 >_<
我是一个正方形,我正在画出自己 >_<
我是一个三角形,我正在画出自己 >_<
☘️ 使用场景
◾️当你在编写代码的过程中,如果无法预知对象确切类别及其依赖关系时,可使用工厂方法。
工厂方法将创建产品的代码与实际使用产品的代码分离,从而能在不影响其他代码的情况下扩展产品创建部分代码。
例如, 如果需要向应用中添加一种新产品,你只需要开发新的创建者子类,然后修改工厂方法即可。
◾️如果你希望用户能扩展你软件库或框架的内部组件,可使用工厂方法。
继承可能是扩展软件库或框架默认行为的最简单方法。但是当你使用子类替代标准组件时,框架如何辨识出该子类?
解决方案是将各框架中构造组件的代码集中到单个工厂方法中,并在继承该组件之外允许任何人对该方法进行重写。
让我们看看具体是如何实现的。假设你使用开源 UI 框架编写自己的应用。你希望在应用中使用圆形按钮,但是原框架仅支持矩形按钮。 你可以使用 圆形按钮(RoundButton)
子类来继承标准的 按钮(Button)
类。 但是,你需要告诉 UI框架(UIFramework)
类使用新的子类按钮代替默认按钮。
为了实现这个功能,你可以根据基础框架类开发子类 圆形按钮UI(UIWithRoundButtons)
,并且重写其 创建按钮(createButton)
方法。基类中的该方法返回 按钮
对象,而你开发的子类返回 圆形按钮
对象。 现在,你就可以使用 圆形按钮UI类
代替 UI框架类
。就是这么简单!
◾️如果你希望复用现有对象来节省系统资源,而不是每次都重新创建对象,可使用工厂方法。
在处理大型资源密集型对象(比如数据库连接、文件系统和网络资源)时,你会经常碰到这种资源需求。
让我们思考复用现有对象的方法:
1)首先,你需要创建存储空间来存放所有已经创建的对象。
2)当他人请求一个对象时,程序将在对象池中搜索可用对象。
3)…然后将其返回给客户端代码。
4)如果没有可用对象,程序则创建一个新对象(并将其添加到对象池中)。
这些代码可不少!而且它们必须位于同一处,这样才能确保重复代码不会污染程序。
可能最显而易见,也是最方便的方式,就是将这些代码放置在我们试图重用的对象类的构造函数中。但是从定义上来讲,构造函数始终返回的是新对象,其无法返回现有实例。
因此,你需要有一个既能够创建新对象,又可以重用现有对象的普通方法。这听上去和工厂方法非常相像。
🧊 实现方式
(1)让所有产品都遵循同一接口。该接口必须声明对所有产品都有意义的方法。
(2)在工厂类中添加一个空的工厂方法。该方法的返回类型必须遵循通用的产品接口。
(3)在创建者代码中找到对于产品构造函数的所有引用。将它们依次替换为对于工厂方法的调用,同时将创建产品的代码移入工厂方法。
(4)现在,为工厂方法中的每种产品编写一个工厂子类,然后在子类中重写工厂方法,并将基本方法中的相关创建代码移动到工厂方法中。
(5)如果代码经过上述移动后,基础工厂方法中已经没有任何代码,你可以将其转变为抽象类。如果基础工厂方法中还有其他语句,你可以将其设置为该方法的默认行为。
🎲 优缺点
➕ 你可以避免创建者和具体产品之间的耦合。
➕ 单一职责原则。你可以将产品创建代码放在程序的单一位置,从而使得代码更容易维护。
➕ 开闭原则。无需更改现有客户端代码,你就可以在程序中引入新的产品类型。
➖ 应用工厂模式需要引入许多新的子类,代码可能会变得非常复杂。
🌸 补充
简单工厂模式和工厂模式,最好加以区分,不要混淆哦!