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

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

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

看到这个标题你的脑海中一定会浮现出两本书,一本就是,《重构--改善既有代码设计》《代码整洁之道》 。这确实是两本非常伟大的图书,但是很遗憾里面提供的 code 都是 Java 的版本。《重构--改善既有代码设计》 的第2版提供了 JavaScript 的版本,已经非常方便我们前端同学阅读了,但是在 TypeScrip 如此火热的今天,缺了 TS 的版本,始终觉得是些遗憾,所以老袁打算每天拿出一些时间将一些非常经典的案例,结合老袁十年的经验总结到一块使用 TS 重写,希望能陪伴各位的技术成长之路,我会从大致如下方向跟各位共同分享:

  • 利用npm有效的工具校验代码的坏味道
  • 使用Jest等前端测试工具构筑测试体系
  • 使用TypeScript重新组织你的函数、数据
  • 简化你的函数表达式、函数调用
  • 处理概括关系、重构实战、基于重构的cli工具
  • 更多....

希望你能和我一起完成这段旅程,写出整洁的TypeScript:

什么是重构 ?

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

何时开始我们的重构?

  • ① 重复性工作,既有的代码无法帮助你轻松添加新特性时
  • ② 修补bug时,排查逻辑困难
  • ③ 太多的代码无注释,已然连自己都无法快速理清代码逻辑

1. 这是如下我们要实现的目标任务列表(打选中的为我们已经实践过的)

  • [x] Add Parameter(添加参数)
  • [x] Change Bidirectional Association to Unidirectional(将双向关联改为单向关联)
  • [x] Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)
  • [ ] Change Reference to Value(将引用对象改为值对象)
  • [ ] Change Value to Reference(将值对象改为引用对象)
  • [ ] 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. Add Parameter(添加参数)

描述?:如果一个函数能够不需要任何参数能够解决你的问题(包括使用其他的函数,),这当然是绝佳的。但是在我们日常开发中需要经常为函数添加参数。

动机?:所以使用这个重构的动机很简单,你必须添加一个函数,而修改后的函数需要一些过去没有的信息

代码语言:javascript
复制
// ① 函数参数 (理论上少于等于2个)
function createMenu(title, body, buttonText, cancellable) {
  ...
}
// ② 超过2个参数 使用对象统一传递
interface IMenuConfig {
  title?: string;
  body?: string;
  buttonText?: string;
  cancellable?: boolean;
}
const menuConfig: IMenuConfig = {
  title: 'Foo',
  body: 'Bar',
  buttonText: 'Baz',
  cancellable: true,
};

function createMenu(menuConfig: IMenuConfig) {}

我们上面解决了将多个参数合并为一个,减少了过长的参数(它是代码的坏味道Data Clnmps,坏味道不是翻译的尴尬是坏味道包含如下),后面也会给大家逐步介绍。

Duplicated Code(重复代码) Long Method(过长函数) Large Class(过大的类) Long Parameter List(过长的参数列) Divergent Change(发散式变化) 等...

代码语言:javascript
复制
function add(...rest: number[]): number;
function add(...rest: string[]): string;
function add(...rest: any[]) {
    let first = rest[0];
    if (typeof first === 'number') {
        return rest.reduce((pre, cur) => pre + cur);
    }
    if (typeof first === 'string') {
        return rest.join('');
    }
}
console.log(add(1, 2))
console.log(add('a', 'b', 'c'));
//不过如上代码我们可以更加简洁的完成它
interface ConfigFn {
  <T>(value: T): void;
}
var getData2: ConfigFn = function <T>(value: T): T {
  return value;
};
getData2<string>('老袁');

当然有关于函数这里我们还有很多其他的重构规则 我们后续再慢慢给大家渗透。

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

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

动机?:双向关联很有用,但是也必须为它付出代价,那就是维护双向链接。大量的双向链接很用以造成某个对象已经死亡,但是依然存在堆区造成内存泄漏。此外双向连接之间有了依赖,如果这是两个独立的文件没有经过webpack打包,比如运行在node中,这样就是跨文件的依赖。跨文件的依赖就会造成耦合系统,使得任何一点点像小改动都造成许多无法预知的后果。所以只有真正需要双向关联的时候,才去使用它。

代码语言:javascript
复制
// 打折商品订单类
class Order {
  private _customer: Customer;
  public getCustomer(): Customer {
    return this._customer;
  }
  public setCustomer(customerName: string, customer: Customer): void {
    const { _customer } = this;
    if (_customer) {
      // 假设我们规定一个打折商品只能购买一单 价格实时计算
      customer.friendOrders.delete(customerName);
    } else {
      this._customer = customer;
      customer.friendOrders.set(customerName, this);
    }
  }
  public disCountedPrice(): number {
    return Math.random() * this._customer.getDiscount();
  }
}
class Customer {
  private _orders = new Map<string, Order>();
  public addOrder(orderName: string, order: Order): void {
    order.setCustomer(orderName, this);
  }
  public getDiscount(): number {
    //根据用户等级获取除折扣以外的价格
    return 0.8;
  }
  public getPriceFor(order: Order): number {
    return order.disCountedPrice();
  }
  get friendOrders() {
    return this._orders;
  }
}
const john = new Customer();
const basket = new Order();
john.addOrder('shoes', basket);
const priceShoes = john.getPriceFor(basket);
console.log('①', priceShoes);
john.addOrder('clothes', basket);
const priceClothes = john.getPriceFor(basket);
console.log('②', priceClothes);
console.log('一共消费', priceShoes + priceClothes);

/* ①以上这段的核心是为了计算最后商品的价格
*  ②获取商品的价格我们分为了2步骤 一个是获取用户的VIP级别
*  ③然后获取了商品本身的随机打折数  最终相乘
*  ④其核心代码是addOrder好给Order的_customer赋值 
     其实完全没必要
*/

仔细想想,先有Customer才会有Order,我们完全可以把链接移除。Order获取折扣信息依赖_customer,所以:

代码语言:javascript
复制
//修改Order 然后外部传入customer 将两个类彻底分开
public disCountedPrice(customer: Customer): number {
    return Math.random() * customer.getDiscount();
}
//或者直接修改Customer
public getPriceFor(order: Order): number {
    return order.disCountedPrice(this);
}

最后还有一种暴力的手段,我们可以修改getCustomer主动去寻找Customer的实例。这样就保持了单向,最后我们把setCustomer其实就可以取消了。核心就是去除反向指针。

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

这个就跟上面?刚好相反了,也就是说如果两个类之间相互需要制约,必须由某个类控制某个类,就刚好符合将单向关联改为双向关联,那么vue的双向数据绑定的watcher 和 data之间的关系就是刚好最适合不过了。

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是重构 ?
  • 何时开始我们的重构?
    • 1. 这是如下我们要实现的目标任务列表(打选中的为我们已经实践过的)
      • 2. Add Parameter(添加参数)
        • 3. Change Bidirectional Association to Unidirectional(将双向关联改为单向关联)
          • 4. Change Unidirectional Association to Bidirectional(将单向关联改为双向关联)
          相关产品与服务
          流计算 Oceanus
          流计算 Oceanus 是大数据产品生态体系的实时化分析利器,是基于 Apache Flink 构建的企业级实时大数据分析平台,具备一站开发、无缝连接、亚秒延时、低廉成本、安全稳定等特点。流计算 Oceanus 以实现企业数据价值最大化为目标,加速企业实时化数字化的建设进程。
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档