我的隐式生活(My Implicit Life)

继续搞“对象”,玩OO.

首页 新随笔 联系 聚合 管理
  11 Posts :: 1 Stories :: 39 Comments :: 0 Trackbacks

    说起这个工厂模式,一时还真不知道该如何说起。反正这是我的开发日志,不提理论的东西,理论的东西那里都有,我只想把具体实践记录下来给师弟师妹们作个参考,积累点经验。所有这些文字都是集中讲一点――“在什么情况下为什么用某种模式好,为什么好,为什么在那种情况下能想起来用?”。

       研究生院项目中“明显”使用了“工厂方法模式”。其实在遇到具体问题的时候,即使我们不知道有这个模式存在,我们也肯定会造一个类似的东西出来。但是,肯定没有书上论述的那么好,那么全面。我想这就是看书的好处吧。

 

工厂方法出现的必然(我的理解,一个很狭隘并幼稚理的人的理解)

 

       刚开始使用这个东西的时候,只是感觉是单纯的一种模式,用于创建需要的对象。

       但是随着使用和思考的深入,越发发现它给我的启示不只在于单纯的对象创建,而是告诉我应该怎么理解“产品”,怎么得到“产品”,怎么消费“产品”,以至于以后怎么设计“产品”。

       下面这个线索是我对它出现必然性的理解:

1.         “针对接口编程”

  这是OO世界中经典的规范,不管你主动还是被动,你天天都在用这个东西。

    接口是共性的表示,在对象的世界中,共性和个性的辩证关系是最重要的关系。在万千的对象中,通过它们之间的共性和个性,可以形成最基本对象层级架构。

假设我们的讨论域中有以下一些对象:“学生”、“大学生”、“小学生”、“中学生”;我们不用细想,学过一天OO的人都可以为这些耳熟能详的对象们,通过个性和共性的关系得出下面的结构图。

o_1.JPG

 

       把这些对象之间的关系定义成这样是顺理成章的。

       下一步肯定是让客户端“使用”这个接口啦。也就是如下图:

 

o_2.JPG

2.         接口和具体类的矛盾

勿庸置疑,我们只希望Client跟接口Student打交道,让它根本就不知道Student有哪些子类,绝对不希望直接跟它们打交道。

但这里出现的困难是,接口都是“假”的,都是由具体类upcast的。

如果Client要使用接口StudentClient中必须会出现下面的代码:

Student marco = new Small_Student();

       只要一出现这个代码,就说明Client不只跟Student打交道了,它知道了Small_Student类,这违反了我们预先的想法。

3.         找“人”帮我去创建“接口对象”

    从上图体现出来的结构看,Client只想跟Student打交道的目的是实现不了的了。

       最简单的方法就是找另外的“帮手”去帮我生成这个“接口对象”。这个帮手它知道“接口对象”的具体类型,但是它为客户端提供的却一定是“接口类型”。这就符合我们的要求了。如图:

o_3.JPG

    这样,Client就可以既用到了“Student接口对象”,又不用因为“只有具体类才能创建对象”的规则,而必须对其子类结构有完全的了解。它成功的解决了2中的矛盾。

       而“负责创建具体类对象的任务”全部都落在了这个“帮手”身上,这个“帮手”(Student_Factory)就是工厂模式中的工厂类,更具体的说,它就是“简单工厂模式”中的“简单工厂类”。

       我觉得,即使一点都不知道工厂模式,一旦我遇到了2里说的矛盾,我也会用这样的方法处理。

4.         这个“帮手”不符合“开-闭原则”

这个帮手的确不错了,而且一跃成为系统中最重要的对象了,所有“创建具体类的逻辑”都放进去了,也就是因为重要,万一挂了不就惨了。

再者,它不符合“开-闭”原则,我不能在不修改这个帮手的情况下添加任何一个产品。在这个例子中就是,如果那天我有病非要加一个“幼儿园学生”进来,那您就必须修改这个“帮手”的代码了,这个“帮手”现在就变成Version2(如下图)了,这个二版的帮手,可以在“代码”层实现对一版(还没有添加幼儿园学生之前)的通用,但这种保证在“开-闭”原则看来,还是不够的,不保险的,它要的是在类的结构上的保证。声明一下,这是我很感性的理解,不正确的可能性很高。

o_4.JPG

5.         让“帮手”也多态

这里可以尝试让“帮手”也多态一下,这样“每种学生类创建的任务”都被分派到了多态出来的类中去了。这时,再有新的学生类型加进来,添加一个对应的帮手就可以了。这样虽然类多了一些,但是符合“开-闭”原则,书上称之为“工厂方法模式”。如图:

o_5.JPG

    假如Client现在要使用一个小学生,代码如下:

1        //创建一个小学生工厂类这个帮手
2        Student_Factory factory = new Small_Student_Factory();
3        //求助这个帮手,帮我创建一个

4        Student primaryStudent = factory.produce();
5        //这时就可以使用这个小学生了,让它玩玩游戏吧

6        primaryStudent.playGames();
7

    在这里还是要强调两点:

n         虽然实际上Client的确使用了一个小学生对象(Small_Student),但这里Client也认为它就是Student对象,这里一定要用Student接口来隐藏它的具体类。

n         另外,却不需要用Student_Factory这个接口来隐藏它的具体类,因为,Client实际就是通过“选择它的具体类”这招儿来“选择创建的学生类型”。这里的Student_Factory更多的功能不是“隐藏”具体类,而是“规范”具体类。

 

项目实践

 

       扯淡到此,该联系我们的项目啦。

       由于是做研究生院的项目,其中巨大的需求就是要让同学能在网上提交各种申请单,申请退学的,申请转专业的,申请复学的,申请保留学籍的,除了申请女朋友的外,应有尽有。       对于这些单子,用最最基本OO思维,根据共性个性分析方式,抽象出一个申请单接口,和若干的具体类。

       当然,除了概念上感性上吻合以外,在项目中它们也要“真”的有巨大的共性才行,如都要提交,修改,删除,审核,打印等功能。

       靠,既然都这样了,肯定就用一接口规定它了。

       想到这里,加上有了上面的思考,不用说了,就用工厂方法模式啦。

 

o_6.JPG

    图中Ent_Shift就是申请单接口。等于前面分析的Student接口。还可以看到有很多具体类,前面例子中是代表各种学生,这里就是代表各种申请单。

       当然,这里还有很多工厂,也就是前面一直叫的“帮手”。

o_7.JPG

       Bean_Shift就是工厂接口,相当于前面的Student_Factory接口。还有很多的具体类就是生产各种申请单的工厂类。

       下面就是使用“申请单工厂方法模式”的一段客户端代码:

 1        //声明申请单接口
 2        Ent_Shift es = null;
 3        //声明申请单工厂接口

 4        Bean_Shift bs = null;
 5        //根据传入的申请单类型数字身成对应的申请单工厂

 6        switch (shiftTypeID) {
 7            case
 Bean_Shift.SHIFT_CHANGETEAAP :
 8                bs = new
 Bean_Shift_CHANGETEAAP();
 9                break
;
10            case
 Bean_Shift.SHIFT_RESERVEAP :
11                bs = new
 Bean_Shift_RESERVEAP();
12                break
;
13            case
 Bean_Shift.SHIFT_RENEWAP :
14                bs = new
 Bean_Shift_RENEWAP();
15                break
;
16             //省略了别的申请单……………………

17            default :
18                this.forwardErr(req, resp, "所选择的异动类别不存在"
);
19        }

20
21        try 
{
22            //调用工厂接口的生产方法

23            es = bs.getBlankShift(stuID);
24
            
25        }
 catch (Exception e) {
26            this
.forwardErr(req, resp, DB_ERROR);
27        }

28        //调用单子的提交方法
29        es.submit(req);
30
        
31        //发给页面去显示

32        es.fowrardWithSessionObject(
33
                req,
34
                resp,
35
                Ent_Shift.nameInSessionAndRequest);
36

 

升华

 

       个人比较同意《Design Pattern Explained》中作者讲的,要用好很多的模式,其中都有一个思路,就是用接口或抽象类来隐藏子类的不同。

       我每当看到这时,老是会被一种思路困扰――“new只能new具体类啊,这咋能隐藏呢,这隐藏还有什么用呢?”。

       作者仿佛也曾经有过我的这个傻B苦恼,它的解决方法就是:根本在使用对象的时候,特别是设计阶段,尽量不去想对象是在那里被new的。他认为:反正有了工厂模式后,你总有办法把他们new出来的。

       所以,我用了工厂模式后更发的启发是:以后设计的时候不要想一个Client怎么创建一个对象,尽管放心大胆的先继续想,直接使用就好了。反正最后我还有工厂模式呢。

       因此俺的副标题才是“Ignore how they were created”,呵呵。
                                    MARCO ZHANG 2006年2月16日3:52:10

posted on 2006-02-16 03:53 marco 阅读(1366) 评论(9)  编辑  收藏 所属分类: -=Design Pattern=--=Java Techs=--=Project Experience=-

Feedback

# re: Java Web Application开发日志之二--“Ignore how they were created”,工厂方法模式应用 2006-02-16 09:38 飞翔的西瓜
把你加入我的链接了,我会经常来“监督”你的进程的:)  回复  更多评论
  

# re: Java Web Application开发日志之二--“Ignore how they were created”,工厂方法模式应用 2006-02-16 10:49 thekll
ft~建议增加申请男朋友和女朋友的功能,这样系统的可用性更强些  回复  更多评论
  

# re: Java Web Application开发日志之二--“Ignore how they were created”,工厂方法模式应用 2006-02-16 13:19 marco
飞翔的西瓜,我就在此谢大哥您啦。

我也把你链上了。  回复  更多评论
  

# re: Java Web Application开发日志之二--“Ignore how they were created”,工厂方法模式应用 2006-02-18 00:48 王知真
blog好详细,呵呵,现在就是希望自己早一点再有一个技术blog,相互交流  回复  更多评论
  

# re: Java Web Application开发日志之二--“Ignore how they were created”,工厂方法模式应用 2006-02-18 21:10 龙卷风
好文章,不过个人感觉工厂模式不实用
本来简单类厂静态函数可以省下一个new,但工厂模式创建工厂还是需要new出来。
我喜欢使用利用了反射机制的简单类厂,既满足开闭原则也用起来也舒服。  回复  更多评论
  

# re: Java Web Application开发日志之二--“Ignore how they were created”,工厂方法模式应用 2006-03-01 12:10 grakiss
也许是我认识不够,但是同楼上一样的想法,在生成具体的类工厂时仍然会需要去确定它的具体类型,那么所谓的封装,隐藏不是因此而失效了么?这也正是你自己一开始的想法.虽然你提到可以不用去考虑具体的生成过程,但这不正是使用该模式的关键之处吗,我无法明白不考虑这个的情况下使用类工厂模式的意义.另外对楼上所说的反射机制很感兴趣,似乎有另外的实现方法.  回复  更多评论
  

# re: Java Web Application开发日志之二--“Ignore how they were created”,工厂方法模式应用 2006-03-02 14:21 marco
grakiss,对于客户端来讲,所谓的工厂就是用来封装“更里面”的“抽象-具体”也就是“接口-具体类”的层级结构,让客户端能“只”关注抽象也就是接口。

所以,换句话讲,在工厂方法模式中,对工厂的层级结构中,的确如你所说,是不能隐藏工程的具体类了。

但是,你再换个思路想想,由这个工厂层级结构,更里面的层级结构,如这个例子中的各种学生的具体类型,真的对于客户端已经隐藏起来了,而只把抽象的接口暴露给了客户端。

所以,从这个角度来讲,我们不是要隐藏一切“抽象-具体”的层级结构,而只是部分的,必要的。如上面的工厂就是隐藏不了了。

不过,也如我上面文章写的,隐藏不了具体工厂,其实没有任何坏处。是可以接受的。毕竟我们对于具体产品的制定,在工厂模式中就转换为对具体工厂的制定。

-----------------
再强调,工厂模式的根本目的是“隐藏具体产品,只让客户端看到抽象产品”,你再品味一下,呵呵,我的话完了。  回复  更多评论
  

# re: Java Web Application开发日志之二--“Ignore how they were created”,工厂方法模式应用 2006-03-02 14:34 marco
另外,对于你所的,为什么可以在设计中忽略工厂模式这个问题。

我想说的是,假设你的类设计中,存在大量的接口,抽象类这样的东西。而且类与类之间,也大部分是通过“这些东西“关联起来的时候。

正如你所说,是很难不去考虑,当你真正要用的时候,到底上来的是那个具体类。

但是,我知道工厂模式肯定最后能帮我解决这个。所以,才暂时忽略,而不是不搞了。等我的设计都搞好了,我再把需要的工厂都补上去。

对我来说,这个补充就是----仿佛是给”接口或抽象类“制造一个new方法。

所以,题目中的ignore不是说这真的不管,而只是说我相信一定可以搞定的意思。而在设计阶段不用把它摆在主要位置而已。

不知我有没有给哥们您讲清楚,再探讨,再探讨。  回复  更多评论
  

# re: Java Web Application开发日志之二--“Ignore how they were created”,工厂方法模式应用 2007-05-26 13:44 goodstuff
你讲的深入浅出,非常棒,要继续努力哦
争取做大陆的java的侯俊杰.  回复  更多评论
  


只有注册用户登录后才能发表评论。


网站导航: