设计模式——工厂模式学习总结
假设现在一个场景:
某物流公司,当前有以下业务:汽车运输和轮船运输。客户可以选择任一运输方式进行运输。
此时,应该如何使用代码将这个现实业务进行抽象实现?
在没有学习工厂模式前,我是这样想的,分别写个物流公司的类,然后它依赖汽车类和轮船类,让汽车类和轮船都实现运输接口,然后让用户传递一个参数,参数将决定给用户返回汽车或者轮船,这样就实现了。
代码如下:
from abc import ABC,abstractmethod
class Logistics:
def __init__(self,transport):
self.transport_map = {
"car":Car(),
"ship":Ship()
}
self.transport = self.transport_map[transport]
def deliver(self):
return self.transport.deliver()
class Delivery(ABC):
@abstractmethod
def deliver(self):
pass
class Car(Delivery):
def deliver(self):
return "A car has delivered."
class Ship(Delivery):
def deliver(self):
return "A ship has deliverd"
def client(transport):
print(Logistics(transport).deliver())
if __name__ == "__main__":
client("car")
client("ship")
执行结果如下:
这样写是能够满足这篇文章最初的要求的,但是随着公司的扩展,业务又支持火车运输了,此时,应该怎么写代码?一定是要修改Logistic类的对吗?每增加一种运输方式,我们就要修改一次Logistics类的代码,对吗?
此时就存在一个问题,我们违背了SOLID原则中的开闭原则:对修改关闭,对扩展开放。我们这里只是写了一个很简单的场景,实际的场景中,一旦违背这个原则,可能会引入很多问题,甚至会威胁软件本身。比如,我们要将一个系统提供给用户,让用户可以自定义增加运输类型,让用户修改代码,或者让用户写的代码通过我们提供的方式植入到原有的代码中,都可能会带来安全隐患。
此时,我们可以通过工厂模式,让父类决定对外的功能,让子类决定创建的具体的对外服务的对象,则可以在不修改代码的情况下,进行扩展。
比如这个在这个题目中,我们让物流公司(后面称为总公司)下面创建两个子公司:陆路物流公司、海洋物流公司。物流总公司定义一个创建运输工具的接口,让子公司通过实现这个接口来创建自己的运输工具。物流总公司定义“运输”的方法,所有它的子公司都将支持“运输”。至于“运输”如何实现,可以让子公司来实现,也可以让运输工具来实现,都是可以的,但是出于对以后扩展性的考虑,可以让运输工具来实现。因为,比如让陆路物流公司实现“运输”方法,那意味着陆路公司只有1种“运输”方法,那么万一这个陆路物流公司后面又支持了火车运输怎么办?而如果让汽车这个运输工具来实现运输方法,则当以后陆路物流公司支持火车运输时,火车可以扩展出自己的运输方法。
代码如下:
from abc import ABC,abstractmethod
class Logistics(ABC):
@abstractmethod
def createTransport(self):
pass
def planDelivery(self):
transport = self.createTransport()
result = transport.deliver()
return result
class Transport(ABC):
@abstractmethod
def deliver(self):
pass
class RoadLogistics(Logistics):
def createTransport(self):
return Car()
class SeaLogistics(Logistics):
def createTransport(self):
return Ship()
class Car(Transport):
def deliver(self):
return "A new car has delivered."
class Ship(Transport):
def deliver(self):
return "A new ship has delivered."
def client(logistic):
print(logistic.planDelivery())
if __name__ == "__main__":
client(RoadLogistics())
client(SeaLogistics())
执行结果如下:
总结:当不同子类有一些差别时,我们可以把创建对象的工作让子类来完成,此时就可以用工厂模式。