科技行者

行者学院 转型私董会 科技行者专题报道 网红大战科技行者

知识库

知识库 安全导航

至顶网软件频道基础软件敏捷开发的必要技巧:慎用继承(3)

敏捷开发的必要技巧:慎用继承(3)

  • 扫一扫
    分享文章到微信

  • 扫一扫
    关注官方公众号
    至顶头条

抽取出父类中没必要的功能   现在,我们来看一下另一个例子。假定一个Component代表一个GUI对象,比如按钮或者文本框之类的。请认真阅读下面的代码:

作者:中国IT实验室 来源:中国IT实验室 2007年8月23日

关键字: java 继承

  • 评论
  • 分享微博
  • 分享邮件

抽取出父类中没必要的功能

  现在,我们来看一下另一个例子。假定一个Component代表一个GUI对象,比如按钮或者文本框之类的。请认真阅读下面的代码:

    abstract class Component {                                                                     
       boolean isVisible;                                                                          
       int posXInContainer;                                                                        
       int posYInContainer;                                                                        
       int width;                                                                                  
       int height;                                                                                
       ...                                                                                        
       abstract void paint(Graphics graphics);                                                    
       void setWidth(int newWidth) {                                                              
           ...                                                                                    
       }                                                                                          
       void setHeight(int newHeight) {                                                            
           ...                                                                                    
       }                                                                                          
    }                
    
    class Button extends Component {                                                              
       ActionListener listeners[];                                                                
       ...                                                                                        
       void paint(Graphics graphics) {                                                            
           ...                                                                                    
       }                                                                                          
    }                                                                                              

    class Container {
       Component components[];
       void add(Component component) {
           ...
       }    
    }
      

  假定你现在要写一个时钟clock组件。它是一个有时分针在转动的圆形的钟,每次更新时针跟分针的位置来显示当前的时间。因为这也是一个GUI组件,所以我们同样让它继承自Component类:

    class ClockComponent extends Component {
       ...  
       void paint(Graphics graphics) {
           //根据时间绘制当前的钟表图形
       }    
    }    
  

  现在我们有一个问题了:这个组件应该是个圆形的,但是它现在却继承了Component的width跟height属性,也继承了setWidth 和 setHeight这些方法。而这些东西对一个圆形的东西是没有意义的。

  当我们让一个类继承另一个类时,我们需要再三的想想:它们之间是否有继承关系?ClockComponent是一个Component吗?它跟其他的Compoent(比如Button)是一样的吗?

  跟ParticipantsInDB的那个案例相反的是,我们不得不承认ClockComponent确实也是一个Component,否则它就不能像其他的组件那样放在一个Container中。因此,我们只能让它继承Component类(而不是用“代理”)。

  它既要继承Component,又不要width, height, setWidth 和 setHeight这些,我们只好将这四样东西从Component里面拿走。而事实上,它也应该拿走。因为已经证明了,并不是所有的组件都需要这四样东西(至少ClockComponent不需要)。

如果一个父类描述的东西不是所有的子类共有的,那这个父类的设计肯定不是一个好的设计。

我们有充分的理由将这些移走。

  只是,如果我们从Component移走了这四样东西,那原来的那些类,比如Button就没了这四样东西,而它确实又需要这些的(我们假定按钮是方形的)。

  一个可行的方案是,创建一个RectangularComponent类,里面有width,height,setWidth和setHeight这四样。然后让Button继承自这个类:

    abstract class Component {
       boolean isVisible;
       int posXInContainer;
       int posYInContainer;
       ...  
       abstract void paint(Graphics graphics);
    }      

    abstract class RectangularComponent extends Component {
       int width;
       int height;                                                                                
       void setWidth(int newWidth) {                                                              
           ...                                                                                    
       }                                                                                          
       void setHeight(int newHeight) {                                                            
           ...                                                                                    
       }                                                                                          
    }    
    
    class Button extends RectangularComponent {                                                    
       ActionListener listeners[];                                                                
       ...                                                                                        
       void paint(Graphics graphics) {                                                            
           ...                                                                                    
       }                                                                                          
    }              
    
    class ClockComponent extends Component {                                                      
       ...                                                                                        
       void paint(Graphics graphics) {                                                            
           //根据时间绘制当前的钟表图形                                                      
       }                                                                                          
    }
                                                                                            

  这并不是唯一可行的方法。另一个可行的方法是,创建一个RectangularDimension,这个类持有这四个功能,然后让Button去代理这个类:

    abstract class Component {                                                                     
       boolean isVisible;                                                                          
       int posXInContainer;                                                                        
       int posYInContainer;                                                                        
       ...                                                                                        
       abstract void paint(Graphics graphics);                                                    
    }                
    
    class RectangularDimension {                                                                  
       int width;                                                                                  
       int height;                                                                                
       void setWidth(int newWidth) {                                                              
           ...                                                                                    
       }                                                                                          
       void setHeight(int newHeight) {                                                            
           ...                                                                                    
       }                                                                                          
    }      
    
    class Button extends Component {                                                              
       ActionListener listeners[];                                                                
       RectangularDimension dim;                                                                  
       ...                                                                                        
       void paint(Graphics graphics) {                                                            
           ...                                                                                    
       }                                                                                          
       void setWidth(int newWidth) {                                                              
           dim.setWidth(newWidth);                                                                
       }                                                                                          
       void setHeight(int newHeight) {                                                            
           dim.setHeight(newHeight);                                                              
       }                                                                                          
    }          
    
    class ClockComponent extends Component {                                                      
       ...                                                                                        
       void paint(Graphics graphics) {
           //根据时间绘制当前的钟表图形
       }    
    }  
    

总结    

  当我们想要让一个类继承自另一个类时,我们一定要再三的检查:子类会不会继承了一些它不需要的功能(属性或者方法)?如果是的话,我们就得认真再想想:它们之间有没有真正的继承关系?如果没有的话,就用代理。如果有的话,将这些不用的功能从基类转移到另外一个合适的地方去。


引述

  里斯科夫替换原则(LSP)表述:Subtype must be substitutable for their base types. 子类应该能够代替父类的功能。或者直接点说,我们应该做到,将所有使用父类的地方改成使用子类后,对结果一点影响都没有。或者更直白一点吧,请尽量不要用重载,重载是个很坏很坏的主意!更多的信息可以去:

http://www.objectmentor.com/resources/articles/lsp.pdf.
http://c2.com/cgi/wiki?LiskovSubstitutionPrinciple.

Design By Contract是个跟LSP有关的东西。它表述说,我们应该测试我们所有的假设。更多信息:

http://c2.com/cgi/wiki?DesignByContract.


系列的PDF下载:
敏捷开发的必要技巧:http://www.blogjava.net/Files/Wingel/%E6%95%8F%E6%8D%B7%E5%BC%80%E5%8F%91%E7%9A%84%E5%BF%85%E8%A6%81%E6%8A%80%E5%B7%A7%E5%AE%8C%E6%95%B4%E7%89%88.rar 查看本文来源
    • 评论
    • 分享微博
    • 分享邮件
    邮件订阅

    如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。

    重磅专题
    往期文章
    最新文章