这是一个会议管理系统。它用来管理所有参会者的信息。刚开始的时候,我们只需要记录每个参会者的ID(这是会议组织者分配的),姓名,电话和地址就行。于是,我们写了如下的代码:
class Participant {
String id;
String name;
String telNo;
String address;
}
class ConferenceSystem {
Participant participants[]
}
接着,新的需求来了:现在每个参会者都可以让组织者帮忙预订酒店,所以我们要记录下他想预订的酒店名,入住日期,离开日期,房间类型(单人房或者双人房)。于是我们又扩充成如下的代码:
class Participant {
String id;
String name;
String telNo;
String address;
boolean bookHotelForHim;
String hotelName;
Date checkInDate;
Date checkOutDate;
boolean isSingleRoom;
void setHotelBooking(String hotelName, Date checkInDate, ...) {
...
}
}
接着,又有一个新的需求来了:参会者可以参加不同的研讨会,所以我们要记录下参会者参加的研讨会。对于他要参加的每一场研讨会,我们还要记录下他的登记时间,同时他还需要什么翻译设备。于是代码又扩充成:
class Participant {
String id;
String name;
String telNo;
String address;
boolean bookHotelForHim;
String hotelName;
Date checkInDate;
Date checkOutDate;
boolean isSingleRoom;
String idOfSeminarsRegistered[];
Date seminarRegistrationDates[];
boolean needSIDeviceForEachSeminar[];
void setHotelBooking(String hotelName, Date checkInDate, ...) {
...
}
void registerForSeminar(String seminarId, Date regDate, boolean needSIDevice) {
//将seminarId加到idOfSeminarsRegistered
//将regDate加到seminarRegistrationDates
//将needSIDevice加到needSIDeviceForEachSeminar.
}
boolean isRegisteredForSeminar(String seminarId) {
...
}
Date getSeminarRegistrationDate(String seminarId) {
...
}
boolean needSIDeviceForSeminar(String seminarId) {
...
}
String [] getAllSeminarsRegistered() {
return idOfSeminarsRegistered;
}
}
代码开始肿胀起来了
请注意,这已经是我们第二次扩充Participant这个类了。每扩充一次,它就包含了更多的代码(实例变量和方法)及更多的功能。本来它只有4个属性。现在已经是12个了!此外,这个类要处理的业务逻辑也极大的增加了。本来它只需要处理参会者的基本信息(姓名,地址等等),现在它还要包含酒店,酒店预订,研讨会和翻译设备等等的逻辑。如果以后新的需求又来了,我们又要扩充Participant这个类,到时候,这个类要复杂庞大成什么样子!
所以我们得修整这个类了!
那怎么修整Participant这个类呢?怎么让它一直保持在第一天那样的简洁度?在回答这两个问题之前,我们先来考虑一下另一个需要优先回答的问题:给你一个类,你怎么认定它需要修整?
怎么判断一个类需要修整
要判断一个类是否需要修整,一个比较主观的方法是:当在读一个类的代码时,看看我们会不会觉得这个类“太长了,“太复杂了,或者讲的概念“太多了?如果会这样觉得的话,我们就认定,这个类需要修整。
另外一个比较简单而且客观的方法是:当发现我们已经在第二次或者第三次扩充这个类的时候,我们认定这个类要修整了。这是一个比较懒惰,被动的方法,但却很有效。
现在让我们看一下怎么修整Participant这个类吧。
抽取出有关酒店预订的功能
首先,先来考虑一下怎么抽取出酒店预订的功能。一个可行的方案是:
class Participant {
String id;
String name;
String telNo;
String address;
}
class HotelBooking {
String participantId;
String hotelName;
Date checkInDate;
Date checkOutDate;
boolean isSingleRoom;
}
class HotelBookings {
HotelBooking hotelBookings[]
void addBooking(HotelBooking booking) {
...
}
}
class ConferenceSystem {
Participant participants[]
HotelBookings hotelBookings;
}
现在,Participant这个类就一点都不知道酒店预订的存在。当然,我们不一定要用数组来存放酒店预订情况。比如,我们可以用Map:
class Participant {
String id;
String name;
String telNo;
String address;
}
class HotelBooking {
String participantId;
String hotelName;
Date checkInDate;
Date checkOutDate;
boolean isSingleRoom;
}
class HotelBookings {
HashMap mapFromPartIdToHotelBooking;
//必须提供参会者id
void addBooking(String participantId, HotelBooking booking) {
...
}
}
class ConferenceSystem {
Participant participants[];
HotelBookings hotelBookings;
}
这样的方案优点是Participant一点都不知道HotelBooking的存在,Participant不依赖于HotelBooking。
还有另一个可行的方案是:
class Participant {
String id;
String name;
String telNo;
String address;
HotelBooking hotelBooking;
}
class HotelBooking {
String hotelName;
Date checkInDate;
Date checkOutDate;
boolean isSingleRoom;
}
class ConferenceSystem {
Participant participants[];
}
注意到,在这种方案里面,Participant这个类还是要知道HotelBooking的存在,也就是说,Participant还是要知道有酒店预订这回事。只是具体酒店预订是怎么做的,这些真正的功能是放在HotelBooking这个里面实现的。因为每个Participant都直接引用了本人的酒店预订情况,所以可以直接找到他的酒店预订情况。而代价就是,Pariticipant还是要知道酒店预订的概念。从类的关系来讲,Pariticipant还要依赖HotelBooking这个类。
当然,除了以上几种情况,还有许多其他的可行方案。
抽取研讨会的相关功能
现在我们来考虑一下怎么抽取出研讨会的功能。一个可行的方案:
class Participant {
String id;
String name;
String telNo;
String address;
}
class SeminarRegistration {
String participantId;
String seminarId;
Date registrationDate;
boolean needSIDevice;
}
class SeminarRegistry {
SeminarRegistration registrations[]
void registerForSeminar(SeminarRegistration registration) {
//将registration加到registrations.
}
boolean isRegisteredForSeminar(String participantId, String seminarId) {
...
}
Date getSeminarRegistrationDate(String participantId, String seminarId) {
...
}
boolean needSIDeviceForSeminar(String participantId, String seminarId) {
...
}
SeminarRegistration[] getAllRegistrations(String participantId) {
...
}
}
class ConferenceSystem {
Participant participants[]
SeminarRegistry seminarRegistry;
}
当然,除了以上的方案以外,还有其他可行的方案,这里就先不讲。
改进后的代码
下面就是改进后的代码:
class Participant {
String id;
String name;
String telNo;
String address;
}
class HotelBooking {
String participantId;
String hotelName;
Date checkInDate;
Date checkOutDate;
boolean isSingleRoom;
}
class HotelBookings {
HotelBooking hotelBookings[]
void addBooking(HotelBooking booking) {
...
}
}
class SeminarRegistration {
String participantId;
String seminarId;
Date registrationDate;
boolean needSIDevice;
}
class SeminarRegistry {
SeminarRegistration registrations[]
void registerForSeminar(SeminarRegistration registration) {
//将registration加到registrations.
}
boolean isRegistered (String participantId, String seminarId) {
...
}
Date getRegistrationDate(String participantId, String seminarId) {
...
}
boolean needSIDevice(String participantId, String seminarId) {
...
}
SeminarRegistration[] getAllRegistrations(String participantId) {
...
}
}
class ConferenceSystem {
Participant participants[]
HotelBookings hotelBookings;
SeminarRegistry seminarRegistry;
}
引述
单一职责原则(The Single Responsibility Principle)认为:每个类都应该只为一个理由而修改。当一个类包含许多其他的功能时,很明显违反了单一职责原则。要看更多细节的话,可以看:
http://www.objectmentor.com/resources/articles/srp.
http://c2.com/cgi/wiki?OneResponsibilityRule
系列的PDF下载:
敏捷开发的必要技巧
Java, java, J2SE, j2se, J2EE, j2ee, J2ME, j2me, ejb, ejb3, JBOSS, jboss, spring, hibernate, jdo, struts, webwork, ajax, AJAX, mysql, MySQL, Oracle, Weblogic, Websphere, scjp, scjd
标签: