Java学习者论坛

 找回密码
 立即注册

QQ登录

只需一步,快速开始

手机号码,快捷登录

恭喜Java学习者论坛(https://www.javaxxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,购买链接:点击进入购买VIP会员
JAVA高级面试进阶视频教程Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程

Go语言视频零基础入门到精通

Java架构师3期(课件+源码)

Java开发全终端实战租房项目视频教程

SpringBoot2.X入门到高级使用教程

大数据培训第六期全套视频教程

深度学习(CNN RNN GAN)算法原理

Java亿级流量电商系统视频教程

互联网架构师视频教程

年薪50万Spark2.0从入门到精通

年薪50万!人工智能学习路线教程

年薪50万!大数据从入门到精通学习路线年薪50万!机器学习入门到精通视频教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程 MySQL入门到精通教程
查看: 399|回复: 0

[设计模式学习]《31天重构》(5)

[复制链接]
  • TA的每日心情
    开心
    2021-3-12 23:18
  • 签到天数: 2 天

    [LV.1]初来乍到

    发表于 2014-11-1 00:00:05 | 显示全部楼层 |阅读模式
    八、以委托取代继承

         在这文章之前本来应该有“《31天重构》七:重命名(方法、类、参数)”这篇文章的,但是由于我个人认为正确、合适地对一些变量进行命名是每位程序员都应该养成的好习惯,适当的命名对我们理解源代码很有帮助的,原作者认为这是最重要、最常用的重构手法,但是我认为这不用再赘述了,原文请点击这里。 回归本文主题,以委托取代继承是指我们常常为了一时的便利而错误地在代码中使用到了继承机制。我们知道,继承可以使得子类获得了父类的非私有方法、属性,而我们却正好是看中了这种无形中的便利而不正当地在逻辑上几乎不相关的类之间使用了继承,实质上这种为追求便利的继承是无意义的。具体请看下面的代码。 重构前:
      
       
       
         
       

         
       
      

    1. class Sanitation {
    2.         public String washHands() {
    3.                 return "Cleaned ...";
    4.         }
    5. }
    6. class Child extends Sanitation {
    7.        
    8. }
    复制代码
    在这里,卫生设备 Sanitation 类(假设为水龙头)具有给人们洗手的方法 washHands() ,但是 这里的 Child 类想要具有 washHands() 的方法,但是完全不可以像上面那样子去 extends Sanitation 类啊,这两个类之间完全没有一点支持继承的联系。为了打破我们这种无意义的继承,以及消除这种带来不良影响的便利,我们应该进行重构,利用委托来实现。重构第一步,先保留 extends 关系,这样便于一点点地检测我们的重构是否出错,如下:

    1. class Sanitation {
    2.         public String washHands() {
    3.                 return "Cleaned ...";
    4.         }
    5. }
    6. // 重构是一小步一小步进行的,先保留 extends 关系
    7. class Child extends Sanitation{
    8.        
    9.         // 具有委托对象 Sanitation 的实例
    10.         private Sanitation sanitation;
    11.        
    12.         public Child() {
    13.                 // 为委托对象赋值为当前对象,
    14.                 // 因为我们还保留了 extends 关系
    15.                 this.sanitation = this;
    16.         }
    17.        
    18.         public String washHands() {
    19.                 // 这里使用委托获得  washHands() 方法
    20.                 return (this.sanitation.washHands());
    21.         }
    22. }
    复制代码
    当我们编译发现没有错误时(这时还不能运行,会发生栈溢出!),立即将上面 Child 类中的 extends 去掉,并记得将构造函数中的 this 引用改成真正的创建一个 Sanitation 对象。重构第二步:

    1. class Child {
    2.        
    3.         // 具有委托对象 Sanitation 的实例
    4.         private Sanitation sanitation;
    5.        
    6.         public Child() {
    7.                 // 删除 extends 关系
    8.                 this.sanitation = new Sanitation();
    9.         }
    10.        
    11.         public String washHands() {
    12.                 // 这里使用委托获得  washHands() 方法
    13.                 return (this.sanitation.washHands());
    14.         }
    15. }
    复制代码
    至此,重构成功了!这才可以运行测试了。 小结:用委托取代这种“莫名其妙”的继承关系,是比较符合逻辑的。像 Child 和 Sanitation 两个类,根本就不是一个继承树上的,原先代码结构表现出来的也是毫无意义的。此外,个人认为,《重构》这本书上对于每个重构的步骤讲得比较详细,但是《31天重构》这本书的这一篇文章中的这个例子比《重构》中的似乎更合理些,因为这里的代码是用了一个逻辑上没有继承关系的例子,就想上文。具体的请参考两本书吧。

    九、提取接口(Extract Interface)
        当我们注意到在我们代码结构中超过一个类使用到另一个特殊类中的同一个方法时,此时应该将该类中的方法提取出来放到一个接口( interface )中,并对外提供这个接口以便被用户类访问、使用,这样有利于打破原来多个类与这个特殊类的依赖关系。这个重构手法很容易实现,更重要的是这样做有利于松耦合。 先瞧瞧重构前的代码:

    1. class ClassRegistration {
    2.        
    3.         public void createCode() {
    4.                 // create registration code
    5.         }
    6.        
    7.         public void transfer() {
    8.                 // class transfer code
    9.         }
    10.        
    11.         public double getTotal() {
    12.                 return 0.01;
    13.         }
    14. }
    15. class RegistrationProcessor {
    16.        
    17.         public double processRegistration(ClassRegistration registration) {
    18.                 registration.createCode();
    19.                 return registration.getTotal();
    20.         }
    21. }
    复制代码
    在上面例子中,如果有多个不同的 RegistrationProcessor 类需要自主性地选择不同的注册方式,也就是说,注册 ClassRegistration 类同样有多个不同的实现方法。但是在上面的代码中, RegistrationProcessor 类很明显已经与单独一个 ClassRegistration 类紧耦合了,并不能选择性地与其他具体的实现相关联。 为了解决这种不足,我们把 ClassRegistration 类中的 create() 方法提取出来,再放到一个新的接口( interface )中,先看重构后的代码:

    1. interface IRegistration {
    2.        
    3.         public void createCode();
    4.        
    5.         public double getTotal();
    6. }
    7. class ClassRegistration implements IRegistration {
    8.        
    9.         @Override
    10.         public void createCode() {
    11.                 // create registration code
    12.         }
    13.        
    14.         public void transfer() {
    15.                 // class transfer code
    16.         }
    17.        
    18.         @Override
    19.         public double getTotal() {
    20.                 return 0.01;
    21.         }
    22. }
    23. class RegistrationProcessor {
    24.        
    25.         public double processRegistration(IRegistration registration) {
    26.                 registration.createCode();
    27.                 return registration.getTotal();
    28.         }
    29. }
    复制代码
    重构之后, ClassRegistration 类实现了 IRegistration 接口,并实现了该接口中所声明的所有方法,在 ClassRegistration 类中用 @Override 表明了。注意,最重要的是 RegistrationProcessor 类(也就是客户类)在自己的 processRegistration() 方法中,其参数类型不再是具体的 ClassRegistration 类,而是 IRegistration 接口。如此一来,我们可以提供多个实现了 IRegistration 接口的不同实现类,然后由具体传入 processRegistration() 方法的参数来运行时确定究竟是哪种实现。 实际上,这是我们常说的“面向接口编程”,也就是运用多态机制的好处。重点在于用户类不必与具体的实现类直接依赖,而是依赖于抽象的接口。

    本文出自 “蚂蚁” 博客,请务必保留此出处http://haolloyin.blog.51cto.com/1177454/347473



      
      
       
       

         
       

         
       
      
    复制代码
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|Java学习者论坛 ( 声明:本站资料整理自互联网,用于Java学习者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-2-25 17:29 , Processed in 0.347663 second(s), 34 queries .

    Powered by Discuz! X3.4

    © 2001-2017 Comsenz Inc.

    快速回复 返回顶部 返回列表