七、工厂方法模式(Factory Method)
1、介绍
工厂方法模式是一种创建型设计模式,其主要目标是为了将对象的实例化(创建)和使用分离,以达到提高代码的可读性和可维护性。这是一种相当常用的模式,特别是在需要创建多种类型对象,但在编译时并不知道具体会创建哪一种类型的场景中。
工厂方法模式在软件开发中的应用非常广泛。例如,你在编写一个画图程序,用户可以在画布上画出各种形状,比如圆形、矩形、三角形等。这些形状有许多共同的属性(如位置、颜色、大小等),但它们的绘制方法是不同的。
如果直接使用 new 操作符来创建各种形状的实例,将使代码变得复杂且难以管理,因为你需要在多个地方包含对各种形状构造函数的调用。这样一旦需要添加新的形状,或者修改现有形状的构造方式,就需要修改多个地方的代码。
而如果使用工厂方法模式,我们只需要定义一个工厂类,这个工厂类包含一个方法,这个方法根据输入参数决定创建何种形状的实例。这样,我们就可以在程序的其它部分使用这个工厂类,而不需要直接使用 new 操作符。当需要添加或修改形状时,只需要修改工厂类即可,无需修改使用形状的代码。
这样的设计降低了程序的复杂度,提高了代码的可读性和可维护性。这就是工厂方法模式被广泛使用的原因。
2、生活实例
假设你想买一部手机,你可以直接去原材料市场购买各种元件(显示器、处理器、电池等),然后自己组装。但是这会非常麻烦,而且需要你具备一定的专业知识。一般情况下,我们会直接去手机商店,告诉店员我们想要的手机型号,然后店员会从仓库里拿出一部已经组装好的手机给我们。
在这个例子中,手机商店就像是一个工厂,我们只需要告诉它我们想要的产品,它就会提供给我们。我们不需要关心手机是如何组装的,也不需要关心手机的各个部件。我们只关心最终的产品。
3、java代码实例
以下是一个简单的工厂模式的 Java 实现。假设我们有两种类型的手机,iPhone 和 AndroidPhone。我们可以创建一个 PhoneFactory 类,它有一个 createPhone 方法,这个方法根据参数决定创建哪种手机:
interface Phone {
void display();
}
class iPhone implements Phone {
@Override
public void display() {
System.out.println("Displaying iPhone");
}
}
class AndroidPhone implements Phone {
@Override
public void display() {
System.out.println("Displaying Android Phone");
}
}
class PhoneFactory {
public Phone createPhone(String type) {
if ("iPhone".equals(type)) {
return new iPhone();
} else if ("AndroidPhone".equals(type)) {
return new AndroidPhone();
}
throw new IllegalArgumentException("Invalid phone type: " + type);
}
}
在使用工厂的代码中,我们可以像下面这样使用:
PhoneFactory phoneFactory = new PhoneFactory();
Phone iphone = phoneFactory.createPhone("iPhone");
iphone.display(); // Output: Displaying iPhone
Phone androidPhone = phoneFactory.createPhone("AndroidPhone");
androidPhone.display(); // Output: Displaying Android Phone
通过这个例子,我们可以看到工厂方法模式的好处:当我们需要创建一个对象时,我们不需要知道这个对象是如何被创建的,也不需要直接调用这个对象的构造函数。我们只需要知道这个对象的类型,然后让工厂来为我们创建这个对象。这样我们的代码就变得更简单,更清晰,也更易于维护。
4、其它例子
Java 的 Calendar 类和 NumberFormat 类都使用工厂方法模式。
对于 Calendar 类,Calendar.getInstance() 方法返回一个 Calendar 对象,这个对象的确切类型取决于你所在的地理位置和语言环境。
对于 NumberFormat 类,NumberFormat.getInstance() 方法返回一个 NumberFormat 对象,这个对象的确切类型取决于你的语言环境。你不需要知道这个对象的确切类型,你只需要知道它是一个 Calendar 或 NumberFormat 对象。