作為前端開發人員,我們通常專注於建立漂亮的使用者介面。然而,重要的是要記住,美麗也在於內部,像素完美的方法也應該轉化為我們的程式碼組織和結構。在本文中,我們將探討每個前端開發人員都應該了解並在其專案中應用的一些基本軟體工程原理。

1. DRY(不要重複)

DRY 原則強調程式碼可重複使用性和維護的重要性。透過將通用功能提取到可重複使用元件、函數或模組中來避免重複程式碼。透過堅持 DRY 原則,您可以減少程式碼重複,提高可維護性,並使您的程式碼庫更加模組化。 React 鼓勵元件驅動的架構,其中職責被隔離,以便於未來的開發和可擴展性。

我們以一個簡單的電子商務應用程式中的產品頁面為例。我們希望看到待售產品清單。我們可以將頁面分解為更小的、可重複使用的元件。

成分:

  1. 產品卡:顯示單一產品及其名稱、價格和描述。

  2. 產品清單:顯示產品清單。

// 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;

在此範例中,我們有一個Vehicle接口,它定義了name屬性和drive方法。然後我們有兩個具體的實作: CarMotorcycle ,它們都實作了Vehicle介面。

在 App 元件中,我們建立CarMotorcycle的實例並將它們傳遞給 VehicleComponent。 VehicleComponent對傳入的車輛物件呼叫驅動方法。

LSP 確保我們可以用CarMotorcycle代替Vehicle接口,而不改變程序的正確性。 VehicleComponent可與CarMotorcycle實例無縫協作,展示子類型對其基本類型的可替換性。

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 => {
  // Calculate the length of the private info to determine how many characters to mask
  const maxLength = privateInformation.length > 4 ? privateInformation.length - 4 : privateInformation.length;
// Create a regular expression pattern to match the desired number of characters
  const regexPattern = `.{1,${maxLength}}`;
  const regex = new RegExp(regexPattern);

  return privateInformation.replace(regex, (match) => '*'.repeat(match.length));
};

我們看到:

  1. 函數的名稱是自我描述的

  2. 它包含可以幫助其他開發人員的有用註釋。

  3. 它有一個可以理解的主要目的。

我們應該以類似的方式建立我們的程式碼。

結論

將這些軟體工程原理融入您的前端開發工作流程中,您可以編寫更高品質的程式碼,改善與團隊成員的協作,並建立強大且可擴展的應用程式。軟體工程不僅僅是編寫程式碼;還涉及編寫程式碼。它是為複雜問題建立可靠、可維護且優雅的解決方案。


原文出處:https://dev.to/gboladetrue/software-engineering-principles-every-frontend-developer-should-know-1ej7


共有 0 則留言