美文网首页
TypeScript 代码整洁之道 - 类

TypeScript 代码整洁之道 - 类

作者: 小校有来有去 | 来源:发表于2019-03-01 23:04 被阅读4次

    将 Clean Code 的概念适用到 TypeScript,灵感来自 clean-code-javascript
    原文地址: clean-code-typescript
    中文地址: clean-code-typescript

    简介

    image

    这不是一份 TypeScript 设计规范,而是将 Robert C. Martin 的软件工程著作 《Clean Code》 适用到 TypeScript,指导读者使用 TypeScript 编写易读、可复用和易重构的软件。

    小、小、小!要事情说三遍

    类的大小是由它的职责来度量的。按照单一职责原则,类要小。

    反例:

    
    class Dashboard {
    
      getLanguage(): string { /* ... */ }
    
      setLanguage(language: string): void { /* ... */ }
    
      showProgress(): void { /* ... */ }
    
      hideProgress(): void { /* ... */ }
    
      isDirty(): boolean { /* ... */ }
    
      disable(): void { /* ... */ }
    
      enable(): void { /* ... */ }
    
      addSubscription(subscription: Subscription): void { /* ... */ }
    
      removeSubscription(subscription: Subscription): void { /* ... */ }
    
      addUser(user: User): void { /* ... */ }
    
      removeUser(user: User): void { /* ... */ }
    
      goToHomePage(): void { /* ... */ }
    
      updateProfile(details: UserDetails): void { /* ... */ }
    
      getVersion(): string { /* ... */ }
    
      // ...
    
    }
    
    

    正例:

    
    class Dashboard {
    
      disable(): void { /* ... */ }
    
      enable(): void { /* ... */ }
    
      getVersion(): string { /* ... */ }
    
    }
    
    // split the responsibilities by moving the remaining methods to other classes
    
    // ...
    
    

    高内聚低耦合

    内聚:定义了类成员之间相互关联的程度。理想情况下,高内聚类的每个方法都应该使用类中的所有字段,实际上这不可能也不可取。但我们依然提倡高内聚。

    耦合:指的是两个类之间的关联程度。如果其中一个类的更改不影响另一个类,则称为低耦合类。

    好的软件设计具有高内聚性低耦合性

    反例:

    
    class UserManager {
    
      // Bad: each private variable is used by one or another group of methods.
    
      // It makes clear evidence that the class is holding more than a single responsibility.
    
      // If I need only to create the service to get the transactions for a user,
    
      // I'm still forced to pass and instance of emailSender.
    
      constructor(
    
        private readonly db: Database,
    
        private readonly emailSender: EmailSender) {
    
      }
    
      async getUser(id: number): Promise<User> {
    
        return await db.users.findOne({ id })
    
      }
    
      async getTransactions(userId: number): Promise<Transaction[]> {
    
        return await db.transactions.find({ userId })
    
      }
    
      async sendGreeting(): Promise<void> {
    
        await emailSender.send('Welcome!');
    
      }
    
      async sendNotification(text: string): Promise<void> {
    
        await emailSender.send(text);
    
      }
    
      async sendNewsletter(): Promise<void> {
    
        // ...
    
      }
    
    }
    
    

    正例:

    
    class UserService {
    
      constructor(private readonly db: Database) {
    
      }
    
      async getUser(id: number): Promise<User> {
    
        return await db.users.findOne({ id })
    
      }
    
      async getTransactions(userId: number): Promise<Transaction[]> {
    
        return await db.transactions.find({ userId })
    
      }
    
    }
    
    class UserNotifier {
    
      constructor(private readonly emailSender: EmailSender) {
    
      }
    
      async sendGreeting(): Promise<void> {
    
        await emailSender.send('Welcome!');
    
      }
    
      async sendNotification(text: string): Promise<void> {
    
        await emailSender.send(text);
    
      }
    
      async sendNewsletter(): Promise<void> {
    
        // ...
    
      }
    
    }
    
    

    组合大于继承

    正如“四人帮”在设计模式中所指出的那样,您尽可能使用组合而不是继承。组合和继承各有优劣。这个准则的主要观点是,如果你潜意识地倾向于继承,试着想想组合是否能更好地给你的问题建模,在某些情况下可以。

    什么时候应该使用继承?这取决于你面临的问题。以下场景使用继承更好:

    1. 继承代表的是“is-a”关系,而不是“has-a”关系 (人 -> 动物 vs. 用户 -> 用户详情)。
    2. 可复用基类的代码 (人类可以像所有动物一样移动)。
    3. 希望通过更改基类对派生类进行全局更改(改变所有动物在运动时的热量消耗)。

    反例:

    
    class Employee {
    
      constructor(
    
        private readonly name: string, 
    
        private readonly email:string) {
    
      }
    
      // ...
    
    }
    
    // Bad because Employees "have" tax data. EmployeeTaxData is not a type of Employee
    
    class EmployeeTaxData extends Employee {
    
      constructor(
    
        name: string, 
    
        email:string,
    
        private readonly ssn: string, 
    
        private readonly salary: number) {
    
        super(name, email);
    
      }
    
      // ...
    
    }
    
    

    正例:

    
    class Employee {
    
      private taxData: EmployeeTaxData;
    
      constructor(
    
        private readonly name: string, 
    
        private readonly email:string) {
    
      }
    
      setTaxData(ssn: string, salary: number): Employee {
    
        this.taxData = new EmployeeTaxData(ssn, salary);
    
        return this;
    
      }
    
      // ...
    
    }
    
    class EmployeeTaxData {
    
      constructor(
    
        public readonly ssn: string, 
    
        public readonly salary: number) {
    
      }
    
      // ...
    
    }
    
    

    使用方法链

    非常有用的模式,在许多库中都可以看到。它让代码表达力更好,也更简洁。

    反例:

    
    class QueryBuilder {
    
      private collection: string;
    
      private pageNumber: number = 1;
    
      private itemsPerPage: number = 100;
    
      private orderByFields: string[] = [];
    
      from(collection: string): void {
    
        this.collection = collection;
    
      }
    
      page(number: number, itemsPerPage: number = 100): void {
    
        this.pageNumber = number;
    
        this.itemsPerPage = itemsPerPage;
    
      }
    
      orderBy(...fields: string[]): void {
    
        this.orderByFields = fields;
    
      }
    
      build(): Query {
    
        // ...
    
      }
    
    }
    
    // ...
    
    const query = new QueryBuilder();
    
    query.from('users');
    
    query.page(1, 100);
    
    query.orderBy('firstName', 'lastName');
    
    const query = queryBuilder.build();
    
    

    正例:

    
    class QueryBuilder {
    
      private collection: string;
    
      private pageNumber: number = 1;
    
      private itemsPerPage: number = 100;
    
      private orderByFields: string[] = [];
    
      from(collection: string): this {
    
        this.collection = collection;
    
        return this;
    
      }
    
      page(number: number, itemsPerPage: number = 100): this {
    
        this.pageNumber = number;
    
        this.itemsPerPage = itemsPerPage;
    
        return this;
    
      }
    
      orderBy(...fields: string[]): this {
    
        this.orderByFields = fields;
    
        return this;
    
      }
    
      build(): Query {
    
        // ...
    
      }
    
    }
    
    // ...
    
    const query = new QueryBuilder()
    
      .from('users')
    
      .page(1, 100)
    
      .orderBy('firstName', 'lastName')
    
      .build();
    
    

    上一章:TypeScript 代码整洁之道 - 对象和数据结构
    下一章:TypeScript 代码整洁之道 - SOLID原则

    相关文章

      网友评论

          本文标题:TypeScript 代码整洁之道 - 类

          本文链接:https://www.haomeiwen.com/subject/kqjruqtx.html