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


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

  • Consolidate Conditional Expression(合并条件表达式) 
  • Consolidate Duplicate Conditional Fragments(合并重复的条件片断)  
  • Convert Procedural Design to Objects(将过程化设计转化为对象设计)  

(图片:梅里雪山)java

什么是重构 ?

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


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

  • 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. Decompose Conditional(分解条件表达式) 

描述🍏:你有一个复杂的if..else if ...else语句,能够把它从复杂的代码中提取出来面试

动机🍃: 在业务开发中,你必须编写代码来检查不一样的条件分支、根据不一样的分支作不一样的事,而后你就会获得一个至关长的函数,大型函数自身就会使代码的可读性降低,而条件逻辑则会使代码更难阅读。算法

//假设我要计算购买某样商品的总价(总价=数量X单价),而这个商品在冬季和夏季的单价是不一样的:
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
  charge = quantity * _winterRate + _winterServiceCharge;
} else {
  charge = quantity * _summerRate;
}
复制代码

我把每一个分支的判断条件都提炼到一个独立函数中,以下所示(伪代码):typescript

if(Summer(date)){
  charge = winterCharge(quantity);
}else{
  charge = summerCharge(quantity);
} 
// ...以下代码在类中
private  Summer(date:Date):boolean{
  return date.before(SUMMER_START) || date.after(SUMMER_END); 
}
private  summerCharge(quantity:number):number{
  return quantity*summerRate;
}
private  winterCharge(int quantity){
  return quantity* winterRate+_winterServiceCharge;
}
复制代码

经过如上代码咱们能看到整个结构更加清晰。还有一个神来之笔就是用字典了。数据库

const result = 'a';
// bad
if(result == "a"){
  ...
}else if(result == "b"){
   ...
}else{
  ...
}
//best 咱们采用字典
interface IDictionaries {
  [key: string]: any;
}

const obj: IDictionaries = {
  a: () => {},
  b: () => {},
};
const fn = obj[result];
fn && fn();

复制代码

3. Duplicate Observed Data(复制“被监视数据”) 

描述🐥:你有一些数据置身在GUI控件中(HTML页面)中,而领域函数(在服务端、NW开发的PC端等)须要访问这些数据。将该数据复制到一个领域对象中,创建一个Observer模式,用以同步领域对象和GUI对象内的重复数据。后端

上文提到了领域对象,对于不少Web前端的同窗是很模糊的,咱们来一块儿学习下吧。数组

“领域对象(Domain Object)也被称为实体类,它表明了业务的状态,且贯穿展示层、业务层和持久层,并最终持久化到数据库,若是只是简单的理解的话,领域对象能够看作是数据库表的对应java类。若是对应到你们的前端开发里也就是咱们Vue里的data啦。markdown

说到了领域对象就必定要谈一下领域驱动模型。业务逻辑都是写在Service中的,WmActPoi充其量只是个数据载体(Vue里的data只是定义了数据的基本形态,所有业务处理逻辑都堆积在了method里)没有任何行为,是一种贫血模型,下图呢就是DDD的总体运行流程。

采用DDD的设计思想,业务逻辑再也不集中在几个大型的类上,而是由大量相对小的领域对象(类)组成,这些类具有本身的状态和行为,每一个类是相对完整的独立体,并与现实领域的业务对象映射。领域模型就是由这样许多的细粒度的类组成。以下我贴上一段很是核心的TypeScript实战DDD的代码。

看不懂的同窗你们能够关注下@node-ts/ddd

// user.ts
import { AggregateRootProperties, AggregateRoot, Uuid } from '@node-ts/ddd'
import { UserRegistered, UserPasswordChanged, UserDisabled } from './events'
import { OAuthService } from './services'

export interface UserProperties extends AggregateRootProperties {
  email: string
  isEnabled: boolean
  passwordChangedAt: Date | undefined
}

export class User extends AggregateRoot implements UserProperties {
  email: string
  isEnabled: boolean
  passwordChangedAt: Date | undefined

  // Creation static method. Aggregates are never "newed" up by consumers.
  static register (id: Uuid, email: string): User {
    const userRegistered = new UserRegistered(
      id,
      email,
      true
    )

    const user = new User(id)
    // event is applied to the user object
    user.when(userRegistered)
    return user
  }

  /** * Changes the user's password that's used to log in to the site * @param oauthService the oauth service that hosts the user account * @param newPassword password the user wants to use */
  async changePassword (oauthService: OAuthService, newPassword: string): Promise<void> {
    // A domain service is used to perform the actual change of password
    await oauthService.changePassword(this.id, newPassword)

    const userPasswordChanged = new UserPasswordChanged(
      this.id,
      new Date()
    )
    super.when(userPasswordChanged)
  }

  /** * Disable the user account so they can no longer log in */
  disable (): void {
    const userDisabled = new UserDisabled(this.id, false)
    super.when(userDisabled)
  }
  
  protected whenUserRegistered (event: UserRegistered): void {
    this.email = event.email
    this.isEnabled = event.isEnabled
  }

  protected whenPasswordChanged (event: UserPasswordChanged): void {
    this.passwordChangedAt = event.passwordChangedAt
  }

  protected whenUserDisabled (event: UserDisabled): void {
    this.isEnabled = event.isEnabled
  }
}
复制代码

动机🐈:一个分层良好的系统,应该将处理用户界面和处理业务逻辑的代码分开。若果你遇到的代码是以两层方式开发,业务逻辑被内嵌在用户的界面之中,你就有必要将行为分离出来,其中重要的任务就是函数的分解和搬移。但数据就不一样了:你不能仅仅只是移动数据,必须将它复制到新的对象中,并提供相同的同步机制。

// 其实这个规则说成前端的大白话就是若是一个数据先后都须要 就把他设置成可Observable
// 目前的前端开发处理这个有Vue 、Mobx等等。可是单独处理这个有Rx.js 
const locations = new RX.Observable((observer) => {
    let watchId: number;
    if ('geolocation' in navigator) {
      watchId = navigator.geolocation.watchPosition((position: Position) => {
        observer.next(position);
      }, (error: PositionError) => {
        observer.error(error);
      });
    } else {
      observer.error('Geolocation not available');
    }
  
    return {
      unsubscribe() {
        navigator.geolocation.clearWatch(watchId);
      }
    };
  });
  
  const locationsSubscription = locations.subscribe({
    next(position:Position) {
      console.log('Current Position: ', position);
    },
    error(msg:string) {
      console.log('Error Getting Location: ', msg);
    }
  });
  
  setTimeout(() => {
    locationsSubscription.unsubscribe();
  }, 10000);

复制代码

如上的demo监听的是定位信息,固然这个定位也能够是后端发起请求的数据,说白了多端一块儿监听这个locations,而不是分散到各自的类中各自维护。

4. Encapsulate Collection(封装集合) 

描述🌾:让一个函数返回该集合的一个只读副本,并在这个类中提供添加、移除集合元素的函数。

动机🐻:咱们经常会在一个类中使用集合(Map、Set、Array),一般这样的类也会提供针对该集合的取值、设值函数。这个时候,取值函数不该该返回集合自己,由于这样会让用户得以修改集合内容而集合拥有者缺一无所知,这样也会暴露过多对象内部数据结构的信息。另外设置函数不该该提供添加修改和移除的操做,但不能直接重写该集合。若是作到了这些集合就被很好的封装了起来,这样即可以下降集合永州这和用户之间的耦合度。

这个规则不是很复杂,老袁就不给你们列具体的 code了。

前端圈最好用的刷题工具

扫码 get 前端圈刷题神器,解锁800+道 有答案和解析的 前端面试真题,所有来自BAT、TMD等一二线互联网公司。


回顾第一天的文章👉 :《重构-代码整洁之道TypeScript版》第一天

回顾次日的文章👉 :《重构-代码整洁之道TypeScript版》次日

回顾第三天的文章👉 :《重构-代码整洁之道TypeScript版》第三天

每一次咱们不会给你们写过多的重构规则,力求天天用几分钟时间真正去理解了重构。明天见~若是您有什么意见和反馈,欢迎您在下方留言。

做者 【老袁】 2020 年 07月 31日

相关文章
相关标签/搜索