Aha!设计模式(25)-工厂方法(6)

网友投稿 618 2022-10-12

Aha!设计模式(25)-工厂方法(6)

Aha!设计模式(25)-工厂方法(6)

9. 实 现

关于实现的部分,《设计模式》书中的描述已经很详细了,所以这里只进行简单的归纳。

当应用Factory Method模式时要考虑下面一些问题: 1) 主要有两种不同的情况

这段叙述集中在几个方面:一是基类是否定义为抽象类,二是基类是否要提供缺省实现。说了一大段其实就是说只要你愿意,条件也允许就可以。如果你不愿意或者没有条件的话不这样也行。从工厂方法模式的角度来看就是怎么都行。

2) 参数化工厂方法 该模式的另一种情况使得工厂方法可以创建多种产品。工厂方法采用一个标识要被创建的对象种类的参数。工厂方法创建的所有对象将共享 Product接口。在Document的例子中,Application可能支持不同种类的Document。你给CreateDocument传递一个外部参数来指定将要创建的文档的种类。 图形编辑框架Unidraw [VL90]使用这种方法来重构存储在磁盘上的对象。 Un idraw定义了一个Creator类,该类拥有一个以类标识符为参数的工厂方法Cre ate。类标识符指定要被实例化的类。当Unidraw将一个对象存盘时,它首先写类标识符,然后是它的实例变量。当它从磁盘中重构该对象时,它首先读取的是类标识符。 一旦类标识符被读取后,这个框架就将该标识符作为参数,调用Create。Cre ate到构造器中查询相应的类并用它实例化对象。最后, Create调用对象的Read操作,读取磁盘上剩余的信息并初始化该对象的实例变量。 一个参数化的工厂方法具有如下的一般形式,此处MyProduct和YourProduct是Product的子类:

和抽象工厂模式一样,工厂方法也可以使用参数来构建产品。

重定义一个参数化的工厂方法使你可以简单而有选择性的扩展或改变一个Creator生产的产品。你可以为新产品引入新的标识符,或可以将已有的标识符与不同的产品相关联。 例如,子类MyCreator可以交换MyProduct和YourProduct并且支持一个新的子类TheirProduct:

注意这个操作所做的最后一件事是调用父类的Create。这是因为MyCreator:: Create仅在对YOURS、MINE和THEIRS的处理上和父类不同。它对其他类不感兴趣。因此MyCreator扩展了所创建产品的种类,并且将除少数产品以外所有产品的创建职责延迟给了父类。

使用参数之后派生类在重新定义工厂方法时就可以决定如何使用基类的工厂方法了。完全另起炉灶当然没问题;自己实现一部分,其他的使用基类也可以。还是那句话,看着办就好。

3) 特定语言的变化和问题 不同的语言有助于产生其他一些有趣的变化和警告(caveat)。 Smalltalk程序通常使用一个方法返回被实例化的对象的类。Creator工厂方法可以使用这个值去创建一个产品,并且ConcreteCreator可以存储甚至计算这个值。这个结果是对实例化的ConcreteProduct类型的一个更迟的绑定。 Smalltalk版本的Document的例子可以在Application中定义一个documentClass方法。该方法为实例化文档返回合适的Document类,其在MyApplication中的实现返回MyDocument类。这样在类Application中我们有

在类MyApplication中我们有

它把将被实例化的类MyDocument返回给Application。一个更灵活的类似于参数化工厂方法的办法是将被创建的类存储为Application的一个类变量。你用这种方法在改变产品时就无需用到Application的子类。

作者没有Smalltalk的经验,大家自己看看就好。

C++中的工厂方法都是虚函数并且常常是纯虚函数。一定要注意在Creator的构造器中不要调用工厂方法 — 在ConcreteCreator中该工厂方法还不可用。 只要你使用按需创建产品的访问者操作,很小心地访问产品,你就可以避免这一点。构造器只是将产品初始化为 0,而不是创建一个具体产品。访问者返回该产品。但首先它要检查确定该产品的存在,如果产品不存在,访问者就创建它。这种技术有时被称为lazy initialization。下面的代码给出了一个典型的实现:

问题的起源来自C++的一个限制:在构造函数不能调用虚函数。上面的使用了lazy initialization技术。lazy的意思是懒惰。想想懒人的做事方式:无论什么事总要拖到最后一刻才做。在lazy initialization中,懒惰的体现就是直到使用之前才构建产品。

推迟构建时机也可以考虑采用分段构建的方法。这种方法将本来在构造函数中进行的类成员初始化移动到另外准备的construct函数中进行。使用者在调用构造函数之后,需要接着调用construct函数来完成构建过程。分段构造可以对初始化过程(处理失败,异常等)进行更加精细的控制,在Symban中曾经被被广泛使用。

无论是lazy initialization也好,分段构造也好都是为了能够正确调用虚函数。这一点并不是工厂方法的要求。姑且算作一个注意事项吧。

4 ) 使用模板以避免创建子类 正如我们已经提及的,工厂方法另一个潜在的问题是它们可能仅为了创建适当的Product对象而迫使你创建Creator子类。在C ++中另一个解决方法是提供Creator的一个模板子类,它使用Produc t类作为模板参数:

使用这个模板,客户仅提供产品类 — 而不需要创建Creator的子类。

当具象Creator的功能只剩下构建具象Product类的时候,这些具象Creator的形式会非常相似,这时考虑模板类是一个很实际的想法。

不止是命名约定,良好的代码风格可以非常有效地增加代码的可读性。对人对己都有好处。

作者观点

和学习任何其他技术一样,学习设计模式的时候也需要识别哪些内容是设计模式的核心(或者说必要)内容。只有这样才能做到书越读越薄,也只有这样才能尽快把握设计模式的本质。

注:

本文中蓝色粗体文字都引自《设计模式》一书。

觉得本文有帮助?请分享给更多人。

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringCloud 分布式锁的多种实现
下一篇:PeerKit 一个开源的Swift框架,用于构建事件驱动的零配置Multipeer连接应用程序
相关文章

 发表评论

暂时没有评论,来抢沙发吧~