Node.js中常用的設計模式有哪些?

2023-10-19 12:03:18

本文由葡萄城技術團隊首發。轉載請註明出處:葡萄城官網,葡萄城為開發者提供專業的開發工具、解決方案和服務,賦能開發者。

設計模式簡介

設計模式是由經驗豐富的程式設計師在日積月累中抽象出的用以解決通用問題的可複用解決方案,它提供了標準化的程式碼設計方案提升開發體驗。Node.js 作為一款用來構建可延伸高效能應用的流行平臺,自然也遵循設計模式解決通用問題。本文中,我們將討論 Node.js 中設計模式的重要性並提供一些程式碼範例。

構建 Node.js 應用為何需要設計模式

設計模式為軟體開發提供了一套標準化的解決方案。構建 Node.js 應用時,善用設計模式能夠幫助開發者提升程式碼質量,節約開發時間,減少出錯機率。同時也方便開發人員之間的溝通交流。

範例程式碼

單例模式

該模式用來保證特定的類在整個應用中只能建立唯一範例。Node.js 中,單例模式可以保證在同一個應用中,每個模組只有唯一範例。

class Singleton {
  constructor() {
    if (Singleton.instance) {
      return Singleton.instance;
    }
    Singleton.instance = this;
  }

  // Your code here
}

module.exports = Singleton;

工廠模式

工廠模式用來在不暴露實現邏輯的情況下建立物件。在 Node.js 中,使用工廠模式可以根據使用者輸入建立不同型別的範例。

class Car {
  constructor(name) {
    this.name = name;
  }
  drive() {
    console.log(`Driving ${this.name}`);
  }
}

class CarFactory {
  static create(name) {
    return new Car(name);
  }
}

const car1 = CarFactory.create("BMW");
const car2 = CarFactory.create("Audi");

car1.drive(); // Driving BMW
car2.drive(); // Driving Audi

觀察者模式

觀察者模式通過維護一個被觀察物件列表,實現當物件發生改變時發出通知。在 Node.js中,該設計模式用來管理事件和回撥。

class EventObserver {
  constructor() {
    this.observers = [];
  }

  subscribe(fn) {
    this.observers.push(fn);
  }

  unsubscribe(fn) {
    this.observers = this.observers.filter(subscriber => subscriber !== fn);
  }

  notify(data) {
    this.observers.forEach(observer => observer(data));
  }
}

const eventObserver = new EventObserver();

eventObserver.subscribe(data => console.log(`Subscribed to ${data}`));
eventObserver.notify("some data");

依賴注入模式

在本案例中,定義了一個依賴database 物件的UserService 類。通過將 database 傳給 UserService 的建構函式,實現在不修改 UserService 的前提下操作不同資料庫物件。

class UserService {
  constructor(database) {
    this.database = database;
  }

  getUser(id) {
    return this.database.query(`SELECT * FROM users WHERE id = ${id}`);
  }
}

module.exports = UserService;

Promise 模式

在本案例中,通過 fs.promises 模組非同步讀取檔案。readFile 函數返回一個 promise 物件,該 promise物件成功時可以通過 then 方法獲取檔案內容,失敗時可以通過 catch 方法獲取錯誤資訊。

const fs = require('fs').promises;

function readFile(filePath) {
  return fs.readFile(filePath, 'utf8');
}

readFile('example.txt')
  .then(data => {
    console.log(data);
  })
  .catch(error => {
    console.error(error);
  });

Node.js 內建模組中的設計模式

預設情況下,Node.js 本身在其功能中不依賴任何特定的設計模式,但它提供了遵循常見設計模式的內建模組。Node.js 中一些常用的設計模式包括:

模組模式

Node.js 預設使用模組模式將程式碼組織成可複用、可維護的模組。在 Node.js 中,每個檔案都被視為一個模組,開發人員可以使用 require 和 module.exports 語句在檔案之間匯出或匯入程式碼。

const fs = require('fs');

// 非同步讀取檔案
fs.readFile('file.txt', 'utf8', (err, data) => {
  if (err) throw err;
  console.log(data);
});

// 同步讀取檔案
const data = fs.readFileSync('file.txt', 'utf8');
console.log(data);

// 寫入檔案
fs.writeFile('file.txt', 'Hello, World!', (err) => {
  if (err) throw err;
  console.log('檔案已寫入');
});

事件驅動模式

Node.js 使用事件驅動模式來處理 I/O 操作,如向檔案或網路通訊端讀取和寫入資料。事件驅動模式基於觀察者模式,允許開發人員建立事件發射器,以便在某些事件發生時通知偵聽器。

// 模組定義
const myModule = (function () {
  // 私有成員
  const privateVar = 'Hello, World!';

  function privateMethod() {
    console.log(privateVar);
  }

  // 公有成員
  return {
    publicMethod: function () {
      privateMethod();
    },
    publicVar: 'I am public'
  };
})();

// 使用模組
myModule.publicMethod();  // 輸出 'Hello, World!'
console.log(myModule.publicVar); // 輸出 'I am public'

回撥模式

Node.js 使用回撥模式來處理非同步操作,如讀寫檔案或網路請求。回撥模式基於觀察者模式,允許開發人員將函數作為引數傳遞,以便在操作完成時執行。

function fetchData(callback) {
  // 模擬非同步資料獲取
  setTimeout(() => {
    const data = 'Hello, World!';
    callback(null, data); // 第一個引數為錯誤物件,第二個引數為返回的資料
  }, 2000);
}

function processData(err, data) {
  if (err) {
    console.error('出錯了:', err);
    return;
  }
  console.log('處理資料:', data);
}

fetchData(processData);

中介軟體模式

中介軟體是 Express.js 等 Node.js 框架中常用的設計模式。中介軟體函數是在管道中執行的函數,其中每個函數都可以在將請求或響應物件傳遞到下一個函數之前修改它們。中介軟體可用於身份驗證、紀錄檔記錄、錯誤處理等任務。

// 中介軟體函數1
function middleware1(req, res, next) {
  console.log('執行中介軟體1');
  // 在這裡可以對 req 和 res 進行處理
  next(); // 呼叫 next() 將控制權傳遞給下一個中介軟體
}

// 中介軟體函數2
function middleware2(req, res, next) {
  console.log('執行中介軟體2');
  // 在這裡可以對 req 和 res 進行處理
  next(); // 呼叫 next() 將控制權傳遞給下一個中介軟體
}

// 最終處理常式
function finalHandler(req, res) {
  console.log('執行最終處理常式');
  // 在這裡進行最終的請求處理和響應
  res.end('Hello, World!');
}

// 使用中介軟體
function handleRequest(req, res) {
  middleware1(req, res, () => {
    middleware2(req, res, () => {
      finalHandler(req, res);
    });
  });
}

// 建立伺服器並處理請求
const http = require('http');
const server = http.createServer(handleRequest);

server.listen(3000, 'localhost', () => {
  console.log('伺服器已啟動');
});

依賴注入模式

依賴注入(DI)模式是一種用於管理物件之間依賴關係的設計模式。在 Node.js 中,DI 可用於將依賴項注入到模組中,使它們更加模組化和可重用。DI 可以使用建構函式注入、屬性注入或方法注入等技術來實現。

// 使用者服務模組
class UserService {
  constructor() {
    this.users = [];
  }

  addUser(user) {
    this.users.push(user);
  }

  getUsers() {
    return this.users;
  }
}

// 使用者控制器模組(依賴於使用者服務模組)
class UserController {
  constructor(userService) {
    this.userService = userService;
  }

  addUser(user) {
    this.userService.addUser(user);
  }

  getUsers() {
    return this.userService.getUsers();
  }
}

// 使用依賴注入建立使用者控制器範例
const userService = new UserService();
const userController = new UserController(userService);

// 在使用者控制器中新增使用者並獲取使用者列表
userController.addUser('John');
userController.addUser('Mary');
console.log(userController.getUsers()); // 輸出:['John', 'Mary']

Promise模式

Promise模式是一種設計模式,用於以更結構化和類似同步的方式處理非同步操作。Promise 是表示非同步操作最終完成或失敗的物件,允許開發人員通過將非同步操作連線在一起來編寫更具可讀性和可維護性的程式碼。

// 使用 Promise 封裝非同步函數
function getUserById(id) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const user = { id, name: 'John' };
      resolve(user);
    }, 1000);
  });
}

// 呼叫非同步函數並使用 Promise 鏈式呼叫處理結果
getUserById(1)
  .then(user => {
    console.log(user);
    return getUserById(2);
  })
  .then(user => {
    console.log(user);
  })
  .catch(err => {
    console.error(err);
  });

總結

設計模式提供了一種結構化方法來解決 Node.js 中的常見程式設計問題。它們幫助開發人員編寫更好、可維護和可延伸的程式碼。設計模式還為開發人員之間的交流提供了「標準詞彙」。設計模式對於使用 Node.js 編寫高質量程式碼至關重要,如果您想了解更多關於前端表格控制元件的知識,歡迎點選這裡

擴充套件連結:

高階SQL分析函數-如何用視窗函數進行排名計算

3D模型+BI分析,打造全新的互動式3D視覺化大屏開發方案

React + Springboot + Quartz,從0實現Excel報表自動化