首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件开发 >

Clojure跟JAVA设计模式(2) 工厂模式之工厂方法

2012-09-14 
Clojure和JAVA设计模式(2) 工厂模式之工厂方法注:本文基于jdk1.6和Clojure1.2?工厂方法????工厂方法模式定

Clojure和JAVA设计模式(2) 工厂模式之工厂方法

注:本文基于jdk1.6和Clojure1.2

?

工厂方法

????工厂方法模式定义了一个创建对象的接口,由子类来决定实例化哪一个类,意即将实例化推迟到子类。

?

????当然,这个说法本身是干巴巴概念性的。让我们针对上篇简单工厂的例子考虑一下,我们在SimpleFactory中

已经定义了产生IProduct实例的方法,这是在调用之前就定好了产生IProduct实例的规则,可是如果遇到

需要在调用时才决定这个规则的情况时,SimpleFactory就无法满足了。

?

??? 比如说,某次调用中需要在参数productType等于1时返回Product1,参数productType等于2时返回

Product2,SimpleFactory正好能满足这种情况,没问题;但另外一次调用时需要在参数productType等于

1时返回Product2,参数productType等于2时返回null,参数productType等于3时返回Product1,

SimpleFactory就傻眼了......

?

?????当然,我们一拍脑袋就想出办法了--在SimpleFactory的factory方法中再加一个参数来增加规则:

/**      * 根据产品类型和规则来生产产品      * @param productType      * @return      */      public static IProduct factory(String productType,String regulation){      if(regulation.equals("1")){        if(productType.equals("1"))              return new Product1();          else if(productType.equals("2"))              return new Product2();         }else if(regulation.equals("2")){        if(productType.equals("1"))              return new Product2();          else if(productType.equals("3"))              return new Product1();        }        return null;      } 
?

??? 哈哈,这不就解决问题了吗?这样做确实解决了眼前的问题而且能够正常工作,但是,如果产品的规则再增添

一种,比如regulation3,毫无疑问,我们又需要再去修改SimpleFactory的factory方法;规则再增加一种,好的,

继续修改SimpleFactory的factory方法...以此类推,SimpleFactory的factory方法会变得越来越大,变成冗长

的、逻辑关系复杂的代码泥团,谁要再去修改这个方法,必须仔细研读代码,小心翼翼地添加类型和规则,生怕

影响到了所有调用它的地方。改完之后,大骂一通设计人员,然后到javaeye上面发一篇《碰到史上最烂JAVA项目》

这样的帖子,然后大家告诉你:“国内项目很多都这样,没办法,找到工资待遇高的就走吧”。

?

????问题在于,这种解决方法是面向过程而非面向对象的。记得面向对象的设计原则吗,面向接口而非面向实现类

编程,对修改封闭对扩展开放。所以,工厂方法模式就出场了:

/** * 工厂方法接口 * @author RoySong */public interface IFactoryMethod {/** * 根据产品类型生产产品 * @param productType * @return */public IProduct factory(String productType);}/** * 规则1工厂 * @author RoySong */public class Regulation1Factory implements IFactoryMethod {@Overridepublic IProduct factory(String productType) {if(productType.equals("1"))              return new Product1();          else if(productType.equals("2"))              return new Product2();return null;}}/** * 规则2工厂 * @author RoySong */public class Regulation2Factory implements IFactoryMethod {@Overridepublic IProduct factory(String productType) {if(productType.equals("1"))              return new Product2();          else if(productType.equals("3"))              return new Product1();return null;}}/** * 工厂方法调用 * @author RoySong */public class FactoryMethodTest {private IFactoryMethod iFactoryMethod;public static void main(String[] args){FactoryMethodTest fmt = new FactoryMethodTest();//采用规则1工厂方法fmt.setiFactoryMethod(new Regulation1Factory());IProduct product1 = fmt.getiFactoryMethod().factory("1");product1.use("regulation 1");IProduct product2 = fmt.getiFactoryMethod().factory("2");product2.use("regulation 1");//采用规则2工厂方法fmt.setiFactoryMethod(new Regulation2Factory());product1 = fmt.getiFactoryMethod().factory("3");product1.use("regulation 2");product2 = fmt.getiFactoryMethod().factory("1");product2.use("regulation 2");}public void setiFactoryMethod(IFactoryMethod iFactoryMethod) {this.iFactoryMethod = iFactoryMethod;}public IFactoryMethod getiFactoryMethod() {return iFactoryMethod;}}
?

??? 调用的结果是:

Product1 use:regulation 1Product2 use:regulation 1Product1 use:regulation 2Product2 use:regulation 2
?

??? 而在Clojure当中,工厂方法简直是信手拈来。首先我们定义两个产品:

(defn product1 [msg]  (println "Product1 use:" msg))(defn product2 [msg]  (println "Product2 use:" msg))
?

??? 然后我们定义两个规则工厂:

(defn regar1-factory [type]  (cond        (= 1 type) #'product1         (= 2 type) #'product2      true nil))(defn regar2-factory [type]  (cond        (= 3 type) #'product1        (= 1 type) #'product2      true nil))
?

??? 最后定义一个调用函数:

(defn use-product [fn type msg]  ((fn type) msg))
?

????我们注意到代码中有一个奇特的符号” #‘ “,这是个什么玩意儿呢?

?

????实际上,这是Clojure中的一种取值方式,用于取得符号(比如product1)所映射的对象值(意即product1所

代表的整个函数体),而并非符号储存的对象的引用值(指针)。换句话说,我们的规则函数(regar*-factory)

所返回的是整个函数,而调用函数接受的参数fn也是一个函数。这就是函数式编程语言的最大特征之一,函数即

数据。你可以把函数作为返回值或者是参数。这也是函数式编程语言比指令式编程语言更加灵活(也更加难懂)的

原因之一。

?

??? 回到例子,我们开始使用调用函数:

(use-product #'regar1-factory 1 "anything")

????毫无意外的结果:

Product1 use: anythingnil

??? 再来一个:

(use-product #'regar2-factory 1 "something")

????仍然是预期的结果:

Product2 use: somethingnil
?

??? 这样,我们在Clojure里面就达到了运行时根据规则产生不同的对象的目的了。严格来说,上面的例子并非在

运行时才进行的实例化,应该通过宏(macro)来产生product1和product2,不过,为了代码的清晰和简单,

这儿暂时就这么写了。

?

附注:很久没有写工厂了,概念模糊了,这里面的java代码实际上不是工厂方法,而是抽象工厂。经朋友提醒才

想起来,原文先保留不动吧。

热点排行