《重构-代码整洁之道TypeScript版》第2天
时间:2022-07-22
本文章向大家介绍《重构-代码整洁之道TypeScript版》第2天,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
昨天我们发了第一篇,今天让我们来继续第二天。先来回顾一下昨天我们都实现了哪些:
- 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(将引用对象改为值对象)
描述?:你有一个引用对象,很小且不可改变,而且不易管理。
动机?:在分布系统和并发系统中,不可变的值对象特别有用,因为你无需考虑他们的同步问题。
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);
要把一个引用对象变成值对象,关键动作:检查它是否不可变。如果不是,我们就用不到使用本项重构,因为可变的值会造成凡人的别名问题。
//如上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.js
和 Immutable.js
yarn add immutable -D
yarn add redux -D
yarn add @types/immutable -D
// 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'));
// 完整版的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(将值对象改为引用对象)
描述?:从一个类衍生出许多彼此相等的实例,希望将他们替换为同一对象。
动机?:许多系统中,像日期、数量、名称等他们完全由所含的意义来标识,你并不在乎他们有多少副本。如果在你的副本中需要某一对象的变化影响到引用了他的地方,就需要考虑将这个对象变成一个引用对象。
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);
其实我们的工厂模式专门就是做这件事的
//我用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(折叠继承体系)
描述?:当超类和子类之间无太大区别的时候,应将他们合为一体。
动机?:如果你曾经编写过继承体系,就会知道,继承体系很容易变得过分复杂。新重构继承体系,往往是将函数和字段在体系中上下移动。完成这些动作后,你很可能发现某个子类并未带来该有的价值,因此需要把超类与子类合并起来。
// 我们把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()
})
- 使用apt-get 安装后的mysql 登录问题
- SQL Server 深入解析索引存储(中)
- 1789: [Ahoi2008]Necklace Y型项链
- 3399: [Usaco2009 Mar]Sand Castle城堡
- 遗传算法(1)
- LOJ#6284. 数列分块入门 8
- 3713: [PA2014]Iloczyn
- 洛谷P3195 [HNOI2008]玩具装箱TOY(单调队列优化DP)
- SQL Server 深入解析索引存储(下)
- 2751: [HAOI2012]容易题(easy)
- codevs3002 石子归并 3
- 算法模板——计算几何2(二维凸包——Andrew算法)
- 算法模板——splay区间反转 2
- 算法模板——Dinic网络最大流 2
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法