《重构-代码整洁之道TypeScript版》第3天

时间:2022-07-22
本文章向大家介绍《重构-代码整洁之道TypeScript版》第3天,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

今天让我们来继续第3天,老规矩先来回顾一下昨天我们都实现了哪些:

  • Change Reference to Value(将引用对象改为值对象)
  • Change Value to Reference(将值对象改为引用对象)
  • Collapse Hierarchy(折叠继承体系)

(图片:博格达峰)

什么是重构?

简单理解就是不改变软件可观察行为的前提下,改善其内部结构,以提高理解性和降低修改成本。


1. 这是如下我们要实现的目标任务列表(每天进步一点点⏰)

  • [x] Consolidate Conditional Expression(合并条件表达式)
  • [x] Consolidate Duplicate Conditional Fragments(合并重复的条件片段)
  • [x] Convert Procedural Design to Objects(将过程化设计转化为对象设计)
  • [ ] Decompose Conditional(分解条件表达式)
  • [ ] Duplicate Observed Data(复制“被监视数据”)
  • [ ] Encapsulate Collection(封装集合)
  • [ ] Encapsulate Downcast(封装向下转型)
  • [ ] Encapsulate Field(封装字段)
  • [ ] Extract Class(提炼类)
  • [ ] Extract Hierarchy(提炼继承体系)
  • [ ] Extract Interface(提炼接口)
  • [ ] Extract Method(提炼函数)
  • [ ] Extract Subclass(提炼子类)
  • [ ] Extract Superclass(提炼超类)
  • [ ] Form Template Method(塑造模板函数)
  • [ ] Hide Delegate(隐藏“委托关系”)
  • [ ] Hide Method(隐藏函数)
  • [ ] Inline Class(将类内联化)
  • [ ] Inline Method(内联函数)
  • [ ] Inline Temp(内联临时变量)
  • [ ] Introduce Assertion(引入断言)
  • [ ] Introduce Explaining Variable(引入解释性变量)
  • [ ] Introduce Foreign Method(引入外加函数)
  • [ ] Introduce Local Extension(引入本地扩展)
  • [ ] Introduce Null Object(引入Null对象)
  • [ ] Introduce Parameter Object(引入参数对象)
  • [ ] Move Field(搬移字段)
  • [ ] Move Method(搬移函数)
  • [ ] Parameterize Method(令函数携带参数)
  • [ ] Preserve Whole Object(保持对象完整)
  • [ ] Pull Up Constructor Body(构造函数本体上移)
  • [ ] Pull Up Field(字段上移)
  • [ ] Pull Up Method(函数上移)
  • [ ] Push Down Field(字段下移)
  • [ ] Push Down Method(函数下移)
  • [ ] Remove Assignments to Parameters(移除对参数的赋值)
  • [ ] Remove Control Flag(移除控制标记)
  • [ ] Remove Middle Man(移除中间人)
  • [ ] Remove Parameter(移除参数)
  • [ ] Remove Setting Method(移除设值函数)
  • [ ] Rename Method(函数改名)
  • [ ] Replace Array with Object(以对象取代数组)
  • [ ] Replace Conditional with Polymorphism(以多态取代条件表达式)
  • [ ] Replace Constructor with Factory Method(以工厂函数取代构造函数)
  • [ ] Replace Data Value with Object(以对象取代数据值)
  • [ ] Replace Delegation with Inheritance(以继承取代委托)
  • [ ] Replace Error Code with Exception(以异常取代错误码)
  • [ ] Replace Exception with Test(以测试取代异常)
  • [ ] Replace Inheritance with Delegation(以委托取代继承)
  • [ ] Replace Magic Number with Symbolic Constant(以字面常量取代魔法数)
  • [ ] Replace Method with Method Object(以函数对象取代函数)
  • [ ] Replace Nested Conditional with Guard Clauses(以卫语句取代嵌套条件表达式)
  • [ ] Replace Parameter with Explicit Methods(以明确函数取代参数)
  • [ ] Replace Parameter with Methods(以函数取代参数)
  • [ ] Replace Record with Data Class(以数据类取代记录)
  • [ ] Replace Subclass with Fields(以字段取代子类)
  • [ ] Replace Temp with Query(以查询取代临时变量)
  • [ ] Replace Type Code with Class(以类取代类型码)
  • [ ] Replace Type Code with State/Strategy(以State/Strategy取代类型码)
  • [ ] Replace Type Code with Subclasses(以子类取代类型码)
  • [ ] Self Encapsulate Field(自封装字段)
  • [ ] Separate Domain from Presentation(将领域和表述/显示分离)
  • [ ] Separate Query from Modifier(将查询函数和修改函数分离)
  • [ ] Split Temporary Variable(分解临时变量)
  • [ ] Substitute Algorithm(替换算法)
  • [ ] Tease Apart Inheritance(梳理并分解继承体系)

2. Consolidate Conditional Expression(合并条件表达式)

描述?:你有一些列的条件测试都得到了相同的结果,你可以将这些测试合并为一个条件表达式,并将这个条件表达式提炼为一个独立的函数。

动机?:检查条件各不相同,行为一直就应该使用“逻辑或”和“逻辑与”将他们合并为一个条件表达式。

interface ConfigFn {
  (): number;
}
const disabilityAmount: ConfigFn = (): number => {
  if (_seniority < 2) return 0;
  if (_monthsDisabled > 12) return 0;
  if (_isPartTime) return 0;
};

在这段代码中,我们看到一连串的条件检查,它们都做同一件事。上述条件检查等价于一个以逻辑或连接起来的语句:

const disabilityAmount: ConfigFn = (): number => {
    (_seniority<2)||(_monthsDisabled>12)||(_isPartTime))return 0;
}
//继续改造
const isNotEligible = ():boolean=>{
    return  (_seniority<2)||(_monthsDisabled>12)||(_isPartTime));
} 
const onVacation = ():boolean=>{} 
const disabilityAmount = (): number => {
  // 这里也可以使用逻辑与继续更复杂的应用
  // if(isNotEligible){
  if(isNotEligible && onVacation){
        return 0;
   }
}

3. Consolidate Duplicate Conditional Fragments(合并重复的条件片段)

描述?:在条件表达式的每个分支上有着相同的一段代码,将这段代码搬到条件表达式之外。

动机?:移除相同的重复代码才更更清楚的表明哪些东西随条件的变化而变化,哪些东西保持不变。

// 简单版本 比如koa2的输出
if(flag){
    const data = "老袁";
    res.end(data)
}else{
    const data = {
        "message":"老袁"
    }
    res.end(data)
}

//改造后
let data = "默认信息";
if(flag){
    data = "老袁";
}else{
    data = {
        "message":"老袁"
    }
}
res.end(data)

我们在来看看复杂的版本:

//编写.jscpd.json
{
  "mode": "strict",
  "threshold": 0,
  "reporters": ["html", "console", "badge"]
}
yarn add jscpd -D
yarn add jscpd-badge-reporter -D
jscpd ./src

补一张效果图:

当然这只是一个小工具,大家想专业的刷起来Sonar。

4. Convert Procedural Design to Objects(将过程化设计转化为对象设计)

描述?:你手上有一些传统过程化的代码,没有任何封装。现我们可将数据记录变成对象,将大块的行为分成小块,并将行为移入到相关对象之中。

动机?:有一次我看到过带的一个实行生一个纯的函数120多行,这个时候我们就可以用一些小型对象改变宿主对象的行为。长长的函数一般都是你下手这条规定的最佳时机。

这里我就不啰嗦了,希望大家有时间可以静下来在好好看看除了封装、继承、多态外基于SOLID的设计,其实足够了。

函数式编程和SOLID其实是相辅相成的。