浏览 5498 次
|
锁定老贴子 主题:再谈面向对象的思维方法
该帖已经被评为精华帖
|
|
|---|---|
| 作者 | 正文 |
|
时间:2004-03-25
这篇文章本来是回贴给Robbin的《面向对象的思维方法》的,后来想想把它单独提出来再讨论讨论可能会比较有意思,所以就又加了些内容,把它作为根帖发了吧。
Robbin的发mail的例子很好,我们就还是拿它来讨论吧。 引用 举个例子,要发广告邮件,广告邮件列表存在数据库里面。 那么实现的过程肯定是这样的: 1. 连接数据库,取邮件地址列表。 2. 遍历邮件列表,设定新邮件(Address, title, body) 3. 调用本机的qmail的sendmail命令发送邮件。 作为一个面向对象的程序员,在写代码之前我主要要考虑什么呢? 1. 怎么封装对象 2. 怎么降低耦合度 3. 怎么实现可重用,比如不从数据库读,而改从文件读怎么处理。再比如不用Qmail,改用Rmail怎么处理。 考虑了以上的问题后,我会这么实现: 1. 首先定义一个Mail DataObject [code:1] public class MailDTO { private String strAddress; private String strTitle; private String strBody; public MailDTO() {super();} public MailDTO(String strAddress, String strName, String strType) { setAddress(strAddress); setName(strName); setType(strType); } public String getAddress() {return this.strAddress;} public void setAddress(String strAddress) { this.strAddress = strAddress;} public String getTitle() {return this.strTitle;} public void setTitle(String strTitle) { this.strTitle = strTitle;} public String getBody() {return this.strBody);} public void setBody(String strBody) { this.strBody) = strBody);} } [/code:1] 2. 定义ListAddress接口 [code:1] public interface ListAddress { public Iterator getListAddress() } [/code:1] 3. 定义ListAddress接口的实现DbListAddress [code:1] public class DbListAddress implements ListAddress { public Iterator getListAddress() { //blablabla } } [/code:1] 以后你可以加FileListAddress, 只要implements ListAddress 4. 定义SendJunk接口 [code:1] public interface SendJunk { public void sendJunk(); } [/code:1] 5. 定义JunkData类 [code:1] public class JunkData { protected MailDTO mdto; public void setMailDTO(MailDTO mdto) { this.mdto = mdto; } } [/code:1] 6. 定义Qmail的SendJunk的实现 [code:1] public class QmailSendJunk extends JunkData implements SendJunk { public void sendJunk() { //blablabla } } [/code:1] 这里要注意的是它 extends JunkData, implements SendJunk,所以用的时候得这么写: [code:1] SendJunk sj = new QmailSendJunk(); sj.setMailDTO(mdto); sj.sendJunk(); [/code:1] 同样,以后你可以加Rmail, Smail, Tmail......只要extends JunkData implements SendJunk 7. 控制程序 [code:1] SendJunk sj = new QmailSendJunk(); ListAddress la = new DbListAddress(); MailDTO mdto = new MailDTO(); mdto.setTitle("good news"); mdto.setBody("blablabla"); Iterator it = la.getListAddress(); while (it.hasNext()) { String strAddress = (String)it.next(); mdto.setAddress(strAddress); sj.setMailDTO(mdto); sj.sendJunk(); } [/code:1] 这里要说的有两点: 1. 以后要改写的就是添加ListAddress和SendJunk这两个接口的实现,然后把控制程序的第1,2行换成相应的class。 2. 一个小Tip,尽量避免在while里面用new,否则如果你的iterator很长,系统消耗会很大。 声明:JavaEye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
|
|
| 返回顶楼 | |
|
时间:2004-03-25
总结的好, 支持。
|
|
| 返回顶楼 | |
|
时间:2004-03-25
bruce 写道 总结的好, 支持。
总结的不好,不支持 |
|
| 返回顶楼 | |
|
时间:2004-03-26
我写那篇文章的时候是从入门的角度来考虑的,所以没有想那么深入和细致,谢谢你的总结,以后改写这篇文章的时候就可以加入这部分内容,并且注明引用自xanada。
不过我想在你的总结基础上再加点东西,也许就更完美些: [code:1]SendJunk sj = new QmailSendJunk(); ListAddress la = new DbListAddress(); [/code:1] 考虑到这两行可以继续修改。之所以抽象出接口是了为了重用,不过此时抽换了接口实现类仍然要修改代码,应该写一个工厂类用来获得接口实现类,而不是直接硬编码。 [code:1]SendJunk sj = JunkFactory.getSendJunk(); ListAddress la = ListAddress.getListAddress();[/code:1] 然后在工厂类方法里面根据某些条件,例如类搜索的顺序,或者读配置文件,或者读命令行传入的参数来决定load哪种接口实现类。这样抽换接口实现类,就不必改代码了。 |
|
| 返回顶楼 | |
|
时间:2004-03-26
在下初来,很喜欢这里的气氛。希望Robbin你的论坛能够越办越好,牛人越聚越多。。。
|
|
| 返回顶楼 | |
|
时间:2004-03-27
Gamble ,Gamble never changes
|
|
| 返回顶楼 | |
|
时间:2004-03-27
[code:1]
public class QmailSendJunk extends JunkData implements SendJunk { public void sendJunk() { //blablabla } } [/code:1] 谈谈我的看法, 首先extends代表一种 is a 的关系, 尽管此处可以这样使用, 但如果说QmailSendJunk is a JunkData, 看起来QmailSendJunk中就一个sendJunk()方法, 所以这样的is a 关系不太合适。 我觉得此处可以使用bridge模式, 把JunkData看做bridge模式的抽象部分(叫abstraction), 把动作sendJunk()看做bridge模式的implementation部分。 随便举一个例子, 不一定贴切, 假设 JunkData 就是一种Data, 它有XML格式和HTML格式。所以abstraction这边的父类为Data, 它的两个子类为XMLData, HTMLData, implementation这边的父类为processMail, 它有两个子类有Qmail和Sendmail两种方式, 所以可以有四种组合: XMLData+Qmail, XMLData+Sendmail, HTMLData+Qmail, HTMLData+Sendmail. 我们可以通过了客户程序处任意指定这四种方式中的几种。 或者, 你可不可以直接用组合的方式,这样就不会是parent-chid的tightly coupling relationship了, 象这样: [code:1] public class QmailSendJunk JunkData implements SendJunk { private JunkData myJunkData = new JunkData(); public QmailSendJunk(MailDTO mdto){ myJunkData.setMailDTO(mdto); } public void sendJunk() { //blablabla } } [/code:1] 在重构中, 如果想保证接口不变,可以考虑proxy和decorator模式,其实它们也是在内部应用组合方式,接口要改变但以前的类内部不改动,可以考虑adaptor模式,它也是组合方式的应用。如果以前类的结构已经定下来,不好再改,但需要扩充类的功能,可以考虑使用visitor模式。这个模式是我最头痛的模式:(, 不过是越来越清楚了。 Robbin,说到的应是一种简单工厂模式, 通过传参数来决定不同类的实例化, 但如果可能的话, 我更愿意把产品抽象出来, 做一种工厂模式或抽象工厂模式, 好处就是代码以后的改动要比简单工厂模式少,这样所有的类的实例化推迟到客户端,是一种真正的面象对象的编程, 当然不好的就是比简单工厂麻烦。 |
|
| 返回顶楼 | |
|
时间:2004-04-02
为啥要一个JunkData类呢?而且还让QmailSendJunk 去继承它?
这样子让程序变得难以理解了。 |
|
| 返回顶楼 | |











