此时,您可能已经遇到了设计模式。例如,单例。
让我们回顾一下什么是模式,为什么需要它们,什么是创建型模式(单例模式就是一个例子)。我们还将研究一种新模式:工厂方法。
在软件开发中,设计模式是一种可重复的架构构造,表示在某些重复出现的上下文中对设计问题的解决方案。
通常,模式不是可以直接转换为代码的最终解决方案。它只是可以在各种情况下使用的问题的模型解决方案。
创建型模式是处理对象创建过程的设计模式。它们使创建一个独立于用于创建、组合和呈现对象的方法的系统成为可能。
工厂方法是一种创建型设计模式,它定义了一个用于在父类中创建对象的通用接口,使其后代能够创建这些对象。在创建时,后代可以确定创建哪个类。
该模式解决了什么问题?
想象一下,您决定创建一个交付程序。最初,您将雇用带汽车的快递员并使用车对象代表程序中的运载工具。快递员将包裹从 A 点运送到 B 点,依此类推。十分简单。
该计划越来越受欢迎。您的业务正在增长,您希望扩展到新市场。例如,您也可以开始运送食品和运输货物。在这种情况下,可以通过快递员步行、滑板车和自行车来运送食物,但货运需要卡车。
现在您需要跟踪几件事(何时、向谁、什么以及多少),包括每个快递员可以携带多少。新的交通方式具有不同的速度和容量。然后你注意到你程序中的大多数实体都与车班级。您意识到,为了使您的程序适用于其他交付方式,您将不得不重写现有的代码库,并且在每次添加新车辆时都需要重新编写。
结果是可怕的代码充满了条件语句,这些语句根据运输类型执行不同的操作。
解决方案
工厂方法模式建议通过调用特殊的工厂方法而不是直接使用new运算符来创建对象。具有工厂方法的类的子类可以修改特定车辆的创建对象。乍一看,这似乎毫无意义:我们只是将构造函数调用从程序中的一个位置移动到另一个位置。但是现在您可以覆盖子类中的工厂方法来更改正在创建的运输类型。
让我们看一下这种方法的类图:
为了让这个系统工作,所有返回的对象必须有一个公共接口。子类将能够生成实现此接口的不同类的对象。
例如,Truck和Car类使用deliver方法实现CourierTransport接口。这些类中的每一个都以不同的方式实现该方法:卡车运送货物,而汽车运送食物、包裹等。TruckCreator类中的工厂方法返回卡车对象,CarCreator类返回汽车对象。
对于工厂方法的客户端,这些对象之间没有区别,因为它会将它们视为某种抽象的CourierTransport。客户会非常关心该对象是否具有交付方法,但该方法究竟如何工作并不重要。
Java中的实现:
public interface CourierTransport {
void deliver();
}
public class Car implements CourierTransport {
@Override
public void deliver() {
System.out.println("The package is being delivered by car");
}
}
public class Truck implements CourierTransport {
@Override
public void deliver() {
System.out.println("The freight is being delivered by truck");
}
}
public abstract class CourierTransportCreator {
public abstract CourierTransport createTransport();
}
public class CarCreator extends CourierTransportCreator {
@Override
public CourierTransport createTransport() {
return new Car();
}
}
public class TruckCreator extends CourierTransportCreator {
@Override
public CourierTransport createTransport() {
return new Truck();
}
}
public class Delivery {
private String address;
private CourierTransport courierTransport;
public void Delivery() {
}
public Delivery(String address, CourierTransport courierTransport) {
this.address = address;
this.courierTransport = courierTransport;
}
public CourierTransport getCourierTransport() {
return courierTransport;
}
public void setCourierTransport(CourierTransport courierTransport) {
this.courierTransport = courierTransport;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
public static void main(String[] args) {
// Accept a new type of order from the database (pseudocode)
String type = database.getTypeOfDeliver();
Delivery delivery = new Delivery();
// Set the transport for delivery
delivery.setCourierTransport(getCourierTransportByType(type));
// Make the delivery
delivery.getCourierTransport().deliver();
}
public static CourierTransport getCourierTransportByType(String type) {
switch (type) {
case "CarDelivery":
return new CarCreator().createTransport();
case "TruckDelivery":
return new TruckCreator().createTransport();
default:
throw new RuntimeException();
}
}
如果我们想创建一个新的交付对象,那么程序会自动创建一个合适的运输对象。
我们什么时候应该应用这种模式?
1. 当您事先不知道您的代码需要处理的对象的类型和依赖性时。
工厂方法将生成交通工具的代码与使用交通工具的代码分开。因此,可以在不触及其余代码的情况下扩展用于创建对象的代码。
例如,要添加对新型交通工具的支持,您需要创建一个新的子类并在其中定义一个返回新交通工具实例的工厂方法。
2. 当您想通过重用现有对象而不是创建新对象来节省系统资源时。
当使用资源密集型对象(如数据库连接、文件系统等)时,通常会出现此问题。
想一想重用现有对象需要采取的步骤:
-
首先,您需要创建一个共享存储库来存储您创建的所有对象。
-
请求新对象时,您需要查看存储库并检查它是否包含可用对象。
-
将对象返回给客户端代码。
-
但是,如果没有可用对象,请创建一个新对象并将其添加到存储库中。
所有这些代码都需要放在不会弄乱客户端代码的地方。最方便的地方是构造函数,因为我们只需要在创建对象时进行所有这些检查。las,构造函数总是创建一个新对象——它不能返回一个现有对象。
这意味着需要另一种方法来返回现有对象和新对象。这将是工厂方法。
3. 当您希望允许用户扩展您的框架或库的某些部分时。
用户可以通过继承来扩展你的框架类。但是如何让框架创建这些新类的对象而不是标准类的对象呢?
解决方案是让用户不仅可以扩展组件,还可以扩展创建这些组件的类。为此,创建类必须具有可以定义的特定创建方法。
优点
- 将一个类与特定的运输类分离。
- 将创建交通工具的代码保存在一个地方,使代码更易于维护。
- 简化向程序中添加新交通方式的过程。
- 贯彻开闭原则。
缺点
可能导致大型并行类层次结构,因为每个产品类都必须有自己的创建者子类。
让我们总结一下
您了解了工厂方法模式并看到了一种可能的实现。这种模式经常用于各种库中,这些库提供用于创建其他对象的对象。
当您想要轻松地添加现有类的子类的新对象以便与您的主要业务逻辑交互并且不会由于不同的上下文而使您的代码膨胀时,请使用工厂方法模式。
GO TO FULL VERSION