当然我已经很多年没有再读设计模式的教科书了,描述可能不足准确,紧张是先容一下运用履历吧。
工厂模式犹如他的名字一样,便是为了「制造实例」而生的,以是一个实现工厂模式的类都会有一个类似create_xxx / new_xxx的方法。
以下写个伪代码举例:
class Factory { function create_object(...) object { ... return new object() }}
看到这里大家一定有个疑问:制造一个实例很大略啊,为什么要写一坨这样的废话来制造一个实例呢?
我刚开始学习这个模式的时候和大家想法一毛一样的,觉得是老学究在扯淡。后来事情了一段韶光创造,这个东西确实是很有用的,我下面举一些例子,把稳这些例子是在真实的事情中会碰着的。
第一种情形:创建一个实例的参数非常多以及繁芜我们在学校学习编程时老师一样平常会讲,不要让一个函数的参数太多太繁芜,同理对付一个类的布局函数来说,也希望是这样的。绝大部分情形下,我们追求这个「让代码更简洁」的目标并且可以实现这个目标,但是在一些情形下,由于功能或者业务的繁芜性导致我们不得不传入很多参数才能实现预期的功能。
可能有些同学不是特殊理解,为啥布局一个工具须要那么多参数,这个实在写一些大型工程,繁芜一些的项目都会碰着。当然我们一样平常都会尽可能避免这种情形发生的,但是险些还是会有个别的情形无法避免。
有些有履历的同学会说,你可以创建一个类似Options的类等办法,把参数聚合在一起通报进去。事实上这是非常好的习气,这个做法是没问题的,但是同样你要考虑到调用方在布局这个Options时仍旧须要从各个位置获取工具、数据然后才能天生这个Options工具。因此这个实践方法虽然很棒,但是无法办理这个问题。尤其是当你布局一个工具时,须要传入一些全局性子的配置(超时、鉴权等)、全局性的资源池(内存分配、连接分配等),那么每布局一个工具,调用方都要去找到这些工具的位置。而一个更好的实践是先创建一个工厂工具,布局的过程中传入这些参数,之后在布局工具时就可以避免这样重复性的传参事情了。
以下举个例子:
class Factory { Factory(Config global_config, ObjectPool object_pool, ConnectionPool connection_pool) { ... } func new_object(String hostname) { ... }}
这个例子写的便是上面那种情形,我们创建一个工具,还须要配置、工具池、连接池,这些参数在工厂创建时就写好了,这样在创建工具时,只须要专注这个工具独占的参数即可。
第二种情形:创建实例的实现和调用分别在不同模块,不同团队掩护这个情形在轻微大一些的项目中就很常见了,比如卖力SDK的团队只开拓接口并不卖力详细的业务,而业务团队依赖SDK封装的库实现功能。这个时候如果业务中须要反复创建同一个类的实例,那么利用工厂模式就会带来额外的好处:
工具的创建很可能要依赖SDK内部的参数,业务方去找SDK内部的参数,是一种非常糟糕的API设计SDK团队的改进可能会让参数发生变革,这个时候如果业务方直接依赖这些参数布局工具实例,SDK团队就必须办理麻烦的向前兼容问题。因此一样平常SDK中都会大量的利用工厂模式来避免暴露过多的细节,一方面API更简洁对调用方更友好,另一方面也方便自身后续的改进优化。
第三种情形:结合适配器模式,天生的实例是接口形式之前本来想把工厂模式和适配器模式一起先容的,然后创造工厂模式的篇幅超出预期了,于是就先单独先写一篇工厂模式的文章了。
一言以蔽之,工厂 + 适配器模式险些是工业界的标准设计范式,由于实在是太太太好用了。这个方法也非常符合面向工具的开放-关闭原则(Open - Close Principle)。
以下先用代码举例,然后说一下上风是什么:
interface Car { func run()}class CarFactory { func new_car() Car { ... }}
这个代码很大略,便是我们定义了一个接口Car,然后CarFactory卖力创建一个新的Car的实例。大家很随意马虎知道一个接口是无法被实例化的,因此CarFactory内部会利用一个实现了Car接口的类进行实例化,这个设计可以充分的让调用方和实现方解耦 —— 调用方不必依赖一个详细的Car实现,他只须要知道Car的实例可以run即可。
那我们再看一段代码
interface Car { func run()}interface CarFactory { func new_car() Car}func get_car_factory() CarFactory {}
这个代码比较于上面那个逻辑,又进一步把CarFactory变成了一个接口,调用方通过SDK供应的一个方法来得到一个实现了CarFactory接口的实例,然后通过这个实例来创建Car。
在实际的生产环境中,末了这种写法是非常普遍的,上风包括:
充分的隐蔽了SDK内部的实现,无论是Car本身还是CarFactory,这对付SDK自身的迭代升级来说是非常友好和方便的如果创建CarFactory仍旧须要大量的内部参数,那么这个方法相称于把创建CarFactory的细节也隐蔽掉了,API是最简洁的状态如果大家仔细看过一些开源项目的代码,比如protobuf / grpc /thrift等等框架,能看到很多类似这样的设计。
以上便是对工厂模式的基本先容了,工厂模式属于设计模式里最根本的一种,但是运用范围却最为广泛,技能难分轩轾只看是否得当。文末再来个彩蛋:工厂模式实在也是办理C++在布局新工具时无法大略的处理缺点状态的一种办理方案。关于为什么C++在布局时处理缺点是一件相称棘手的事情,闲的时候我再写一篇短文来说说吧。