不完美的库类

不完美的库类(Incomplete Library Class)

当一个类库已经不能满足实际需要时,你就不得不改变这个库(如果这个库是只读的,那就没辙了)。

问题原因

许多编程技术都建立在库类的基础上。库类的作者没用未卜先知的能力,不能因此责怪他们。麻烦的是库往往构造的不够好,而且往往不可能让我们修改其中的类以满足我们的需要。

解决方法

引入外加函数(Introduce Foreign Method)
引入本地扩展(Introduce Local Extension)

收益

  • 减少代码重复(你不用一言不合就自己动手实现一个库的全部功能,代价太高)

何时忽略

  • 如果扩展库会带来额外的工作量。

重构方法说明

引入外加函数(Introduce Foreign Method)

问题

你需要为提供服务的类增加一个函数,但你无法修改这个类。

class Report {//...void sendReport() {Date nextDay = new Date(previousEnd.getYear(),previousEnd.getMonth(), previousEnd.getDate() + 1);//...}
}复制代码

解决

在客户类中建立一个函数,并一个第一个参数形式传入一个服务类实例。

class Report {//...void sendReport() {Date newStart = nextDay(previousEnd);//...}private static Date nextDay(Date arg) {return new Date(arg.getYear(), arg.getMonth(), arg.getDate() + 1);}
}复制代码

引入本地扩展(Introduce Local Extension)

问题

你需要为服务类提供一些额外函数,但你无法修改这个类。

解决

建立一个新类,使它包含这些额外函数,让这个扩展品成为源类的子类或包装类。

中间人

中间人(Middle Man)

如果一个类的作用仅仅是指向另一个类的委托,为什么要存在呢?

问题原因

对象的基本特征之一就是封装:对外部世界隐藏其内部细节。封装往往伴随委托。但是人们可能过度运用委托。比如,你也许会看到一个类的大部分有用工作都委托给了其他类,类本身成了一个空壳,除了委托之外不做任何事情。

解决方法

应该运用 移除中间人(Remove Middle Man) ,直接和真正负责的对象打交道。

收益

  • 减少笨重的代码。

何时忽略

如果是以下情况,不要删除已创建的中间人:

  • 添加中间人是为了避免类之间依赖关系。
  • 一些设计模式有目的地创建中间人(例如代理模式和装饰器模式)。

重构方法说明

移除中间人(Remove Middle Man)

问题

某个类做了过多的简单委托动作。

解决

让客户直接调用委托类。

依恋情结

依恋情结(Feature Envy)

一个函数访问其它对象的数据比访问自己的数据更多。

问题原因

这种气味可能发生在字段移动到数据类之后。如果是这种情况,你可能想将数据类的操作移动到这个类中。

解决方法

As a basic rule, if things change at the same time, you should keep them in the same place. Usually data and functions that use this data are changed together (although exceptions are possible).

有一个基本原则:同时会发生改变的事情应该被放在同一个地方。通常,数据和使用这些数据的函数是一起改变的。

搬移函数(Move Method)
提炼函数(Extract Method)
提炼函数(Extract Method)

收益

  • 减少重复代码(如果数据处理的代码放在中心位置)。
  • 更好的代码组织性(处理数据的函数靠近实际数据)。

何时忽略

  • 有时,行为被有意地与保存数据的类分开。这通常的优点是能够动态地改变行为(见策略设计模式,访问者设计模式和其他模式)。

重构方法说明

搬移函数(Move Method)

问题

你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。

解决

在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。

提炼函数(Extract Method)

问题

你有一段代码可以组织在一起。

void printOwing() {printBanner();//print detailsSystem.out.println("name: " + name);System.out.println("amount: " + getOutstanding());
}复制代码

解决

移动这段代码到一个新的函数中,使用函数的调用来替代老代码。

void printOwing() {printBanner();printDetails(getOutstanding());
}void printDetails(double outstanding) {System.out.println("name: " + name);System.out.println("amount: " + outstanding);
}复制代码

狎昵关系

狎昵关系(Inappropriate Intimacy)

一个类大量使用另一个类的内部字段和方法。

问题原因

类和类之间应该尽量少的感知彼此(减少耦合)。这样的类更容易维护和复用。

解决方法

  • 最简单的解决方法是运用 搬移函数(Move Method) 和 搬移字段(Move Field) 来让类之间斩断羁绊。

  • 你也可以看看是否能运用 将双向关联改为单向关联(Change Bidirectional Association to Unidirectional) 让其中一个类对另一个说分手。

  • 如果这两个类实在是情比金坚,难分难舍,可以运用 提炼类(Extract Class) 把二者共同点提炼到一个新类中,让它们产生爱的结晶。或者,可以尝试运用 隐藏委托关系(Hide Delegate) 让另一个类来为它们牵线搭桥。

  • 继承往往造成类之间过分紧密,因为子类对超类的了解总是超过后者的主观愿望,如果你觉得该让这个子类自己闯荡,请运用 以委托取代继承(Replace Inheritance with Delegation) 来让超类和子类分家。

收益

  • 提高代码组织性。
  • 提高代码复用性。

重构方法说明

搬移函数(Move Method)

问题

你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。

解决

在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。

搬移字段(Move Field)

问题

在你的程序中,某个字段被其所驻类之外的另一个类更多地用到。

解决

在目标类新建一个字段,修改源字段的所有用户,令他们改用新字段。

将双向关联改为单向关联(Change Bidirectional Association to Unidirectional)

问题

两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。

解决

去除不必要的关联。

提炼类(Extract Class)

问题

某个类做了不止一件事。

解决

建立一个新类,将相关的字段和函数从旧类搬移到新类。

隐藏委托关系(Hide Delegate)

问题

客户通过一个委托类来调用另一个对象。

解决

在服务类上建立客户所需的所有函数,用以隐藏委托关系。

以委托取代继承(Replace Inheritance with Delegation)

问题

某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。

解决

在子类中新建一个字段用以保存超类;调整子类函数,令它改而委托超类;然后去掉两者之间的继承关系。

过度耦合的消息链

过度耦合的消息链(Message Chains)

消息链的形式类似于: obj.getA().getB().getC() 。

问题原因

如果你看到用户向一个对象请求另一个对象,然后再向后者请求另一个对象,然后再请求另一个对象……这就是消息链。实际代码中你看到的可能是一长串 getThis()或一长串临时变量。采取这种方式,意味客户代码将与查找过程中的导航紧密耦合。一旦对象间关系发生任何变化,客户端就不得不做出相应的修改。

解决方法

  • 可以运用 隐藏委托关系(Hide Delegate) 删除一个消息链。

  • 有时更好的选择是:先观察消息链最终得到的对象是用来干什么的。看看能否以 提炼函数(Extract Method) 把使用该对象的代码提炼到一个独立函数中,再运用 搬移函数(Move Method) 把这个函数推入消息链。

收益

  • 能减少链中类之间的依赖。
  • 能减少代码量。

何时忽略

  • 过于侵略性的委托可能会使程序员难以理解功能是如何触发的。

重构方法说明

隐藏委托关系(Hide Delegate)

问题

客户通过一个委托类来调用另一个对象。

解决

在服务类上建立客户所需的所有函数,用以隐藏委托关系。

提炼函数(Extract Method)

问题

你有一段代码可以组织在一起。

void printOwing() {printBanner();//print detailsSystem.out.println("name: " + name);System.out.println("amount: " + getOutstanding());
}复制代码

解决

移动这段代码到一个新的函数中,使用函数的调用来替代老代码。

void printOwing() {printBanner();printDetails(getOutstanding());
}void printDetails(double outstanding) {System.out.println("name: " + name);System.out.println("amount: " + outstanding);
}复制代码

搬移函数(Move Method)

问题

你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者,或被后者调用。

解决

在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是旧函数完全移除。

Java开发中存在这样的代码,反而影响整体整洁和可读性相关推荐

  1. 编写高质量代码:改善Java程序的151个建议 (第1章 Java开发中通用的方法和准则)

    第1章 Java开发中通用的方法和准则 The reasonable man adapts himself to the world;the unreasonable one persists in ...

  2. Java开发中文件读取方式总结

    JAVA开发中,免不了要读文件操作,读取文件,首先就需要获取文件的路径.路径分为绝对路径和相对路径. 在文件系统中,绝对路径都是以盘符开始的,例如C:abc1.txt. 什么是相对路径呢?相对路径就是 ...

  3. Java开发中更多常见的危险信号

    在< Java开发中的常见危险信号>一文中,我研究了一些不一定本身就是错误或不正确的做法,但它们可能表明存在更大的问题. 这些"红色标记"类似于"代码气味&q ...

  4. Java开发中的常见危险信号

    在开发,阅读,复审和维护成千上万行Java代码的几年中,我已经习惯于看到Java代码中的某些" 危险信号 ",这些信号通常(但可能并非总是)暗示着代码问题. 我不是在谈论总是错误的 ...

  5. java 线程 john_教你Java开发中多线程同步技巧

    教你Java开发中多线程同步技巧 在编写一个类时,如果该类中的代码可能运行于多线程环境下,那么就要考虑同步的问题.在Java中内置了语言级的同步原语--synchronized,这也大大简化了Java ...

  6. Java开发中巧妙使用链表来实现模拟栈的入栈出栈操作

    2019独角兽企业重金招聘Python工程师标准>>> 在Java开发中经常会碰到需要你用Java链表来实现入栈出栈的模拟操作,下文就这个操作做了一个详细的描述. 栈:后进先出:最后 ...

  7. Java开发中常用的设计模式-单例模式

    单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式. Java开发中常用的设计模式-单例模式 单例模式有3个特点: 单例类只有一个实例对象: 该单例对象必须 ...

  8. Java开发中常见的危险信号(中)

    本文来源于我在InfoQ中文站原创的文章,原文地址是:http://www.infoq.com/cn/news/2013/12/common-red-flags-in-java-1 Dustin Ma ...

  9. Java开发中常见的危险信号(下)

    本文来源于我在InfoQ中文站原创的文章,原文地址是:http://www.infoq.com/cn/news/2013/12/common-red-flags-in-java-3 Dustin Ma ...

  10. Java开发中常见的危险信号

    Dustin Marx是一位专业软件开发者,从业已经有17年的时间,他拥有电子工程学士学位,还是一位MBA.Dustin维护着一个博客,专门介绍软件开发的各个主题.近日,他撰文谈到了Java开发中常见 ...

最新文章

  1. 介绍 Saltstack批量管理文件和计划任务
  2. curl header 自定义参数接收不到_如何快速有效的通过curl命令来检测url告警机制?...
  3. oracle数据字典表与视图
  4. 千位亿万富豪调研:六成富豪担心继承人婚姻问题致财富外流
  5. makefile中变量有哪些?
  6. 微信小程序 悬浮按钮
  7. docker 删除所有镜像_Docker常用命令
  8. wed6699整站程序下载【首发】
  9. deny后加to do还是doing_become to do还是doing
  10. 组合枚举——妖梦拼木棒(洛谷 P3799)
  11. hibernate4.3 无法获取数据库最新值
  12. 华为8lite支持云闪付吗_2K/120Hz屏?华为P40Pro尊享版价格曝光 | 一加8曝4.15发布
  13. html旋转360度特效,CSS3图片旋转特效(360/60/-360度)?
  14. ear的英语怎么念_高中英语快速记忆法有哪些?
  15. ireport导出Excel报表的实现
  16. web 常见的攻击方式
  17. 转图精灵算法分析!!
  18. 【Linux开发】Orange Pi上WiringPi的使用
  19. 谁对云服务器安全负责:客户还是供应商 ?
  20. showdoc内容备份

热门文章

  1. Jetty9开发(1)
  2. String类常用方法解析
  3. 怎么克隆计算机的mac,选中克隆为当前电脑MAC地址
  4. 逆转裁判Ace Attorney
  5. do还是doing imagine加to_“imagine to do”与“imagine doing”的区别是什么?
  6. 01-locale-fix.sh的/usr/bin/locale-check: No such file or directory解决 Ubuntu 18.04 20.04
  7. java之简易生成彩色二维码实践
  8. Photoshop CC 2017安装
  9. python 阮一峰_阮一峰关于 Javascript 中闭包的解读是否正确?
  10. android layout.inflater,LayoutInflater.inflate详解