💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
<!-- Factories: Encapsulating Object Creation --> ## 工厂模式 当你发现必须将新类型添加到系统中时,合理的第一步是使用多态性为这些新类型创建一个通用接口。这会将你系统中的其余代码与要添加的特定类型的信息分开,使得可以在不改变现有代码的情况下添加新类型……或者看起来如此。起初,在这种设计中,似乎你必须更改代码的唯一地方就是你继承新类型的地方,但这并不是完全正确的。 你仍然必须创建新类型的对象,并且在创建时必须指定要使用的确切构造器。因此,如果创建对象的代码分布在整个应用程序中,那么在添加新类型时,你将遇到相同的问题——你仍然必须追查你代码中新类型碍事的所有地方。恰好是类型的创建碍事,而不是类型的使用(通过多态处理),但是效果是一样的:添加新类型可能会引起问题。 解决方案是强制对象的创建都通过通用工厂进行,而不是允许创建代码在整个系统中传播。 如果你程序中的所有代码都必须执行通过该工厂创建你的一个对象,那么在添加新类时只需要修改工厂即可。 由于每个面向对象的程序都会创建对象,并且很可能会通过添加新类型来扩展程序,因此工厂是最通用的设计模式之一。 举例来说,让我们重新看一下**Shape**系统。 首先,我们需要一个用于所有示例的基本框架。 如果无法创建**Shape**对象,则需要抛出一个合适的异常: ```java // patterns/shapes/BadShapeCreation.java package patterns.shapes; public class BadShapeCreation extends RuntimeException { public BadShapeCreation(String msg) { super(msg); } } ``` 接下来,是一个**Shape**基类: ```java // patterns/shapes/Shape.java package patterns.shapes; public class Shape { private static int counter = 0; private int id = counter++; @Override public String toString(){ return getClass().getSimpleName() + "[" + id + "]"; } public void draw() { System.out.println(this + " draw"); } public void erase() { System.out.println(this + " erase"); } } ``` 该类自动为每一个**Shape**对象创建一个唯一的`id`。 `toString()`使用运行期信息来发现特定的**Shape**子类的名字。 现在我们能很快创建一些**Shape**子类了: ```java // patterns/shapes/Circle.java package patterns.shapes; public class Circle extends Shape {} ``` ```java // patterns/shapes/Square.java package patterns.shapes; public class Square extends Shape {} ``` ```java // patterns/shapes/Triangle.java package patterns.shapes; public class Triangle extends Shape {} ``` 工厂是具有能够创建对象的方法的类。 我们有几个示例版本,因此我们将定义一个接口: ```java // patterns/shapes/FactoryMethod.java package patterns.shapes; public interface FactoryMethod { Shape create(String type); } ``` `create()`接收一个参数,这个参数使其决定要创建哪一种**Shape**对象,这里是`String`,但是它其实可以是任何数据集合。对象的初始化数据(这里是字符串)可能来自系统外部。 这个例子将测试工厂: ```java // patterns/shapes/FactoryTest.java package patterns.shapes; import java.util.stream.*; public class FactoryTest { public static void test(FactoryMethod factory) { Stream.of("Circle", "Square", "Triangle", "Square", "Circle", "Circle", "Triangle") .map(factory::create) .peek(Shape::draw) .peek(Shape::erase) .count(); // Terminal operation } } ``` 在主函数`main()`里,要记住除非你在最后使用了一个终结操作,否则**Stream**不会做任何事情。在这里,`count()`的值被丢弃了。 创建工厂的一种方法是显式创建每种类型: ```java // patterns/ShapeFactory1.java // A simple static factory method import java.util.*; import java.util.stream.*; import patterns.shapes.*; public class ShapeFactory1 implements FactoryMethod { public Shape create(String type) { switch(type) { case "Circle": return new Circle(); case "Square": return new Square(); case "Triangle": return new Triangle(); default: throw new BadShapeCreation(type); } } public static void main(String[] args) { FactoryTest.test(new ShapeFactory1()); } } ``` 输出结果: ```java Circle[0] draw Circle[0] erase Square[1] draw Square[1] erase Triangle[2] draw Triangle[2] erase Square[3] draw Square[3] erase Circle[4] draw Circle[4] erase Circle[5] draw Circle[5] erase Triangle[6] draw Triangle[6] erase ``` `create()`现在是添加新类型的Shape时系统中唯一需要更改的其他代码。