前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >《重构-代码整洁之道TypeScript版》第2天

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

作者头像
疯狂的技术宅
发布2020-08-04 16:43:37
6460
发布2020-08-04 16:43:37
举报
文章被收录于专栏:京程一灯京程一灯

昨天我们发了第一篇,今天让我们来继续第二天。先来回顾一下昨天我们都实现了哪些:

  • Add Parameter(添加参数)
  • Change Bidirectional Association to Unidirectional(将双向关联改为单向关联)
  • Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)

(图片:贡嘎山)

什么是重构 ?

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


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

  • [x] Change Reference to Value(将引用对象改为值对象)
  • [x] Change Value to Reference(将值对象改为引用对象)
  • [x] Collapse Hierarchy(折叠继承体系)
  • [ ] Consolidate Conditional Expression(合并条件表达式)
  • [ ] Consolidate Duplicate Conditional Fragments(合并重复的条件片段)
  • [ ] 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. Change Reference to Value(将引用对象改为值对象)

描述?:你有一个引用对象,很小且不可改变,而且不易管理。

动机?:在分布系统和并发系统中,不可变的值对象特别有用,因为你无需考虑他们的同步问题。

代码语言:javascript
复制
export interface ICurrency {
  getCode(): string;
}
class Currency implements ICurrency {
  private _code: string;
  constructor(code: string) {
    this._code = code;
  }
  public getCode(): string {
    return this._code;
  }
}
//假设现在你上个月的工资发的是人民币 类里包含了其他汇率等
const rmb = new Currency('RMB');
//这个月也发的是人民币
const rmb2 = new Currency('RMB');
//都是人民币这个结果显然是false
console.log(rmb == rmb2);

要把一个引用对象变成值对象,关键动作:检查它是否不可变。如果不是,我们就用不到使用本项重构,因为可变的值会造成凡人的别名问题。

代码语言:javascript
复制
//如上RMB显然是不可变的币种 所以我们可以把它改成值类型
//有2种方式可以改变他为值类型 一种是设计模式里的单例
// 我们这里按照原书的逻辑添加了一个equal的函数
export interface ICurrency {
  equals(arg: Object): boolean;
  getCode(): string;
}
class Currency implements ICurrency {
  ...
  public equals(arg: Object): boolean {
    if (!(arg instanceof Currency)) {
      return false;
    }
    return this._code === arg._code;
  }
  ...
}
const rmb = new Currency('RMB');
const rmb2 = new Currency('RMB');
console.log(rmb.equals(rmb2));

因为两个对象实际上是对值的比较了 无论你声明多少个,我们实际都操作rmb这个实例了。

接下来,我就要多说几句了,实际上这个规则对我们前端用途非常的大。Redux.jsImmutable.js

代码语言:javascript
复制
yarn add immutable -D
yarn add redux -D
yarn add @types/immutable -D
代码语言:javascript
复制
// map1是不可变对象 正用了这条重构规则
import { Map } from 'immutable';
const map1 = Map({ a: 1, b: 2, c: 3 });
const map2 = map1.set('b', 50);
console.log(map1.get('b') + ' vs. ' + map2.get('b'));
代码语言:javascript
复制
// 完整版的code地址
// https://redux.js.org/recipes/usage-with-typescript
import { Action } from 'redux'
import { sendMessage } from './store/chat/actions'
import { AppState } from './store'
import { ThunkAction } from 'redux-thunk'

export const thunkSendMessage = (
  message: string
): ThunkAction<void, AppState, null, Action<string>> => async dispatch => {
  const asyncResp = await exampleAPI()
  dispatch(
    sendMessage({
      message,
      user: asyncResp,
      timestamp: new Date().getTime()
    })
  )
}

function exampleAPI() {
  return Promise.resolve('Async Chat Bot')
}

Redux的状态肯定是不可变的,否则整个对象的内存地址不变就无法响应我们的React组件了。

3. Change Value to Reference(将值对象改为引用对象)

描述?:从一个类衍生出许多彼此相等的实例,希望将他们替换为同一对象。

动机?:许多系统中,像日期、数量、名称等他们完全由所含的意义来标识,你并不在乎他们有多少副本。如果在你的副本中需要某一对象的变化影响到引用了他的地方,就需要考虑将这个对象变成一个引用对象。

代码语言:javascript
复制
class Order {
  public setCustomer(customerName: string): void {}
}
class Customer {
}
const john = new Customer();
const basket = new Order();
// 原来的处理方式 Order内部去newCustomer
basket.setCustomer("john");
//变为将值对象改为引用对象
basket.setCustomer(john);

其实我们的工厂模式专门就是做这件事的

代码语言:javascript
复制
//我用Java改过来的一段TS实现的简单工厂
abstract class Noodles {
    /**
     * 描述每种面条啥样的
     */
    public abstract desc(): void;
}

class LzNoodles extends Noodles {
    public desc(): void {
        console.log("兰州拉面 上海的好贵 家里才5 6块钱一碗");
    }
}
class PaoNoodles extends Noodles {
    public desc(): void {
        console.log("泡面好吃 可不要贪杯");
    }
}
class GankouNoodles extends Noodles {
    public desc(): void {
        console.log("还是家里的干扣面好吃 6块一碗");
    }
}

class SimpleNoodlesFactory {
    public static TYPE_LZ: number = 1;//兰州拉面
    public static TYPE_PM: number = 2;//泡面
    public static TYPE_GK: number = 3;//干扣面

    public static createNoodles(type: number): Noodles {
        switch (type) {
            case SimpleNoodlesFactory.TYPE_LZ:
                return new LzNoodles();
            case SimpleNoodlesFactory.TYPE_PM:
                return new PaoNoodles();
            case SimpleNoodlesFactory.TYPE_GK:
            default:
                return new GankouNoodles();
        }
    }
}
const noodles:Noodles = SimpleNoodlesFactory.createNoodles(SimpleNoodlesFactory.TYPE_GK);
noodles.desc();

如上代码非常清晰,我们将数字类型(值对象)通过工厂生产出了应用对象(Noodles实现类)。

4. Collapse Hierarchy(折叠继承体系)

描述?:当超类和子类之间无太大区别的时候,应将他们合为一体。

动机?:如果你曾经编写过继承体系,就会知道,继承体系很容易变得过分复杂。新重构继承体系,往往是将函数和字段在体系中上下移动。完成这些动作后,你很可能发现某个子类并未带来该有的价值,因此需要把超类与子类合并起来。

代码语言:javascript
复制
// 我们把eat回归了父类 把sleep强制留给了子类
abstract class Animal {
  eat() {
      console.log('eat')
  }
  abstract sleep(): void
}
// let animal = new Animal()

class Dog extends Animal {
  constructor(name: string) {
      super()
      this.name = name
  }
  // 随意设置了些自己的属性和方法
  public name: string = 'dog'
  protected pro() {}
  readonly legs: number = 4
  static food: string = 'bones'
  sleep() {
      console.log('Dog sleep')
  }
}
// console.log(Dog.prototype)
let dog = new Dog('wangwang')
console.log(Dog.food)
dog.eat()

class Cat extends Animal {
  sleep() {
      console.log('Cat sleep')
  }
}
let cat = new Cat()
let animals: Animal[] = [dog, cat]
animals.forEach(i => {
  i.sleep()
})

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-07-29,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 前端先锋 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是重构 ?
  • 1. 这是如下我们要实现的目标任务列表(每天进步一点点⏰)
  • 2. Change Reference to Value(将引用对象改为值对象)
  • 3. Change Value to Reference(将值对象改为引用对象)
  • 4. Collapse Hierarchy(折叠继承体系)
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档