敏捷开发的必要技巧:保持代码简洁

来源:互联网 发布:网络红猫红小胖 编辑:程序博客网 时间:2024/04/28 04:36
单一职责原则(The Single Responsibility Principle)认为:每个类都应该只为一个理由而修改。当一个类包含许多其他的功能时,就违反了单一职责原则。作者通过简单的例子,展示了如何让你的代码更加简洁.

敏捷开发的必要技巧:保持代码简洁

Wingel 发表于 2006-12-28 13:41:50
作者:Wingel     来源:www.matrix.org.cn

摘要:

单一职责原则(The Single Responsibility Principle)认为:每个类都应该只为一个理由而修改。当一个类包含许多其他的功能时,就违反了单一职责原则。作者通过简单的例子,展示了如何让你的代码更加简洁.

 
示例
    这是一个会议管理系统。它用来管理所有参会者的信息。刚开始的时候,我们只需要记录每个参会者的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下载:
敏捷开发的必要技巧

0 0
原创粉丝点击