软件开发可靠,提升和第一原则
#javascript #编程 #solid #software

固体,提升和第一原则

它们都是软件开发领域的最佳实践和设计原则集,旨在提高代码的可维护性,可读性和可扩展性。他们每个人都有不同的目的,但相互补充,旨在生产高质量,可管理且健壮的软件。

坚硬的:

这是面向对象的编程和设计中五个设计原理的首字母缩写。这些原则可以帮助开发人员创建易于维护,理解和扩展的系统。

举起:

该原则提供了一组指南,可以帮助构建和组织代码,尤其是在大型代码库中。它可以帮助开发人员快速找到并确定他们要寻找的代码。

第一的:

这主要与在软件开发中编写良好的测试,尤其是单位测试有关。第一原则有助于创建快速运行,可以独立运作,提供一致的结果,验证自己并可以及时写入的有效测试。

尽管SOLIDLIFTFIRST可能专注于软件开发过程的不同方面,但它们共享相同的目标:提高代码质量。现在,当一起使用时,这些原理可以帮助创建清洁,高效且可维护的代码。

良好设计(固体),清晰的组织(提升)和有效测试(第一)的组合可以大大提高软件项目的鲁棒性和质量。


坚硬的

实心描述了面向对象的软件设计的五个基本原理。它提倡一种开发方法,该方法使您能够生产可以轻松扩展且易于阅读的软件。

¢S-(SRP)单个责任原则
A Class should only do ONE job或“责任”

// Violates SRP
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})
export class UserService {
  constructor(private http: HttpClient) { }

  getUserData(userId: string) {
    return this.http.get(`https://example.com/user/${userId}`);
  }

  saveUserDataInLocalStorage(userData) {
    localStorage.setItem('user', JSON.stringify(userData));
  }
}
// Follows SRP
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})
export class UserDataService {
  constructor(private http: HttpClient) { }

  getUserData(userId: string) {
    return this.http.get(`https://example.com/user/${userId}`);
  }
}

@Injectable({
  providedIn: 'root',
})
export class LocalStorageService {
  saveUserDataInLocalStorage(userData) {
    localStorage.setItem('user', JSON.stringify(userData));
  }
}

¢O-(OCP)开/关闭原理
Software entities (classes, modules, function, etc) should be: open for extension (inherit, extend, interfaces of, etc), but closed for modification (it works, do not touch.)

在角度(类似于ReactJ)中,应用OCP的一种方法是使用依赖注射(DI)。依赖注入允许我们提供服务的不同实现,而无需更改服务代码。

FallbackUserService中,我们使用原始的UserService来获取用户数据。如果失败,我们会提供默认用户数据。这样,用户服务仍然“关闭”以进行修改,但它是“ shoultbackuserservice”的“打开”。

@Injectable({
  providedIn: 'root',
})
export class FallbackUserService {
  // UserService remains "closed" for changes but "open" for extension
  constructor(private userService: UserService) { }

  getUserData(userId: string) {
    return this.userService.getUserData(userId).pipe(
      catchError(() => of({ id: userId, name: 'Default User' }))
    );
  }
}

¢l -(LSP) Liskov Substitution Principle (LSP)
Objects of a "base class" should be replasable with any of its "subclasses" without breaking stuff,没有更改该程序的正确性,因为子类必须像其父

一样行为

现在,假设我们必须支持另一种用户数据检索 - 例如,VIP用户,他们拥有自己的API端点并且可能具有更多的数据字段。

没有LSP,我们可能会很想在诸如getVipuserData()之类的用户dataservice中添加另一种方法。但是,在LSP之后,我们将创建一个新的类,扩展UserDataService:

@Injectable({
  providedIn: 'root',
})
export class UserDataService {
  constructor(protected http: HttpClient) { }

  getUserData(userId: string) {
    return this.http.get(`https://example.com/user/${userId}`);
  }
}

@Injectable({
  providedIn: 'root',
})
export class VIPUserDataService extends UserDataService {
  getUserData(userId: string) {
    return this.http.get(`https://example.com/vipuser/${userId}`);
  }
}

现在,VipuserDataService是用户dataservice的子类,它们可以互换而不影响程序的功能。这样可以确保使用UserDataService的任何功能也可以接受VipuserDataService。

¢i -Interface segregation principle (ISP)
客户(或在这种情况下,组件或其他服务)不应被迫依靠他们不使用的接口。本质上,是better to have many small, "specific" interfaces than one large, general, "multi-purpose" interface

// Interface for reading user data
export interface UserDataReader {
  getUserData(userId: string): Observable<User>;
}

// Interface for managing user data
export interface UserDataManager {
  createUser(user: User): Observable<User>;
  updateUser(user: User): Observable<User>;
  deleteUser(userId: string): Observable<void>;
}
@Injectable({
  providedIn: 'root',
})
export class UserDataService implements UserDataReader, UserDataManager {
  constructor(private http: HttpClient) { }

  getUserData(userId: string): Observable<User> {
    return this.http.get<User>(`https://example.com/user/${userId}`);
  }

  createUser(user: User): Observable<User> {
    return this.http.post<User>('https://example.com/user', user);
  }

  updateUser(user: User): Observable<User> {
    return this.http.put<User>(`https://example.com/user/${user.id}`, user);
  }

  deleteUser(userId: string): Observable<void> {
    return this.http.delete<void>(`https://example.com/user/${userId}`);
  }
}

现在,任何只需要读取用户数据的组件或服务都可以取决于用户数据,并且也不会被迫依赖于创建,更新和删除用户数据的方法(这是UserDatamanager的一部分)。<<<<<<<<<<<<<<<<<< /p>

这遵守ISP,它使我们的代码更加灵活,更易于理解,并且不太可能从更改中脱颖而出。

¢d -(DIP) Dependency Inversion Principle
High-level modules should not depend on low-level modules. Both should depend on abstractions而不是具体。 (依赖注射是按照此原则的一种方法)

要与我们的用户dataService示例Angular实现依赖性反转原理,我们将首先需要创建一个抽象服务(typescript中的接口)。该接口将是任何“用户数据服务”都需要实现的合同:

export interface IUserDataService {
  getUserData(userId: string);
}

然后,我们使我们的UserDataService实现此接口:

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})
export class UserDataService implements IUserDataService {
  constructor(private http: HttpClient) { }

  getUserData(userId: string) {
    return this.http.get(`https://example.com/user/${userId}`);
  }
}

现在,使用UserDataService的任何组件或服务都应取决于IuserDataService(抽象),而不是直接在UserDataService(细节)上。

import { Component, OnInit } from '@angular/core';
import { IUserDataService } from './i-user-data.service';

@Component({
  selector: 'app-user',
  templateUrl: './user.component.html',
  styleUrls: ['./user.component.css']
})
export class UserComponent implements OnInit {
  constructor(private userDataService: IUserDataService) { } // depends on the abstraction, not the detail

  ngOnInit() {
    this.userDataService.getUserData('123').subscribe(
      data => {...}
    );
  }
}

现在,以这种方式,UserComponent取决于抽象(IuserDataservice),而不是细节(UserDataService)。如果我们需要更改获取用户数据的方式(例如,如果要从本地存储而不是HTTP服务器获取数据),我们可以创建一个实现IuserDataService的新服务,并且USERCOMPONENT无需在全部。


FIRST

第一原则是构建清洁,可维护的代码的绝佳方法。它与软件开发中固体和其他最佳实践中的许多概念保持一致。让我们打破每个原则。我们可以吗?

首先保留组件:专注,独立,可重复使用,小且可测试。

¢F - every object should have a single responsibility(该原理与固体的单个责任原则(SRP)紧密相符)

¢I - make it more独立且可测试的(这里的目的是将组件的依赖性减少到最低))

¢R - Reusable code can save you time and make it portable

¢S - Smaller APIs are easier to learn and teach to others. This is generally helped if you do one thing and do it well.(。此原理也与SRP保持一致,因为只有一件事的组件往往很小。)

¢T - Test your code(如果组件是专注,独立,可重复使用和小的,则可以更容易地测试代码的行为,与LSP原理相似)


LIFT

¢L: Locating our code is easy

¢I: Identify code at a glance

¢F: Flat structure as long as we can

¢T: Try to stay DRY


最后说明:

如果您喜欢干净的代码实践,我敢打赌您已经阅读了:“干净的代码:敏捷软件手工艺手册”
罗伯特·马丁(Robert C. Martin)(2008)必须阅读。