你好呀!你最近怎麼樣?你沒事吧?但願如此!
今天我要談一個每個人都在談論或寫到的主題。但有時要理解每一個原則是很困難的。我說的是固體。
當我問及 SOLID 時,很多人可能總是記得第一個原則(單一職責原則)。但當我問起另一個人時,有些人不記得了,或覺得很難解釋。我明白了。
確實,如果不編碼或重新修改每個原則的定義,就很難解釋。但在本文中,我想以簡單的方式介紹每個原則。所以我會用Typescript來舉例。
那麼就讓我們開始吧!
更容易理解和記住的原則。
當我們編碼時,很容易發現我們何時忘記了原則。
假設我們有一個 TaskManager 類別:
class TaskManager {
constructor() {}
connectAPI(): void {}
createTask(): void {
console.log("Create Task");
}
updateTask(): void {
console.log("Update Task");
}
removeTask(): void {
console.log("Remove Task");
}
sendNotification(): void {
console.log("Send Notification");
}
sendReport(): void {
console.log("Send Report");
}
}
好的!也許你注意到你的問題了,不是嗎?
TaskManager 類別有很多不屬於她的職責。例如:sendNotification 和 sendReport 方法。
現在,讓我們重構並應用該解決方案:
class APIConnector {
constructor() {}
connectAPI(): void {}
}
class Report {
constructor() {}
sendReport(): void {
console.log("Send Report");
}
}
class Notificator {
constructor() {}
sendNotification(): void {
console.log("Send Notification");
}
}
class TaskManager {
constructor() {}
createTask(): void {
console.log("Create Task");
}
updateTask(): void {
console.log("Update Task");
}
removeTask(): void {
console.log("Remove Task");
}
}
很簡單,不是嗎?我們只是將通知和報告分離到指定的類別中。現在我們尊重單一原則責任!
定義: Each class must have one, and only one, reason to change
。
第二個原則。另外,我認為很容易理解。給您一個提示,如果您發現在某種方法中需要滿足很多條件來驗證某些內容,那麼您可能遇到了 OCP。
讓我們想像一下下面的考試類別範例:
type ExamType = {
type: "BLOOD" | "XRay";
};
class ExamApprove {
constructor() {}
approveRequestExam(exam: ExamType): void {
if (exam.type === "BLOOD") {
if (this.verifyConditionsBlood(exam)) {
console.log("Blood Exam Approved");
}
} else if (exam.type === "XRay") {
if (this.verifyConditionsXRay(exam)) {
console.log("XRay Exam Approved!");
}
}
}
verifyConditionsBlood(exam: ExamType): boolean {
return true;
}
verifyConditionsXRay(exam: ExamType): boolean {
return false;
}
}
是的,您可能已經多次看到這段程式碼了。首先,我們打破了SRP的第一原則,並提出了許多條件。
現在想像一下如果出現另一種類型的檢查,例如超音波。我們需要加入另一個方法來驗證和另一個條件。
讓我們重構一下這段程式碼:
type ExamType = {
type: "BLOOD" | "XRay";
};
interface ExamApprove {
approveRequestExam(exam: NewExamType): void;
verifyConditionExam(exam: NewExamType): boolean;
}
class BloodExamApprove implements ExamApprove {
approveRequestExam(exam: ExamApprove): void {
if (this.verifyConditionExam(exam)) {
console.log("Blood Exam Approved");
}
}
verifyConditionExam(exam: ExamApprove): boolean {
return true;
}
}
class RayXExamApprove implements ExamApprove {
approveRequestExam(exam: ExamApprove): void {
if (this.verifyConditionExam(exam)) {
console.log("RayX Exam Approved");
}
}
verifyConditionExam(exam: NewExamType): boolean {
return true;
}
}
哇,好多了!現在,如果出現另一種類型的檢查,我們只需實作ExamApprove
介面。如果出現另一種類型的考試驗證,我們只更新介面。
定義: Software entities (such as classes and methods) must be open for extension but closed for modification
理解和解釋比較複雜的一種。但我怎麼說,我會讓你更容易理解。
想像一下,您有一所大學和兩種類型的學生。學生和研究生。
class Student {
constructor(public name: string) {}
study(): void {
console.log(`${this.name} is studying`);
}
deliverTCC() {
/** Problem: Post graduate Students don't delivery TCC */
}
}
class PostgraduateStudent extends Student {
study(): void {
console.log(`${this.name} is studying and searching`);
}
}
我們這裡有一個問題,我們正在延長學生的時間,但研究生不需要提供 TCC。他只是研究和尋找。
我們要怎麼解決這個問題呢?簡單的!讓我們建立一個學生類,並將畢業學生和畢業後學生分開:
class Student {
constructor(public name: string) {}
study(): void {
console.log(`${this.name} is studying`);
}
}
class StudentGraduation extends Student {
study(): void {
console.log(`${this.name} is studying`);
}
deliverTCC() {}
}
class StudentPosGraduation extends Student {
study(): void {
console.log(`${this.name} is studying and searching`);
}
}
現在我們有更好的方法來分離他們各自的職責。這個原理的名字可能很可怕,但它的原理很簡單。
定義: Derived classes (or child classes) must be able to replace their base classes (or parent classes)
要理解這個原理,訣竅是記住定義。不應強迫類別實作不會使用的方法。
因此,假設您有一個類別實作了一個從未使用過的介面。
讓我們想像一下某個商店的賣家和接待員的場景。賣家和接待員都有薪水,但只有賣家有佣金。
讓我們看看問題:
interface Employee {
salary(): number;
generateCommission(): void;
}
class Seller implements Employee {
salary(): number {
return 1000;
}
generateCommission(): void {
console.log("Generating Commission");
}
}
class Receptionist implements Employee {
salary(): number {
return 1000;
}
generateCommission(): void {
/** Problem: Receptionist don't have commission */
}
}
兩者都實現了 Employee 接口,但接待員沒有佣金。所以我們被迫實施一種永遠不會被使用的方法。
所以解決方法:
interface Employee {
salary(): number;
}
interface Commissionable {
generateCommission(): void;
}
class Seller implements Employee, Commissionable {
salary(): number {
return 1000;
}
generateCommission(): void {
console.log("Generating Commission");
}
}
class Receptionist implements Employee {
salary(): number {
return 1000;
}
}
輕鬆輕鬆!現在我們有兩個介面了!雇主類別和佣金介面。現在只有賣方將實現將獲得佣金的兩個介面。接待員不僅負責員工的工作。因此,接待員不必被迫實施永遠不會使用的方法。
定義: A class should not be forced to implement interfaces and methods that will not be used.
最後一個!光看名字就會覺得很難記!但也許你每次都已經看到這個原則了。
想像一下,您有一個 Service 類,它與一個將呼叫資料庫的 Repository 類別集成,例如 Postgress。但是,例如,如果 MongoDB 的儲存庫類別發生變化並且資料庫發生變化。
讓我們來看看例子:
interface Order {
id: number;
name: string;
}
class OrderRepository {
constructor() {}
saveOrder(order: Order) {}
}
class OrderService {
private orderRepository: OrderRepository;
constructor() {
this.orderRepository = new OrderRepository();
}
processOrder(order: Order) {
this.orderRepository.saveOrder(order);
}
}
我們注意到,儲存庫是 OrderService 類,直接耦合到 OrderRepository 類別的具體實作。
讓我們重構一下這個例子:
interface Order {
id: number;
name: string;
}
class OrderRepository {
constructor() {}
saveOrder(order: Order) {}
}
class OrderService {
private orderRepository: OrderRepository;
constructor(repository: OrderRepository) {
this.orderRepository = repository;
}
processOrder(order: Order) {
this.orderRepository.saveOrder(order);
}
}
好的!好多了!現在我們接收儲存庫作為建構函數的參數來實例化和使用。現在我們依賴抽象,我們不需要知道我們正在使用哪個儲存庫。
定義: depend on abstractions rather than concrete implementations
那你現在感覺怎麼樣?我希望透過這個簡單的範例,您可以記住並理解在程式碼中使用這些原則的內容和原因。除了應用乾淨的程式碼之外,它還更容易理解和擴展。
我希望你喜歡!
非常感謝您,祝您永遠健康!
聯絡方式:
領英:https://www.linkedin.com/in/kevin-uehara/
Instagram:https://www.instagram.com/uehara\_kevin/
推特:https://twitter.com/ueharaDev
Github:https://github.com/kevinuehara
dev.to:https://dev.to/kevin-uehara
Youtube:https://www.youtube.com/@ueharakevin/
原文出處:https://dev.to/kevin-uehara/solid-the-simple-way-to-understand-47im