设计模式的主题很受欢迎。 拍摄了很多视频,并撰写了文章。 将所有这些材料与“反模式”结合在一起。偶然的复杂性。 结果,示例繁琐,描述混乱,如何应用尚不清楚。 设计模式的主要任务-简化(代码和一般工作)无法实现。 毕竟,使用模板需要付出额外的努力。 与单元测试大致相同。
我将尝试就如何应用设计模式,在何处以及为什么进行解释。
六个生成器可以归因于:
与生成器有关的所有其他模式都是特殊的应用情况,没有必要再赘述了。
生成模式可将其回答的问题分为三组。 因此,三个问题:
在哪
三种模式回答了这个问题:原型,抽象工厂和工厂方法。
关于条款的一点点在OOP概念的框架内,理论上只有三个地方可以生成一个新实例。
- 产品是实例化的类。
- 客户端是将使用实例化实例的类。
- 合作伙伴-客户可见度领域中的任何第三等。
实际上,这些模式决定了产生的地点。 此外,它们是分层连接的,并且具有不同的范围。 该连接如图所示,箭头确定呼叫方向。

在其实现中,“工厂方法”可以将实例的生成委派给现有的“工厂”或“原型”。 但是,“原型”不应依赖任何人,而是自己做任何事情。 现在更详细。
“原型”
该模板对应于位置“ Product”,实际上是该类的构造函数。 因此,总是生成特定(先前已知)类的实例。
在此模板的框架内,构造函数仅知道直接传递给它的参数(参数的数量趋向于类字段的数量)。 当然,可以完全访问所创建类的所有字段和属性。
正确实现的“原型”方法可让您摆脱公共的其他初始化方法。 反过来,该类的外部接口也变得更容易,也更不愿意将其用于其他目的。
该模板为我们提供了什么:
- 连通性低-类仅了解自己,不依赖外部数据;
- 可扩展性-可以重新定义构造函数或将其添加到后代;
缺点:
- 对于复杂的类,您可能需要传递许多参数以进行初始化。 虽然有一个简单的解决方案。
- 在客户端中直接使用会损害可读性,并实际上阻止了覆盖在客户端后代中生成的实例类型的可能性。
最受欢迎的模板。 每个人都在使用它,但很少有人知道它的用途。 在获得第一个工作模型之前,直到完全定义类及其关系之前,这是很好的方法。 之后,必须进行处理并增加抽象度。
“抽象工厂”
一些班级的伙伴。 它可以是专用的,也可以是“组合”的。 可能是静态的(无实例)。 “合并”的一个示例可以是配置类。 它也可能隐藏在立面的后面。
“工厂”通常查看应用程序(或单独的子系统)的所有全局设置。 立即生成可以委托给原型。 同时,Factory方法中输入参数的数量将少于类似的Prototype构造函数中的输入参数的数量。 工厂不会根据传入的参数决定由谁创建。
该模板非常方便且易于实现,但需要进行初步设计。 如果为所有内容创建工厂,这将使代码复杂化。 实际上,我们得到了Prototype的类似物,但转到了第三方类。
从优点:
- 重新定义后代
- 简化通话
- 在工厂的基础上,很容易实现替换(状态模板)
但是也有缺点:
- 它需要设计,特别是对于通用工厂(在许多项目中使用)。 换句话说,立即建立一个好的工厂并不容易。
- 弄乱代码很容易,主要有两个方面:
- 滑向原型,但在室外课堂上。 方法重载了参数;方法本身也很多。 结果,无论在工厂本身还是在客户端中,继承都是困难的。
- 工厂采用通用方法。 此方法根据传递的参数返回任何实例。 结果与第一种情况相同。
很受欢迎 参加GoF课程的人员使用此模板。 通常,代码变得比“应用模板之前”更糟糕。
当工厂在第一次代码返工期间出现时,这是有意义的。 在此阶段,已创建实例的参数组合是已知的,编写通用化的Factory方法并不困难。 结果,将简化客户端中的呼叫。
在某些情况下,将工厂隐藏在立面后面很方便。 例如,该应用程序有十二个工厂和十二个库。 对于他们来说,您可以构建立面。 这将允许不将库链接到每个模块,并且很容易用另一个工厂替换一个工厂。
工厂方法
生成模式中的抽象顶部。 原产地客户。 将每种产品置于“工厂方法”中的类别都有很长的使用寿命。 如果没有狂热主义,则假定的发展轴必须必须基于此模板。
工厂方法没有超出其类的范围。 直接传输的参数的数量应最少(限制为零)。 在构建方法本身时,必须考虑到后代重叠的可能性。
一个常见的错误是使用一种方法进行复杂的初始化。 例如,当创建一个复杂的实例(Builder模板)时,将来对象的所有部分的创建都放在一种方法中。 结果,这种方法在后代中很难重叠。
从优点:
- 匹配模板模板方法会很容易
- 我们得到了简洁的代码,其中的逻辑清晰可见(不需要在大量的方法和参数中查看它)
基本上没有缺点。
该模板几乎从未使用过。 通常,只有在进行了深入初步准备的项目中才能看到它。 当工厂方法将生成委托给“工厂”或“原型”时,是理想选择。
小例子
我们有一个用于记录硬盘上文件的类。 这是“哪里”模式中的通用方法的外观:
原型:
constructor Create(aFilename: string; aLogLevel: TLogLevel);
设计师应该知道的所有信息都以参数的形式传递给他。
工厂:
function GetLogger(aLogLevel: TLogLevel): ILogger;
根据应用程序设置,工厂知道要写入哪个文件。
工厂方法:
function NewLogger: ILogger;
在Client类中,知道要记录什么详细信息。
在此设计中,为了用存根替换日志记录类,足以在客户端的后代中重新定义NewLogger。 这在进行单元测试时很有用。
要登录到数据库,只需在Factory的后代中覆盖GetLogger方法即可。