每個前端開發人員都應該了解的軟件工程原理
作為前端開發人員,我們常常專注于創建漂亮的用戶界面。然而,我們必須牢記,美也在于內在,完美像素的方法也應轉化為我們的代碼組織和結構。在本文中,我們將探討一些基本的軟件工程原則,每個前端開發人員都應該了解并在自己的項目中應用這些原則。
1. DRY(不要重復)
DRY 原則強調代碼可重用性和維護的重要性。通過將通用功能提取到可重用組件、函數或模塊中來避免重復代碼。通過堅持 DRY 原則,您可以減少代碼重復,提高可維護性,并使您的代碼庫更加模塊化。React 鼓勵組件驅動的架構,其中職責被隔離,以便于未來的開發和可擴展性。
讓我們以一個簡單的電子商務應用程序的產品頁面為例。我們希望看到一個待售產品列表。我們可以將頁面分解成更小的、可重復使用的組件。
組件:
- ProductCard:顯示單個產品及其名稱、價格和描述。
- ProductList:顯示產品列表。
// ProductCard.js
import React from 'react';
const ProductCard = ({ product }) => {
return (
<div>
<h2>{product.name}</h2>
<p>Price: ${product.price}</p>
<p>Description: {product.description}</p>
</div>
);
};
export default ProductCard;
// ProductList.js
import React, { useState } from 'react';
import ProductCard from './ProductCard';
const ProductList = () => {
const [products, setProducts] = useState([
{ id: 1, name: 'Product 1', price: 9.99, description: 'Description 1' },
{ id: 2, name: 'Product 2', price: 19.99, description: 'Description 2' },
// ...
]);
return (
<div>
{products.map((product) => (
<ProductCard key={product.id} product={product} />
))}
</div>
);
};
export default ProductList;
在這個示例中,我們可以看到,通過將有關產品的邏輯分離到 ProductCard 組件中,我們可以在 ProductList 組件的 map 功能中重復使用這些邏輯,從而避免為列表頁面中的每個產品項目重復編寫代碼。
2. SOLID 原則
SOLID 是一個縮寫詞,代表面向對象設計的五個關鍵原則:
- 單一職責原則(SRP):每個模塊或類應該只有一個更改的理由。
- 開放/封閉原則(OCP):軟件實體應該對擴展開放,對修改關閉。
- 里氏替換原則(LSP):子類型應該可以替換其基本類型,而不改變程序的正確性。
- 接口隔離原則 (ISP):不應強迫客戶端依賴于他們不使用的接口。
- 依賴倒置原則(DIP):高層模塊不應該依賴于低層模塊。兩者都應該依賴于抽象。
讓我們看一下如何在 React TypeScript 組件中應用里氏替換原則 (LSP):
// Vehicle.ts
interface Vehicle {
drive(): void;
name: string;
}
// Car.ts
class Car implements Vehicle {
constructor(private name: string) {
this.name = name;
}
drive(): void {
console.log(`Driving a ${this.name}`);
}
}
// Motorcycle.ts
class Motorcycle implements Vehicle {
constructor(private name: string) {
this.name = name;
}
drive(): void {
console.log(`Riding a ${this.name}`);
}
}
// App.tsx
import React from 'react';
import { Vehicle } from './Vehicle';
import Car from './Car';
import Motorcycle from './Motorcycle';
function VehicleComponent(props: { vehicle: Vehicle }) {
props.vehicle.drive();
return <div>Driving a {props.vehicle.name}</div>;
}
const App = () => {
const car = new Car();
const motorcycle = new Motorcycle();
return (
<div>
<VehicleComponent vehicle={car} />
<VehicleComponent vehicle={motorcycle} />
</div>
);
};
export default App;
在此示例中,我們有一個定義 name 屬性和 drive 方法的 Vehicle 接口。然后我們有兩個具體的實現:Car 和 Motorcycle ,它們都實現 Vehicle 接口。
在 App 組件中,我們創建 Car 和 Motorcycle 的實例并將它們傳遞給 VehicleComponent。VehicleComponent 在傳入的車輛對象上調用驅動方法。
LSP 確保我們可以用 Car 或 Motorcycle 替換 Vehicle 接口,而不會改變程序的正確性。VehicleComponent 與 Car 和 Motorcycle 實例無縫協作,展示了子類型對其基本類型的可替換性。
3. KISS(保持簡單,笨)
KISS 原則提倡設計和實現的簡單性。編寫易于理解、簡單且能做好一件事的代碼。避免不必要的復雜性和過度設計,因為從長遠來看,這可能會導致混亂和維護挑戰。
讓我們看一下 Counter 組件的 2 個實現。
// Complex Counter
import React, { useState, useEffect } from 'react';
import { debounce } from 'lodash';
const ComplexCounter = () => {
const [count, setCount] = useState(0);
const [clicked, setClicked] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
if (clicked) {
setCount(prev => prev + 1)
setClicked(false)
}
}, [clicked, setClicked]);
const handleClick = (clicked: boolean) => {
setClicked(!clicked);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={() => handleClick(clicked)}>Increment</button>
</div>
);
};
export default ComplexCounter;
// Simple Counter
import React, { useState } from 'react';
const SimpleCounter = () => {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>Increment</button>
</div>
);
};
export default SimpleCounter;
我們看到,ComplexCounter 的實現更難理解和維護,也更容易出錯。
它為 clicked 和 useEffect 鉤子引入了不必要的狀態變量。
這是一個如何不實現 React 組件的示例。
4. YAGNI(你不需要它)
YAGNI 提醒我們避免基于推測的未來需求過早地添加功能。相反,應專注于正確實現當前所需的功能。當您構建一個非常以用戶為中心的產品時,這一點變得非常重要。最好不要根據您認為用戶可能想要的假設來引入新功能。使用適當的用戶研究框架和原型設計方法。
通過遵循 YAGNI,您可以防止不必要的復雜性、減少開發時間并維護精簡的代碼庫。
5. 干凈的代碼
干凈的代碼是可讀的、可理解的、可維護的。遵循編碼約定和最佳實踐,使用有意義的變量名稱,并編寫不言自明的代碼。保持函數和類小而集中,堅持一致的格式,并努力使代碼庫清晰。
讓我們看一個簡單的實用函數,用于出于數據安全目的隱藏部分用戶的私人信息。
const hashUsersPrivateInformation = (privateInformation: string): string => {
// 計算私人信息的長度,以確定需要屏蔽多少個字符
const maxLength = privateInformation.length > 4 ? privateInformation.length - 4 : privateInformation.length;
// 創建正則表達式模式,以匹配所需的字符數
const regexPattern = `.{1,${maxLength}}`;
const regex = new RegExp(regexPattern);
return privateInformation.replace(regex, (match) => '*'.repeat(match.length));
};
我們看到:
- 函數的名稱是自我描述的
- 它包含可以幫助其他開發人員的有用注釋。
- 它有一個可以理解的主要目的。
我們應該以類似的方式構建我們的代碼。
結論
將這些軟件工程原理融入您的前端開發工作流程中,您可以編寫質量更高的代碼,改善與團隊成員的協作,并構建強大且可擴展的應用程序。軟件工程不僅僅是編寫代碼;還涉及編寫代碼。它是為復雜問題創建可靠、可維護且優雅的解決方案。
原文:https://dev.to/gboladetrue/software-engineering-principles-every-frontend-developer-should-know-1ej7?ref=dailydev