资讯专栏INFORMATION COLUMN

重构-改变既有代码的设计(八)--重新组织数据

qpal / 1634人阅读

摘要:并隐藏对象内与用户无关的数据结构。有一种变化行为被称为常量函数,它们会返回一个硬编码的值。尽管常量函数有其用途,但若与子类中只有常量函数,实在没有足够的存在价值。你可以在超类中设计一个与常量函数返回值相应的字段,从而完全除去这样的子类。

前言

前面已经对方法,类列举了重构方法。现在该轮到字段了
注:值对象:不可变对象
引用对象:可变对象

Self Encapsulate Field 自封装字段 问题

你直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙。

方法

为这个字段建立取值/设值函数,并且只以这些函数来访问字段。(getter和setter方法)

动机

子类可以通过覆写一个函数而改变获取数据的途径;它还支持更灵活的数据管理方式,例如延迟初始化。

Replace Data Value with Object 对象取代数据值 问题

你有一个数据项,需要与其他数据和行为一起使用才有意义。

方法

将数据项变成对象。

动机

开发初期,一个用户的信息只有名字,就用一个字符串来存,如果后期要添加用户的信息。比如性别,年龄。会很难扩展

Change value to Reference 将值对象改为引用对象 问题

希望将多个相等对象,替换为一个对象。

方法

使用单例模式。保证该对象只有一个

动机

当你希望在值对象中加一些可修改的字段时。保证所有对象都能被修改

Change Reference to Value 将引用对象改为值对象 问题

你有一个引用对象,很小且不可变,而且不易管理。将它变成一个值对象。
注: 要在引用对象和值对象之间做选择,有时并不容易。做出选择后,你常会需要一条回头路,

方法

引用对象改为值对象

动机

当引用对象变得难以使用(比如因为同步问题)。就变回值对象

Replace Array with Object 以对象取代数组 问题
你有一个数组,其中的元素各自代表不同的东西。以对象替换数组,对于数组中的每个元素,以一个字段来表示。
方法

将每个元素用对象封装

动机

注:我觉得java程序员应该很少这么用。一般都不会把不同的元素存到数组中。

Duplicate Observed data 复制被监视数据 问题

你有一些领域数据置身于GUI控件中,而领域函数需要访问这些数据。

方法

将该数据复制到一个领域对象中。建立一个观察者模式,用以同步领域对象和GUI对象内的重复数据。

动机

业务逻辑和用户界面不能写在一起,因为这样用户类就会有两种职责。用户类就变得很复杂。

Change Unidirection Association to Bidirectional 将单向关联改为双向关联(特定情况使用) 问题

两个类都需要使用对方特性,但其间只有一条单向连接。

方法

添加一个反向指针,并使修改函数能够同时更新2条连接。

动机

两个类都需要使用对方特性,本重构运用反向指针实现双向关联,其他技术需要其他重构手法。

Change Bidirectional Association to Unidirection将双向关联改为单向关联(特殊情况使用) 问题

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

方法

去除不必要的关联。

动机

双向关联很有用,但你必须为它付出代价,那就是维护双向连接、确保对象被正确创建和删除而增加的复杂度。而且,由于很多程序员并不习惯使用双向关联,它往往成为错误之源。
大量的双向连接也很容易造成“僵尸对象”:某个对象本来应该死亡了,却仍然保留在系统中,因为对它的引用没有完全清除。
此外,双向关联也迫使2个类之间有了依赖:对其中任一个类的任何修改,都可能引发另一个类的变化。如果这2个类位于不同的程序集,这种依赖就是程序集之间的相依。过多的跨程序集依赖会造就紧耦合的系统,使得任何一点小小改动就可能造成许多无法预知的后果。
只有在真正需要双向关联的时候,才该使用它。如果发现双向关联不再有存在价值,就应该去掉不必要的一条关联。

Replace Magic Number with Symbolic Constant字面常量取代魔法数 问题

你有一个字面数值,带有特别含义。

方法

创建一个常量,根据其意义为它命名,并将上述的字面数值替换为这个常量。

动机

在计算科学中,魔法数是历史悠久的不良现象之一。所谓魔法数是指拥有特殊意义,却又不能明确表现出这种意义的数字。如果你需要在不同的地点引用同一个逻辑数,魔法数会让你烦恼不已,因为一旦这些数发生变化,你就必须在程序中找到所有魔法数,并将它们全部修改一遍。就算你不需要修改,要准确指出每个魔法数的用途,也会让你颇费脑筋。

Encapsulate Field 封装字段 问题

你的类中存在一个public字段。

方法

将它声明为private,并且提供相应的访问函数。

面向对象的首要原则之一就是封装,或者称为“数据隐藏”。按此原则,你绝不应该将数据声明为public,否则其他对象就有可能访问甚至修改这项数据,而拥有该数据的对象却毫无察觉。于是,数据和行为就被分开了。

Encapsulate Collection 封装集合 问题

有一个函数返回一个集合。

方法

让这个函数返回该集合的一个只读副本,并在这个类中提供添加/移除集合元素的函数。

动机

我们常常会在一个类中使用集合来保存一组实例。这样的类通常也会提供针对该集合的取值/设值函数。
但是,集合的处理方式应该和其他种类的数据略有不同。取值函数不该返回集合自身,因为这会让用户得以修改集合内容而集合拥有者却一无所悉。也会对用户暴露过多对象内部数据结构信息。如果一个取值函数确实需要返回多个值,它应该避免用户直接操作对象内所保存的集合。并隐藏对象内与用户无关的数据结构。
另外,不应该为这整个集合提供设值函数,但应该提供用以为集合添加/移除元素的函数。这样,集合拥有者就可以控制集合元素的添加和移除。

Replace Record with Data Class 以数据类取代记录

这条我还真没懂

Replace Type Code with Class 以类来取代类型码 问题

类之中有一个数值类型码,但它并不影响类的行为(不会因为他的改变而调用不同的逻辑)

方法

以一个新的类替换该数值类型码。

动机

任何接受类型码作为参数的函数,所期望的实际上是一个数值,无法强制使用符号名。这会大大降低代码的可读性,从而成为bug之源。

Replace Type Code with Subclasses 以子类来取代类型码 问题

你有一个不可变的类型码,它会影响类的行为。

方法

以子类取代这个类型码。

动机

一般来说,这种情况的标志就是像switch这样的条件表达式。这种条件表达式可能有2种表现形式:switch语句或者if –then-else结构。不论哪种形式,它们都是检查类型码值,并根据不同的值执行不同的动作。这种情况下,你应该以 Replace Conditional with Polymorphism (以多态取代条件表达式)进行重构。但为了那个顺利进行那样的重构,首先应该将类型码替换为可拥有多态行为的继承体系。这样一个继承体系应该以类型码宿主类为基类,并针对每一种类型码建立一个子类。

Replace Type Code with State/Strategy 以状态/策略取代类型码 问题

你有一个类型码,它会影响类的行为,但你无法提供继承手法消除它。

方法

以状态对象取代类型码。

本项重构和Replace Type Code with Subclass (以子类取代类型码)类似,但如果“类型码在对象生命期中发生变化”或“其他原因使得宿主类不能被继承”,你也可以使用本重构。本重构使用State模式和Strategy模式。

   State模式和Strategy模式非常相似,因此无论你选择其中哪一个,重构过程都是一样的。“选择哪个模式”并非问题的关键所在,你只需要选择更合适特定情境的模式就行了。如果你打算在完成本重构后再以Replace Conditional with Polymorphism (以多态取代条件表达式)简化一个算法,那么选择Strategy模式较合适;如果你打算搬移状态相关的数据,而且你把新建对象视为一种变迁状态,就应该选择State模式。
Replace Subclass with Fieldls 以字段取代子类 问题

你的各个子类的唯一差别只在“返回常量数据”的函数身上。

方法

修改这些函数,使它们返回超类中的某个(新增)只读,然后销毁子类。

动机

建立子类的目的,是为了增加新特性或变化其行为。有一种变化行为被称为“常量函数”,它们会返回一个硬编码的值。这东西有其用途:你可以让不同的子类中的同一个访问函数返回不同的值。你可以在超类中将访问函数声明为抽象函数,并在不同子类中让它返回不同的值。
尽管常量函数有其用途,但若与子类中只有常量函数,实在没有足够的存在价值。你可以在超类中设计一个与常量函数返回值相应的字段,从而完全除去这样的子类。如此一来就可以避免因继承而带来的额外复杂性。
注:这好像和以子类取代状态码有一定冲突

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69736.html

相关文章

  • 重构-改善既有代码设计(六)--重新组织函数

    摘要:函数重构重构有很大一部分都是在重构函数。这条也要具体情况具体使用函数对象取代函数函数对象代替函数大函数变成类你有一个大型函数,其中对局部变量的使用使你无法采用提炼函数。将这个大型函数放进一个单独对象中,如此一来局部变量就成了对象内的字段。 函数重构 重构有很大一部分都是在重构函数。尤其是长函数。这是问题的根源。以下是重构方法 Extract Method 提炼函数 提炼函数:(由复杂的...

    CoyPan 评论0 收藏0
  • Java学习路线总结,搬砖工逆袭Java架构师(全网最强)

    摘要:哪吒社区技能树打卡打卡贴函数式接口简介领域优质创作者哪吒公众号作者架构师奋斗者扫描主页左侧二维码,加入群聊,一起学习一起进步欢迎点赞收藏留言前情提要无意间听到领导们的谈话,现在公司的现状是码农太多,但能独立带队的人太少,简而言之,不缺干 ? 哪吒社区Java技能树打卡 【打卡贴 day2...

    Scorpion 评论0 收藏0
  • 读书笔记《重构 改善既有代码设计

    摘要:重构在不改变代码的外在的行为的前提下对代码进行修改最大限度的减少错误的几率本质上,就是代码写好之后修改它的设计。重构可以深入理解代码并且帮助找到。同时重构可以减少引入的机率,方便日后扩展。平行继承目的在于消除类之间的重复代码。 重构 (refactoring) 在不改变代码的外在的行为的前提下 对代码进行修改最大限度的减少错误的几率 本质上, 就是代码写好之后 修改它的设计。 1,书中...

    mdluo 评论0 收藏0
  • 重构---改善既有代码设计

    摘要:为何重构重构有四大好处重构改进软件设计如果没有重构,程序的设计会逐渐腐败变质。经常性的重构可以帮助维持自己该有的形态。你有一个大型函数,其中对局部变量的使用使你无法采用。将这个函数放进一个单独对象中,如此一来局部变量就成了对象内的字段。 哪有什么天生如此,只是我们天天坚持。 -Zhiyuan 国庆抽出时间来阅读这本从师傅那里借来的书,听说还是程序员的必读书籍。 关于书的高清下载连...

    baihe 评论0 收藏0

发表评论

0条评论

qpal

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<