敏捷开发的必要技巧:保持代码简洁
Wingel 发表于 2006-12-28 13:41:50作者:Wingel 来源:原创
评论数:10 点击数:2,236 投票总得分:10 投票总人次:2
关键字:敏捷;代码简洁
摘要:
单一职责原则(The Single Responsibility Principle)认为:每个类都应该只为一个理由而修改。当一个类包含许多其他的功能时,就违反了单一职责原则。作者通过简单的例子,展示了如何让你的代码更加简洁.示例
这是一个会议管理系统。它用来管理所有参会者的信息。刚开始的时候,我们只需要记录每个参会者的ID(这是会议组织者分配的),姓名,电话和地址就行。于是,我们写了如下的代码:
接着,新的需求来了:现在每个参会者都可以让组织者帮忙预订酒店,所以我们要记录下他想预订的酒店名,入住日期,离开日期,房间类型(单人房或者双人房)。于是我们又扩充成如下的代码:
接着,又有一个新的需求来了:参会者可以参加不同的研讨会,所以我们要记录下参会者参加的研讨会。对于他要参加的每一场研讨会,我们还要记录下他的登记时间,同时他还需要什么翻译设备。于是代码又扩充成:
代码开始肿胀起来了
请注意,这已经是我们第二次扩充Participant这个类了。每扩充一次,它就包含了更多的代码(实例变量和方法)及更多的功能。本来它只有4个属性。现在已经是12个了!此外,这个类要处理的业务逻辑也极大的增加了。本来它只需要处理参会者的基本信息(姓名,地址等等),现在它还要包含酒店,酒店预订,研讨会和翻译设备等等的逻辑。如果以后新的需求又来了,我们又要扩充Participant这个类,到时候,这个类要复杂庞大成什么样子!
所以我们得修整这个类了!
那怎么修整Participant这个类呢?怎么让它一直保持在第一天那样的简洁度?在回答这两个问题之前,我们先来考虑一下另一个需要优先回答的问题:给你一个类,你怎么认定它需要修整?
怎么判断一个类需要修整
要判断一个类是否需要修整,一个比较主观的方法是:当在读一个类的代码时,看看我们会不会觉得这个类“太长了”,“太复杂了”,或者讲的概念“太多了”?如果会这样觉得的话,我们就认定,这个类需要修整。
另外一个比较简单而且客观的方法是:当发现我们已经在第二次或者第三次扩充这个类的时候,我们认定这个类要修整了。这是一个比较”懒惰,被动”的方法,但却很有效。
现在让我们看一下怎么修整Participant这个类吧。
抽取出有关酒店预订的功能
首先,先来考虑一下怎么抽取出酒店预订的功能。一个可行的方案是:
现在,Participant这个类就一点都不知道酒店预订的存在。当然,我们不一定要用数组来存放酒店预订情况。比如,我们可以用Map:
这样的方案优点是Participant一点都不知道HotelBooking的存在,Participant不依赖于HotelBooking。
还有另一个可行的方案是:
注意到,在这种方案里面,Participant这个类还是要知道HotelBooking的存在,也就是说,Participant还是要知道有酒店预订这回事。只是具体酒店预订是怎么做的,这些真正的功能是放在HotelBooking这个里面实现的。因为每个Participant都直接引用了本人的酒店预订情况,所以可以直接找到他的酒店预订情况。而代价就是,Pariticipant还是要知道酒店预订的概念。从类的关系来讲,Pariticipant还要依赖HotelBooking这个类。
当然,除了以上几种情况,还有许多其他的可行方案。
抽取研讨会的相关功能
现在我们来考虑一下怎么抽取出研讨会的功能。一个可行的方案:
当然,除了以上的方案以外,还有其他可行的方案,这里就先不讲。
改进后的代码
下面就是改进后的代码:
引述
单一职责原则(The Single Responsibility Principle)认为:每个类都应该只为一个理由而修改。当一个类包含许多其他的功能时,很明显违反了单一职责原则。要看更多细节的话,可以看:
http://www.objectmentor.com/resources/articles/srp.
http://c2.com/cgi/wiki?OneResponsibilityRule
系列的PDF下载:
敏捷开发的必要技巧
这是一个会议管理系统。它用来管理所有参会者的信息。刚开始的时候,我们只需要记录每个参会者的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下载:
敏捷开发的必要技巧
本页页面地址:
用户评论列表
要是可以重新format一下就更好了。
深受启发,虽然平时写代码,有意识这样做,但是有时为了赶工,忽略这一点!看来以后还得加强!
按咱们的国情,谁会给你这个仔细考虑的时间?!!!
在新建一个类的时候,是按这个类是否一个对象来建立,这个对象中包括其属性和相关操作?一个功能就建立一个新的类,这个是否应该这样做???有点不明白了。
还有一个问题,是不是应该把数据和操作封装在对象里么?
在国内的软件公司估计是不会考虑这么多的,关键是软件人员的水平问题和开发时间问题。这些可以当理论学习一番,实践不太实现。。。
类设计:要牢记single responsible;宁多勿强..呵呵
为什么总是说国内就不行呢,其实前期良好的设计,能有效的减少开发时间和维护开销;关键是自己要把握好。
同意kualer 的看法,,,随便就开一个新类不可取,,我觉得主要是看数据库的设计,如果你是在一个表中添加一个字段,那当然也是在类中了添加一个,,但如果你是开一张新表来处理,那当然了也是新建一个类了,
发表我的评论 (评论可增加个人积分...)
| 用户*: | E-mail: | ||
| 评论内容*: 支持BBCode |
|||
| 算术题*: | + = | ||

ICP:?B2-20040367