固体,提升和第一原则
它们都是软件开发领域的最佳实践和设计原则集,旨在提高代码的可维护性,可读性和可扩展性。他们每个人都有不同的目的,但相互补充,旨在生产高质量,可管理且健壮的软件。
坚硬的:
这是面向对象的编程和设计中五个设计原理的首字母缩写。这些原则可以帮助开发人员创建易于维护,理解和扩展的系统。
举起:
该原则提供了一组指南,可以帮助构建和组织代码,尤其是在大型代码库中。它可以帮助开发人员快速找到并确定他们要寻找的代码。
第一的:
这主要与在软件开发中编写良好的测试,尤其是单位测试有关。第一原则有助于创建快速运行,可以独立运作,提供一致的结果,验证自己并可以及时写入的有效测试。
尽管SOLID
,LIFT
和FIRST
可能专注于软件开发过程的不同方面,但它们共享相同的目标:提高代码质量。现在,当一起使用时,这些原理可以帮助创建清洁,高效且可维护的代码。
良好设计(固体),清晰的组织(提升)和有效测试(第一)的组合可以大大提高软件项目的鲁棒性和质量。
坚硬的
实心描述了面向对象的软件设计的五个基本原理。它提倡一种开发方法,该方法使您能够生产可以轻松扩展且易于阅读的软件。
¢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)必须阅读。