写这篇博文,是因为在论坛里看到了一篇有关这一话题的帖子,产生了一些想法,自知记忆力较差,便急切的想找到个地方记录下来,以便日后温故。
我所说的帖子的地址是http://topic.csdn.net/u/20110120/12/4ee6335d-247d-4c1f-8c9d-8d70f44ddaba.html
提问者提出了这样的问题:
Shape是抽象基类,Triangle和Rectangle继承自Shape,Draw()、SaveToXml()、LoadFromXml()是三个虚函数。
按照SRP(单一职责原则),把这三个虚函数放在一个类中,是否不好?应该怎么做?分别创建三个子类去实现?
class Shape{
public:
virtual Draw()=0;
virtual SaveToXml()=0;
virtual LoadFromXml()=0;
};
class Triangle public: Shape{
...
};
class Rectangle public: Shape{
...
};
个人认为8楼的解答最给力:
class Shape{
public:
virtual Draw()=0;
};
class XmlPersist
{public:
virtual SaveToXml()=0;
virtual LoadFromXml()=0;
};
class Triangle public: Shape,public XmlPersist{
...
};
class Rectangle public: Shape,public XmlPersist {
...
};
现在,说说我对这个问题的理解。
人们通常认为,之所以要坚持这样的一个原则,目的是要一个类只做一件事情。可是怎么样的事情才算是一件事情呢?考试算是一件事情么?如果算,那么考试中还包含了复习,准备考试用具,答题,交卷子等一系列的事情,这样想来考试算是一件事情么?
更深奥的解释是,让一个类只有一个可以引起它变化的原因。这样的解释就更让人难以理解了,软件概念如此复杂,如此多变,所谓的这一个引起变化的原因到底是指一种情况,还是一个因素亦或是一个条件?
为什么一个类的职责不宜过多?有人会说那样在使用或修改时会遇到麻烦,可是为什么一个类的职责过多时就会导致修改或使用时遇到麻烦呢?这里的职责又是什么概念?一个函数代表一个职责么?还是一个类代表一个职责?还是一个模块代表一个职责?
我想起《人月神话》中的一句话,开发软件的主要根本任务是打造抽象软件实体的复杂概念结构。我这样理解这句话:人,通过一系列手段和方法将现实生活中的事物映射成复杂的软件概念,在这个映射的过程中,我们采取各种各样的设计模式,各种原则,无非是想使映射后的软件概念最大限度的反应真实的事物。
我之所以人为8楼的答案最给力,并不是因为他的方法符合了职责单一性原则,而是在于我人为他的方法最大程度的反应了真实的事物(事实上我现在仍不理解单一职责)。
问一问自己,什么是Shape?当你只考虑什么是Shape的时候,你会考虑到读取文件么?当你只考虑打开一个XML文件,写一个XML文件时,你会考虑到Shape么?Shape本身和XML文件,这两者之间本身没有任何关系。从根本上讲,他们完全是两个不同的问题域里的东西。你也许会说,他们之间终究是有联系的呀,错,他们之间没有任何联系,你所说的联系是软件概念中的联系,我所说的没有联系,是现实世界中的联系。可如果我们让他们在软件概念中仍和现实世界中一样,没有任何联系,那么似乎这段程序,或者说功能就没有办法实现了。
为了实现其功能,我们只好让Triangle继承两个完全没有联系的类,如此一来,我们所期望的软件概念中的联系被Triangle实现,而两个基类则最大程度的反映了现实事物间的关系。如此,两个基类都做了自己该做的事情,而且是一件事情。但是新的问题出现了,似乎Triangle做了两件事情,一件是Draw(),一件是把自己保存到XML文件中和从XML文件中获取自己。
那么这又该如何应对呢?以笔者之愚见,Draw()这个方法根本不是Shape及其子类该做的事情,如果是我设计这段代码,我更倾向于将这三个方法放到一个抽象类(Operate)里,这个类专门负责对图形的操作,这样,Shape类及其子类最大程度上反应了现实世界中的事物,而我所说的Operate虽然在现实世界中找不到一个与之对应的具体实物,但它可以找到一个与之对应的抽象概念------操作图形。
对于这个问题,我的思考还很混乱,也希望博友们能给些意见,更欢迎大家批评指正。
原文链接:https://www.f2er.com/javaschema/287025.html