聊聊最俗的工厂相关话题

一个朋友问到了这么个问题:

-------------------------------------------------------------------

现在 都说工厂方法支持开闭原则,那客户端不要也new具体的工厂吗?工厂变了 那块也会变 所以不 支持开闭原则。 又有人说,用反射来对付这种情况,既然 这样 为什么不一开始就用反射对付 具体产品呢?所以 我觉着很多人对本模式的理解是有误的,但我又不知道如何正确的理解本模式。

-------------------------------------------------------------------

我最近脑子有点乱其八糟,瞎说两句吧。主要是关于客户new的问题,咱们可以简单的认为,少new一个是一个 :)。当然这只是表象,更深层次的说,如果new只存在在一个地方,修改的点就变的单一了。虽然创建对象的接口(主要是参数列表)也可能变化,但是总归引起大规模修改的点是越少越好。

这是不考虑反射时的情况。对于载入具体类型未知的对象,我们可以看一看asp.net的方式:文件更改了,在访问时编译;所以反射+配置文件,并非是唯一一种解决之道。关键在于接口,强调设计模式作用的人,往往必须保证他们的接口是稳定的,这种接口有时候是抽象层面上的,有时候是底层的,比如模块的二进制接口。

在接口不稳定时,面向对象和设计模式,往往仅能带来更多的繁文缛节。我们可以看到很多框架和库最终实际上是放弃了这种基于静态检查的做法,而使用反射来动态检查对象的属性;这与更老式的程序风格,比如围绕字典或表编程,实际上有很大相似性。

不过这种相似性,与上面的问题并无直接联系。也就是说,在这个问题上,我个人倾向于肯定工厂的存在,即便是在过程或函数式的编程语言中也是如此。回到第一段所说,我们还可以注意到,复杂对象的创建,往往并不是一个new就解决了的,可能还需要进行一些其它(但步骤相同或相似)的工作,对于这个,就不是仅仅把new都给替换成Activitor.CreateInstance(...)所能解决得了。

在这里,工厂有两个作用,一个是将变化中相对好解决的部分(主要是刨除接口的变化后的部分)集中在一起;另一个作用类似于重构中方法的提取。至于Factory本身可以有Provider进行替换这些,暂时不在这次讨论之列。从开放-封闭原则的角度去看,并非说只有达到一尘不染的程度,才来说是不是遵循了这个原则。也许客户在某行代码中new了工厂,但一个正确的工厂应用中至少能够发现,设计中有某些其它部分对修改封闭了。

关于这个原则,实际上想要完全贴合它那是相当的不容易的。如果最严格的要求,我们可以认为配置文件也是一种代码,也不能修改。这并不可笑:看看web.config,实际上应用的config就覆盖了.net自带的config中的设置,而不是鼓励我们去修改原来的文件。我们可以从这个标准情况一步一步倒退,倒退到可修改的配置文件或者仅new一个Factory,当然也可以倒退到直接在使用对象时通过反射和一些判断条件装载,乃至于四处都是new。

这还是原来的那个比喻,面向对象好比挤牙膏,看我们是不是把变化挤到了更合理、修改起来代价更小的地方去。具体做到哪一步,关键是成本和收益的一个折中了。下一段是八卦时间:

之所以工厂会比较流行,是因为工厂的实现成本并不高;对于非典型性应用,大多数人为了方便,少数人是赶时髦。就我的了解,国内企业级Java应用的设计和实现,最多出现的模式就是工厂相关的,甚至还有整个产品只有这一个模式(还是最简单的那种)的情况。如果使用的不到位,有时会模糊工厂的优点。这点我十分推崇以微软的.NET平台(而不是XX最佳实践那些恶心人的例子)本身作为标本来学习。

既然说到工厂,又说到创建步骤,我顺便说说Builder这个模式和工厂的区别与配合。Builder这个模式从根本上讲,就不应该和工厂摆在一起讲。Builder强调的是创建步骤。上面我说工厂时,也提到了这个创建步骤;但是这是非常不同的。

工厂的创建步骤仅仅是简化了我们的重复工作,将一组固定的步骤封装在一个过程中;不同的步骤可能有不同的过程。一个Builder也有很多方法,但它们发挥的作用是,让我们能在不同时间、不同地点,由不同的客户,来填充创建行为所需要的信息。在没有真正要求它创建对象之前,它扮演了一个信息收集者的角色。当我们的需求中存在着若干时刻,可以提供某一种但不是全部用于创建的信息时,就是使用Builder的时候。.NET中的StringBuilder和各种不同平台上构建XML文档的对象都是不错的例子。

Builder配合工厂,是一种典型的、但少见的情况。因为平时我们往往没有这样的需求,或者说收益还不足以承担设计并实现的成本。它们能干什么呢?解决我上面提到的,关于创建并返回对象的那个方法接口(在这里是签名)变化的问题。

首先我们要明确的一点是,Builder本身是可以被替换的;其次我们可以注意到,Builder真正创建对象的那个方法,接口是很容易稳定的。这是因为收集信息的工作,不再像Factory那样,集中在一个方法之上,而是分散成若干方法和它们的签名。从客户方来看,这就是一个极大的优点了:旧的Builder被替换成新的,在未知的地方接受了更多的信息,改变了创建逻辑,但是创建接口(签名)还是原来的;而恰恰客户在很多情况下只关心并调用这个创建接口。

这样,Builder和Factory合一,或者配合工作,就成了一种应付复杂情况的有效解决手段。好像已经够长了,本来还有些其它的要写,不知怎么突然使劲儿想也想不起来是什么了,就此打住。

posted on 2009-03-12 07:58  怪怪  阅读(2744)  评论(41编辑  收藏  举报

导航