文档章节

[读书笔记]重构改善既有代码的设计

马湖村第九后羿
 马湖村第九后羿
发布于 09/17 14:09
字数 8319
阅读 14
收藏 0

章节一 重构,第一个案例

快速而随性(quick and dirty)地设计一个简单的程序并没有错,但是,如果这是复杂系统中具有代表性的一段。。。

tip: 如果你发现自己需要为程序添加一个特性,而代码结构是你无法很方便地那么做,那就先重构那个程序,使特性的添加比较容易进行,然后再添加特性

重构的第一步永远相同:为即将修改的代码建立一组可靠的测试环境。

tip: 重构技术以微小的步伐修改程序。如果你犯下错误,很容易就可发现他。

tip: 任何一个傻瓜都能写出计算机理解的代码。唯有写出人类理解的代码,才是优秀的程序员。

在另一个对象属性的基础上使用switch-case语句,并不是什么好主意,如果不得不使用,也是在对象自己的数据上使用,而不是在别人的数据上使用。

就Java语言体系来说,GOF是java基础知识和j2EE知识之间一座隐形的桥。虽然它是隐性的,却是不可越过(缺少)的。整个设计模式贯穿一个原理:面对接口编程,而不是面对实现。

章节二 重构原则

两顶帽子比喻添加新功能和重构,软件开发过程中,你可能会发现自己经常变换帽子,无论如何你都应该清楚自己戴的是哪一顶帽子。

『我不是个伟大的程序员:我只是个有着一些优秀习惯的好程序员而已。 —— Kent Beck』

几乎任何情况下我都反对专门拨出时间进行重构。在我看来,重构本来就不是一件‘特别拨出时间做’的事情,重构应该随时随地进行。你不应该为重构而重构,你之所以重构,是因为你想做别的什么事,而重构可以帮助你把那些事做好。

何时重构

  • 三次法则
  • 添加功能时一并重构
  • 修补错误时一并重构
  • 复审代码时一并重构

tip: 事不过三,三则重构。

计算机科学是这样一门学科:它相信所有问题都可以通过多一个间接层来解决。

tip: 不要过早发布接口。请修改你的代码拥有权政策,使重构更顺畅。

关于性能,一件很有趣的事情是:如果你对大多数程序进行分析,你会发现它把大半时间都耗费在一小半代码上。如果你一视同仁地优化所有代码90%的优化工作都是白费劲儿,因为被你优化的代码有许多难得被执行起来。

性能热点[hot spot]

章节三 代码的坏味道

Duplicated Code重复代码:同一个类中,两个互为兄弟的子类中,毫不相干的类中。

Long Method 过长函数:你应该更积极进取地分解函数。

我们遵循这样一条原则:每当感觉需要以注释来说明点什么的时候,我们就把需要说明的东西,写进一个独立的函数中,并以其用途(而非实现手法)命名。我们可以对一组或者短短一行代码做这件事。哪怕替换后的函数调用动作比函数自身还长,只要函数名称能够解释其用途,我们也该毫不犹豫地这么做。关键不在于函数的长度,而在于函数『做什么』和『如何做』之间的语义距离。

如何确定该提炼哪一段代码呢?一个很好的技巧是:寻找注释。它们通常是指出『代码用途和实现手法间的语义距离』的信号。如果代码前方有一行注释,就是在提醒你:可以将这段代码替换成一个函数,而且可以在注释的基础上给这个函数命名。就算只有一行代码,如果它需要以注释来说明,那也值得将它提炼到独立函数去。

条件和循环常常也是提炼的信号。你可以使用分解条件表达式处理条件式。至于循环,你应该将循环和其内的代码提炼到一个独立函数中。

Large Class过大类

如果单一类做太多事情,会出现大量的实例变量,可以使用Extract Class将数个变量一起提炼至新类中。提炼时应选择类内彼此相关的变量。

一个类如果拥有太多代码,往往也适合使用Extract Class, Extract SubClass。这里有个有用的技巧:先确定客户端如何使用他们,然后运用Extract Interface为每一种使用方式提炼出一个接口。

Long Parameter List过长参数

抽取出一个参数对象。

Divergent Change发散式变化:一个类受到多个变化影响

一旦需要修改,我们希望能够跳到系统的某一点,只在该处做修改。如果不能够做到这一点,你就嗅出两种紧密相关的刺鼻味道中的一种了。

如果某个类经常因为不同的原因在不同的方向上发生变化,发散式变化就出现了。把这个对象分成几个,这样每个对象就可以只因一种变化而需要修改,针对某一外界变化的所有相应修改,都只应该发生在单一类中,而这个新类中所有内容,都应该反应该外界变化。

Shotgun Surgery散弹式修改:一个变化影响多个类

如果遇到某种变化,必须在许多不同的类内做出小修改以响应,这个坏味道就是散弹式修改,和发散式变化正好相反。此时,应该使用Move method, Move fild 把所有需要修改的代码放进同一个类,

Feature Envy 依恋情节

函数对某个类的兴趣,高过对自己宿主类的兴趣,那就把函数移至另一个地点。有时候函数有一部分有依恋情节,那就先Extract Method 然后移动到另一个地点。

有些设计模式(策略模式和访问者模式)破坏了这个规则。最根本的原则:将总是一起变化的东西放在一块儿。

Data Clumps数据泥团

类中的值域,多个函数中的相同参数,这些总是绑在一起出现的数据应该放进属于他们自己的对象中。

一个好的评断方法是:删掉众多数据中的一笔。其他数据有没有因而失去意义?如果它们不再有意义,这就是个明确的信号:你应该为他们产生一个新对象。

Primitive Obsession基本类型偏执

对象的一个极具价值的东西是:他们模糊(甚至打破)了横亘在基本数据和体积较大的类之间的界限。你可以轻松地编写出一些与语言内置类型无异的小型类。

Switch Statementsswitch 惊悚现身

面相对象程序的一个最明显特征就是:少用switch-case语句。大多数时候,一看到switch语句,就应该考虑以多态来替换他。问题是多态该出现在哪儿?switch语句常常根据类型码进行选择,你要的是“与该类型码相关的函数或类”,所以应该使用ExtractMethod将switch语句提炼到一个独立函数中,再以MoveMethod将他搬移到需要多态性的那个类里。

如果只是单一函数有一些选择事例,且不想改动它们,那么多态就有点杀鸡用牛刀了。

Parallel Inheritance Hierarchies平行继承体系

这是散弹式修改的一个特殊情况,此时每当你为某个类增加一个子类,必须也为另一个类相应增加一个子类。

应对策略是:让一个继承体系的实例引用另一个继承体系的实例。

Lazy Class冗赘类:无用的类

Speculative Generality夸夸其谈未来性:为未来功能预设的伏笔的类,会造成系统难以理解和维护

Temporary Field令人迷惑的暂时字段

某个实例变量仅为某个特定情形而设。这样的代码让人不易理解,因为通常认为对象在所有时候需要它的所有变量 。使用Extract Class 把这个变量和相关函数提炼到一个独立的类中。

Message Chains过渡耦合的消息链

此时应该使用Hide Delegate 。可以在消息链的不同位置进行这种重构。先观察消息链最终得到的对象是用来干什么的,看看能否以Extract Method把使用该对象的代码提炼到一个独立函数中,在运用Move Method把这个函数推入消息链。

Middle Man 中间转手人

如果你看到某个类有一半的函数都委托给其他类,这样就是过渡运用委托了。这时应该使用Remove Middle Man,直接和真正负责的对象打交道。如果这样的(使用委托的)函数比较少,使用InlineMethod把他们放入调用端。如果这个MiddleMan还有其他行为,可以使用Replace Delegation with Inheritance,把它们变成实责对象的子类,这样既可以扩展,又不必负担那么多的委托。

Inappropriate Intimacy 狎昵关系

有时候两个类过于亲密,话费太多时间去探究彼此的Private成分。继承往往造成过度亲密,子类对超类的了解总是超过后者的主观愿望。使用Replace Inheritance with Delegation

Alertnative Classes with Different Interfaces 异曲同工的类

如果函数签名不同,做着同一件事,请运用Rename Method根据用途重新命名。这往往不够,请反复使用Move Method甚至Extract SuperClass

Incomplete Library Class 不完美的类库

如果只想修改库类的一两个函数,可以运用Introduce Foreign Method,如果想添加一大堆额外的行为,就得运用Introduce Local Extension

Data Class幼稚的数据类

DataClass是指拥有一些字段,以及用于访问(读写)这些字段的函数,除此之外一无长物。只是不会说话的数据容器。

对于public 字段,使用Encapsulate Field将它们封装起来。

对于容器类字段,使用Encapsulate Collection 把他们封装起来。

对于不该被别的类修改的字段,使用Remove Setting Method

找出这些取值/设值函数被其他类运用的地点,尝试以Move Method把那些调用行为搬移到Data Class

Data Class 必须承担一定的责任。

Refused Bequest 被拒绝的馈赠

子类应该继承超类的函数和数据,如果不想或不需要,这就意味着继承体系错误。需要为这个子类新建一个兄弟类,在把用不到的字段,方法都放入这个兄弟类中。

Comments 过多的注释

如果需要注释解释一块代码做了什么,试试Extract Method;

如果函数已经提炼出来了,但还是需要注释来解释其行为,试试Rename Method;

如果需要注释说明某些系统的需求规格,试试Introduce Assertion

tips: 当你感觉需要撰写注释时,请先尝试重构,试着让所有注释都变得多余。

章节四 构筑测试体系

如果你想要重构,首要前提就是拥有一个可靠的测试环境。

自我测试类:每个类都应该有一个测试函数,并以他来测试这个类。

tip: 确保所有测试都完全自动化,让它们检查自己的测试结果。

tip: 一整组测试就是一个强大的bug侦测器,能够大大缩减查找bug所需要的时间。

tip: 每当接收到bug report ,请先撰写一个单元测试来揭发这只臭虫。

观察类该做的所有事情,然后针对任何一项功能的任何一种可能失败情况,进行测试。

测试应该是一种风险驱动行为,而不是编写大量测试,比如测试所以public函数。

tip: 编写未臻完善的测试并实际运行,好过对完美测试的无尽等待。

tip: 考虑可能出错的边界条件,把测试火力集中在那。

tip: 当事情被大家认为应该会出错时,别忘了检查彼时是否有异常如预期般被抛出。

tip: 不要因为测试无法捕捉臭虫,就不撰写测试代码,因为测试的确可以捕捉到大多数臭虫。

章节五 重构列表

真要表示货币金额,我会使用Quantity模式。

重构的基本技巧:小步前进,频繁测试。

章节六 重新组织函数

Extract Method:提炼函数,你有一段代码可以被组织在一起并独立出来,将这段代码放入独立函数中,并让函数名称解释函数用途。

Inline Method:将函数内联化,一个函数的本体与名称同样清楚易懂。在函数调用点插入函数本地,然后移除该函数。

Inline Temp :将临时变量内联化,你有一个临时变量,只被一个简单表达式赋值一次,而它妨碍了其他重构手法。将所有对该变量的引用动作,替换为对它赋值的那个表达式自身。

Replace Temp with Query :以查询取代临时变量。你的程序以一个临时变量保存某一表达式的运算结果。将这个表达式提炼到独立函数中。将这个临时变量的所有引用点替换为对新函数的调用。此后,新函数就可被其他函数调用。

Introduce Explaining Variable:引入解释性变量,你有一个负责的表达式,将复杂表达式的结果放进一个临时变量,以此变量名称来解释表达式用途。在条件逻辑中,特别有价值。

Split Temporary Variable : 分解临时变量,你的程序有某个临时变量被赋值超过一次,它既不是循环变量,也不被用于收集计算结果。针对每次赋值,创造一个独立,对应的临时变量。

“循环变量”和“结果收集变量”可以多次赋值,除了这两种情况,如果临时变量被赋值超过一次,就意味着在函数中承担了一个以上的责任。如果临时临时变量承担多个责任,就应该被替换成多个临时变量。

Remove Assignments to Parameters : 移除对参数的赋值。代码对一个参数进行赋值,应该以一个临时变量取代该参数的位置。

Replace Method with Method Object :以函数对象取代函数。你有一个大型函数,其中对局部变量的使用使你无法采用Extract Method 。将这个函数放入一个单独对象中,如此一来局部变量就变成了对象内的字段。然后你可以在同一个对象中,将这个大型函数分解为多个小函数。

Substitute Algorithm :替换算法。你想要把某个算法替换为另一个更清晰的算法。将函数本体替换为另一个算法。

章节七 在对象之间搬移特性

Move Method :搬移函数。你的程序中,有个函数与其所驻类之外的另一个类进行更多交流:调用后者或被后者调用。在该函数最常引用的类中建立一个有着类似行为的新函数。将旧函数变成一个单纯的委托函数,或是将旧函数完全移除。

Move Field : 搬移字段。你的程序中,有个字段被其所驻类之外的另一个类更多地用到。在目标类新建一个字段,修改原字段的所有用户,令他们改用新字段。

Extract Class :提炼类。某个类做了应该由两个类做的事。建立一个新类,将相关的字段和函数从旧类搬移到新类。

一个类应该是一个清楚的抽象,处理一些明确的责任。

Inline Class :将类内联化。某个类没有做太多事情,将这个类的所有特性搬移到另一个类中,然后移除原类。

Hide Delegate : 隐藏委托关系。客户通过一个委托类来调用另一个对象。在服务类上建立客户所需的所有函数,用以隐藏委托关系。

Remove Middle Men : 移除中间人。某个类做了过多的简单委托工作。让客户直接调用受委托类。

Introduce Foregin Method :引入外加函数。你需要为提供服务的类增加一个函数,但你无法修改这个类。在客户类中建立一个函数,并以第一参数的形式传入一个服务类实例。

Introduce Local Extension :引入本地扩展。你需要为服务类提供一下额外函数,但你无法修改这个类。建立一个新类,使它包含这些额外函数。让这个扩展品成为源类的子类或包装类。

章节八 重新组织数据

Self Encapsulate Filed :自封装字段。你直接访问一个字段,但与字段之间的耦合关系逐渐变得笨拙。为这个字段设立getter/setter函数,并且只以这些函数来访问字段。

我比较喜欢直接访问方式,直到这种方式给我带来麻烦为止。比如想访问超类中的字段,却又想在子类中对这个变量的访问改为一个计算后的值,此时是最该使用Self Encapsulate Field的时候。

Replace Data Value with Object :以对象取代数据值。你有一个数据项,需要和其他数据和行为一起使用才有意义。将数据项变为对象。

注意这样一条规则:值对象应该是不可修改内容的。

Change Value to Reference :将值对象改为引用对象。你从一个类衍生出许多彼此相等的实例,希望将他们替换为同一个对象。将这个值对象变为引用对象。

Change Reference to Value :将引用对象改为值对象。你有一个引用对象,很小且不可变,而且不易管理。将它变成一个值对象。

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

Dulplicate Observed Data :复制“被监视数据”。你有一些领域数据置身于GUI控件中,而领域函数需要访问这些数据。将该数据复制到一个领域对象中。建立一个Observer模式,用以同步领域对象和GUI对象内的重复数据。

Change Unidirectional Association to Bidirectional :将单向关联改为双向关联。两个类都需要使用对方的特性,但其间只有一条单向连接。添加一个反向指针,并使修改函数能够同时更新两条连接。

Change Bidirectional Association to Unidirectional :将双向关联改为单向关联。两个类之间有双向关联,但其中一个类如今不再需要另一个类的特性。去除不必要的关联。

Replace Magic Number with Symbolic Constant :以字面常量取代魔法数。你有一个字面数值,含有特别含义。创造一个变量,根据其意义为它命名,并将上述的字面数值替换为这个常量。

Encapsulate Field :封装字段。你的类中存在一个public的字段。将它声明为private,并声明访问函数。

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

public Set getCourse() {
    return Collections.unmodifiableSet(mCourses);
}

Replace Record with Data Class :以数据类取代记录。你需要面对传统编程环境中的记录结构。为该记录创建一个“哑”数据对象。

Replace Typecode with Class : 以类取代类型码。类之中有一个数值类型码,但它并不影响类的行为。以一个新的类替换该数值类型码。

Replace Typecode with Subclass : 以子类取代类型码。你有一个不可变的类型码,它会影响类的行为。以子类取代这个类型码。

如果类型码不会影响宿主类的行为,使用Replace Typecode with Class。如果影响宿主类的行为,最好的办法是借助多态来处理。一般来说这种情况的标志是switch-caseif-then-else。应该使用Replace Conditional with Porlymorphism进行重构。在这之前,首先应该将类型码替换为可拥有多态行为的继承体系,以类型码的宿主类为基类,并针对每一种类型码各建一个子类。

有两种特例:(1)类型码值在对象创建之后发生了改变;(2)类型码宿主类有了子类。这时候应该使用Replace Typecode with State/Strategy

Replace Typecode with State/Strategy :以State/Strategy取代类型码。你有一个类型码,它会影响类的行为,但是你无法通过继承手法消除它。以状态对象取代类型码。

Replace Subclass with Fields :以字段取代子类。你的各个子类的唯一差别,只在“返回常量数据”的函数身上。修改这些函数,使他们返回超类中的每个新增字段,然后销毁子类。

章节九 简化条件表达式

Decompose Conditional :分解条件表达式。你有一个复杂的条件语句,从if then else三个段落中分别提炼出独立函数。

程序之中,复杂的条件逻辑是最常导致逻辑复杂度上升的地点之一。

Consolidate Conditional Expression :合并条件表达式。你有一系列条件测试,都得到相同结果。将这些测试合并成一个条件表达式,并将这个表达式提炼成一个独立函数。

Consolidate Duplicate Conditional Fragments :合并重复的条件片段。在条件表达式的每个分支上有着相同的一段代码。将这段重复代码搬移到条件表达式之外。

Remove Control Flag :移除控制标记。在一系列布尔表达式中,某个变量带有“控制标记(control flag)”的作用。以break语句或return语句取代控制标记。

set done to false
    while not done
    if (condition) {
    	do something
    	set done to true
    }
    next step of loop

Replace Nested Conditional with Guard Clauses :以卫语句取代嵌套条件表达式。函数中的条件逻辑使人难以看清正常的执行路径。使用卫语句表现所有特殊情况。

卫语句就是把复杂的条件表达式拆分成多个条件表达式,比如一个很复杂的表达式,嵌套了好几层的if - then - else 语句,转换为多个if语句,实现它的逻辑,这多条的if 语句就是卫语句。

Replace Conditional with Polymorphism :以多态取代条件表达式。你手上有多个条件表达式,它根据对象类型的不同而选择不同的行为。将这个条件表达式的每个分支放进一个子类内的覆写函数中,然后将原始函数声明为抽象函数。

Introduce Null Object :引入Null对象。你需要再三检查对象是否为null。将null值替换为null对象。

Introduce Assertion :引入断言。某一段代码需要对程序状态做出某种假设。以断言明确表现这种假设。

常常有这样一段代码:只有当某个条件为真时,该段代码才能正常运行。断言是一个条件表达式,应该总是为真。如果它失败,表示程序员犯了错误。

实际上,程序的成品往往将断言统统删除。

你可以新建一个Assert类,用于处理各种情况下的断言。

章节十 简化函数调用

Rename Method :函数改名。函数的名称未能揭示函数的用途。修改函数名称。

函数的名称应该准确表达它的用途。给函数命名有一个好办法:首先考虑给这个函数写上一句怎样的注释,然后想办法将注释变成函数名称。

如果你看到一个函数名称不能很好地表达它的用途,应该马上加以修改。

要想成为真正的编程高手,起名水平至关重要。(嚯 ~!)

Add Parameter :添加参数。某个函数需要从调用端得到更多信息。为此函数添加一个对象参数,让该对象带进函数所需信息。

Remove Parameter : 移除参数。函数本体不再需要某个参数。将该参数去除。

Separate Query from Modifier :将查询函数和修改函数分离。某个函数即返回对象状态值,又修改对象状态。建立两个不同的函数,其中一个负责查询,另一个负责修改。

任何有返回值的函数,都不应该有看得到的副作用。

Parameterize Method :令函数携带参数。若干函数做了类似工作,但在函数本题中却包含了不同的值。建立单一函数,以参数表达那些不同的值。

Replace Parameter with Explicit Method :以明确函数取代参数。你有一个函数,其中完全取决于参数值而采取不同行为。针对该参数值的每一个可能值,建立一个独立函数。

Preserve Whole Object :保持对象完整。你从某个对象中取出若干值,将它们作为某一次函数调用时的参数。改为传递整个对象。

Replace Parameter with Methods :以函数取代参数。对象调用某个函数,并将所得结果作为参数,传递给另一个函数。而接受该参数的函数本身也能够调用前一个函数。让参数接受者去除该项参数,并直接调用前一个函数。

你应该只在必要关头才添加参数,预先添加这个参数很可能并不是你所需要的。

Introduce Parameter Object : 引入参数对象。某些参数总是很自然地同时出现。以一个对象取代这些参数。

你常会看到特定的一组参数总是一起被传递。这就是数据泥团(Data Clumps)。可以运用一个对象包装这些数据,再以对象取代他们。

本项重构的价值在于缩短参数列。

尽量以“范围对象”取代用一对值表示一个范围的代码。

Remove Setting Method:移除设值函数。类中的某个字段应该在对象创建时被设值,然后就不再改变。去掉该字段的所有设值函数,同时将该字段设为final.

Hide Method :有一个函数,从来没有被其他任何类用到。将这个函数改为private。

重构往往促使你修改函数的可见度。

Replace Constructor with Factory Method :以工厂函数取代构造函数。你希望在创建对象时不仅仅是做简单的构建动作。将构造函数替换为工厂函数。

Encapsulate Downcast :抽取向下转型。某个函数返回的对象,需要由函数调用者执行向下转型。将向下转型动作移到函数中。

Replace Error Code with Exception :以异常取代错误码。某个函数返回一个特定的代码,用以表示某种错误情况。改用异常。

代码的可理解性是我们虔诚追求的目标。(内心OS:不不不不~~~能跑就行)

Replace Exception with Test :以测试取代异常。面对一个调用者可以预先检查的条件,你抛出了一个异常。修改调用者,使它在调用函数之前先做检查。

“异常”只应该被用于异常的,罕见的行为,也就是那些产生意料之外的错误的行为,而不应该称为条件检查的替代品。

章节十一 处理概括关系

概括关系 - generalization - 即继承关系,主要是将函数上下移动于继承体系之中。

Pull Up Field : 字段上移。两个子类拥有相同的字段。将该字段移至超类。

本项重构从两方面减少重复:去除了重复的数据声明,去除重复的行为。

Pull Up Method :函数上移。有些函数,在各个子类中产生完全相同的结果。将该函数移至超类。

Pull Up Constructor Body : 构造函数本地上移。你在各个子类中拥有一些构造函数,它们的本体几乎完全一致。在超类中新建一个构造函数,并在子类构造函数中调用它。

Push Down Method : 函数下移。超类中的某个函数只与部分而非全部子类有关。将这个函数移到相关的子类去。

Push Down Field : 字段下移。超类中的某个字段只被部分而非全部子类用到。将这个字段移到需要它的那些子类去。

Extract Subclass : 提炼子类。类中的某些特性只被某些而非全部实例用到。新建一个子类,将上面所说的那一部分特性移到子类中。

Extract Superclass : 提炼超类。两个类有相似特性。为这两个类建立一个超类,将相同特性移至超类。

Extract Interface :提炼接口。若干客户使用类接口中的同一子集,或者两个类的接口有部分相同。将相同的子类提炼到一个独立接口中。

Collapse Hierarchy :折叠继承体系。超类和子类之间无太大区别。将它们合为一体。

所谓重构继承体系,往往是将函数和字段在体系中上下移动。

Form Template Method : 塑造模板函数。你有一些子类,其中相应的某些函数以相同顺序执行类似的操作,但各个操作细节上有所不同。将这些操作分别放入独立函数中,并保持它们都有相同的签名,于是原函数也就变得相同了。然后将原函数上移至超类。

Replace Inheritance with Delegation : 以委托取代继承。某个子类只使用超类接口中的一部分,或是根本不需要继承而来的数据。在子类中新建一个字段保存超类;调整子类函数,令它改而委托超类;然后去掉两者之间的继承关系。

Replace Delegation with Inheritance : 以继承取代委托。你在两个类之间使用委托关系,并经常为整个接口编写许多极简单的委托函数。让委托类继承受托类。

章节十二 大型重构

应该根据需要安排工作,只在需要添加新功能或修补错误时才进行重构。不必一开始就完成整个系统的重构,重构程度只要能满足其他任务的需要就行了。

Tease Apart Inheritance: 梳理并分解继承体系。某个继承体系同时承担两项责任。建立两个继承体系,并通过委托关系让其中一个可以调用另一个。

Convert Procedural Design to Object :将过程化设计转化为对象设计。你手上有一些传统过程化风格的代码。将数据记录变为对象,将大块的行为变为小块,并将行为移入相关对象之中。

Seprate Domain from Presentation :将领域和表述/显示分离。某些GUI类之中包含了领域逻辑。将领域逻辑分离出来,为它们建立独立的领域类。

MVC模式最核心的价值在于:它将用户界面代码(视图、展示层)和领域逻辑(模型)分离。

Extract Hierarchy : 提炼继承体系。你有某个类做了太多工作(瑞士军刀般的类),其中一部分工作是以大量条件表达式完成的。建立继承体系,以一个子类表示一种特殊情况。

© 著作权归作者所有

马湖村第九后羿
粉丝 0
博文 14
码字总数 15877
作品 0
东城
私信 提问
[读书]读《重构-改善既有代码的设计》

读《重构-改善既有代码的设计》 断断续续,加上过年,花了快2个月吧,把《重构-改善既有代码的设计》读完了,这里总结下。 发现此书背景 读的感觉 知识感触 发现此书背景 这本书是从同事的桌...

zemel
2016/03/07
40
0
读书笔记《重构 改善既有代码的设计》

重构 (refactoring) 在不改变代码的外在的行为的前提下 对代码进行修改最大限度的减少错误的几率 本质上, 就是代码写好之后 修改它的设计。 1,书中开始用一个例子简单阐释为什么要重构,以...

MichaelDuan
09/29
0
0
31 天重构学习笔记10. 提取方法

摘要:由于最近在做重构的项目,所以对重构又重新进行了一遍学习和整理,对31天重构最早接触是在2009年10月份,由于当时没有订阅Sean Chambers的blog,所以是在国外的社区上闲逛的时候链接过...

技术小甜
2017/11/08
0
0
31 天重构学习笔记2. 移动方法

摘要:由于最近在做重构的项目,所以对重构又重新进行了一遍学习和整理,对31天重构最早接触是在2009年10月份,由于当时没有订阅Sean Chambers的blog,所以是在国外的社区上闲逛的时候链接过...

技术小甜
2017/11/15
0
0
31 天重构学习笔记9. 提取接口

摘要:由于最近在做重构的项目,所以对重构又重新进行了一遍学习和整理,对31天重构最早接触是在2009年10月份,由于当时没有订阅Sean Chambers的blog,所以是在国外的社区上闲逛的时候链接过...

技术小甜
2017/11/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

nginx学习笔记

中间件位于客户机/ 服务器的操作系统之上,管理计算机资源和网络通讯。 是连接两个独立应用程序或独立系统的软件。 web请求通过中间件可以直接调用操作系统,也可以经过中间件把请求分发到多...

码农实战
今天
5
0
Spring Security 实战干货:玩转自定义登录

1. 前言 前面的关于 Spring Security 相关的文章只是一个预热。为了接下来更好的实战,如果你错过了请从 Spring Security 实战系列 开始。安全访问的第一步就是认证(Authentication),认证...

码农小胖哥
今天
11
0
JAVA 实现雪花算法生成唯一订单号工具类

import lombok.SneakyThrows;import lombok.extern.slf4j.Slf4j;import java.util.Calendar;/** * Default distributed primary key generator. * * <p> * Use snowflake......

huangkejie
昨天
12
0
PhotoShop 色调:RGB/CMYK 颜色模式

一·、 RGB : 三原色:红绿蓝 1.通道:通道中的红绿蓝通道分别对应的是红绿蓝三种原色(RGB)的显示范围 1.差值模式能模拟三种原色叠加之后的效果 2.添加-颜色曲线:调整图像RGB颜色----R色增强...

东方墨天
昨天
11
1
将博客搬至CSDN

将博客搬至CSDN

算法与编程之美
昨天
13
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部