MVC 模式有效分離應用程式關注點,使開發和維護更加便捷。模型負責資料和業務邏輯,檢視負責呈現資料,控制器則協調兩者互動。這種分離使得修改或擴充套件應用程式更加容易,例如新增檢視只需新增對應控制器即可。微服務架構則更進一步,將應用程式拆分為更小的、獨立佈署的服務,提升系統彈性和可擴充套件性。每個微服務專注於單一功能,並透過 API 進行通訊,例如使用 gRPC 實作支付服務,可以獨立佈署和擴充套件支付功能,而不會影響其他服務。

MVC 設計模式:軟體架構的關鍵

MVC(Model-View-Controller)設計模式是軟體開發中一種廣泛使用的架構模式,它透過將應用程式分為三個主要元件來實作鬆耦合和關注點分離。MVC 模式不僅在傳統的桌面應用程式中發揮作用,在現代的 Web 開發框架中也佔有重要地位。

MVC 模式的核心元件

  1. 模型(Model):模型是 MVC 模式的核心,代表了應用程式的知識和業務邏輯。它包含了資料、狀態和規則的管理,是整個應用程式的骨架。模型負責處理資料的驗證、狀態變更等業務邏輯,並且獨立於使用者介面。

  2. 檢視(View):檢視是模型的視覺表示,用於向使用者展示資料。它可以是圖形使用者介面(GUI)、文字輸出、PDF 檔案、圖表等各種形式。檢視的主要職責是顯示資料,而不處理資料。

  3. 控制器(Controller):控制器是模型和檢視之間的橋樑,負責處理使用者的輸入並更新模型和檢視。當使用者與檢視互動時,檢視會通知控制器,控制器再與模型互動,並根據模型的回饋更新檢視。

MVC 模式的工作流程

一個典型的 MVC 應用程式工作流程如下:

  1. 使用者透過點選按鈕等方式觸發檢視的事件。
  2. 檢視將使用者的動作通知給控制器。
  3. 控制器處理使用者的輸入,並與模型互動。
  4. 模型執行必要的驗證和狀態變更,並通知控制器下一步該怎麼做。
  5. 控制器指示檢視更新顯示,以反映模型的變更。

為什麼需要控制器?

有人可能會問,為什麼不能直接跳過控制器,讓模型和檢視直接互動?雖然理論上可行,但這樣做會喪失 MVC 的一個重要優勢:能夠在不修改模型的情況下使用多個檢視。為了實作模型和檢視之間的解耦,通常每個檢視都需要自己的控制器。如果模型直接與特定的檢視通訊,將很難實作多個檢視的靈活切換。

實務範例

MVC 模式在現實生活中有很多對應的例子。例如,在建造新房子的過程中,不同的專業人員負責不同的工作,如安裝水電、油漆裝潢等。在餐廳中,服務員接收訂單並將餐點呈給顧客,而廚師則負責烹飪食物。

在 Web 開發領域,許多框架採用了 MVC 模式或其變體。例如,Web2py 和 Django 等 Python 框架都使用了 MVC 的概念。Django 中將控制器稱為「view」,而將檢視稱為「template」,形成了 Model-View-Template(MVT)的架構。

MVC 模式的優點

  1. 關注點分離:MVC 將應用程式的不同關注點分離開來,使得圖形設計師可以專注於使用者介面的設計,而開發人員則可以專注於業務邏輯的實作,兩者互不幹擾。
  2. 鬆耦合:由於模型和檢視之間的耦合度很低,因此可以獨立地修改或擴充套件每個部分,而不會影響到其他部分。例如,新增一個新的檢視非常簡單,只需為其實作一個新的控制器即可。
  3. 易於維護:由於每個元件的職責明確,因此維護工作變得更加容易。

從零實作 MVC

在從零實作 MVC 時,應確保建立出「聰明」的模型、「瘦身」的控制器和「笨拙」的檢視。

  • 聰明的模型
    • 包含所有的驗證/業務規則/邏輯
    • 處理應用程式的狀態
    • 存取應用程式資料(資料函式庫、雲端等)
    • 不依賴於使用者介面
  • 瘦身的控制器
    • 當使用者與檢視互動時更新模型
    • 當模型變更時更新檢視
    • 在必要時處理資料並傳遞給模型/檢視
    • 不顯示資料

總之,MVC 模式透過將應用程式分解為模型、檢視和控制器三個元件,實作了關注點分離和鬆耦合,從而提高了軟體的可維護性和可擴充套件性。無論是在傳統的桌面應用程式還是在現代的 Web 開發框架中,MVC 都是一種非常有價值的設計模式。

MVC 設計模式詳解與實作

MVC(Model-View-Controller)是一種廣泛使用的軟體架構模式,主要用於將應用程式的邏輯、資料和呈現層分離,以提高程式碼的可維護性和可擴充套件性。

MVC 的基本組成

  1. Model(模型):負責管理應用程式的資料和業務邏輯。它直接與資料函式庫或其他資料來源互動,並對資料進行處理。
  2. View(檢視):負責向使用者展示資料。它接收來自控制器的資料,並將其呈現給使用者。
  3. Controller(控制器):負責協調模型和檢視之間的互動。它接收使用者的輸入,呼叫模型進行資料處理,並將結果傳遞給檢視進行展示。

MVC 的特點

  • 分離關注點:MVC 將應用程式的邏輯、資料和呈現層分離,使得各個部分可以獨立開發和維護。
  • 提高可維護性:由於各個部分的分離,修改或替換其中一個部分不會影響到其他部分,從而提高了程式碼的可維護性。
  • 提高可擴充套件性:MVC 使得新增或修改功能變得更加容易,因為新的功能可以透過新增或修改個別的部分來實作,而不需要對整個應用程式進行大幅度的修改。

實作 MVC 模式

以下是一個簡單的例子,展示如何從零開始實作 MVC 模式。這個例子是一個名為「Quote Printer」的應用程式,使用者可以輸入一個數字,然後看到與該數字相關的名言。

資料定義

quotes = (
    "A man is not complete until he is married. Then he is finished.",
    "As I said before, I never repeat myself.",
    "Behind a successful man is an exhausted woman.",
    "Black holes really suck...",
    "Facts are stubborn things.",
)

模型(Model)

class QuoteModel:
    def get_quote(self, n):
        try:
            value = quotes[n]
        except IndexError as err:
            value = "Not found!"
        return value

檢視(View)

class QuoteTerminalView:
    def show(self, quote):
        print(f'And the quote is: "{quote}"')

    def error(self, msg):
        print(f"Error: {msg}")

    def select_quote(self):
        return input("Which quote number would you like to see? ")

控制器(Controller)

class QuoteTerminalController:
    def __init__(self):
        self.model = QuoteModel()
        self.view = QuoteTerminalView()

    def run(self):
        valid_input = False
        while not valid_input:
            try:
                n = self.view.select_quote()
                n = int(n)
                valid_input = True
            except ValueError as err:
                self.view.error(f"Incorrect index '{n}'")
        quote = self.model.get_quote(n)
        self.view.show(quote)

主函式(Main)

def main():
    controller = QuoteTerminalController()
    while True:
        controller.run()

if __name__ == "__main__":
    main()

微服務架構模式

微服務架構是一種將應用程式設計為一組鬆散耦合、協作的服務的架構風格。每個服務都是獨立佈署的,並透過明確定義的 API 進行通訊。

微服務的特點

  • 鬆散耦合:每個服務都是獨立的,可以獨立開發、佈署和擴充套件。
  • 獨立佈署:每個服務都可以獨立佈署,不需要影響其他服務。
  • API 通訊:服務之間透過明確定義的 API 進行通訊。

實務範例

許多知名公司,如 Netflix、Uber 和 Amazon,都採用了微服務架構來處理複雜的業務需求和大規模的使用者請求。

微服務架構模式的應用場景與實作

微服務架構模式提供了多種聰明的解決方案,適用於具有特定特徵的應用程式開發。以下列舉了一些微服務架構模式的典型應用場景:

  • 需要支援多種客戶端,包括桌面和行動裝置
  • 需要為第三方提供 API 介面
  • 需要透過訊息傳遞與其他應用程式進行通訊
  • 需要存取資料函式庫、與其他系統進行通訊,並傳回適當的回應格式(JSON、XML、HTML 或 PDF)
  • 應用程式具有對應不同功能區域的邏輯元件

使用 gRPC 實作微服務模式:支付服務

在微服務的世界中,從佈署單一應用程式轉變為佈署多個小型服務,意味著需要處理的事務數量呈指數級增長。 Docker 的出現使得事情變得更加簡單,因為我們可以將服務封裝成容器,然後執行這些容器,並確保它們之間可以相互通訊。

定義服務及其方法

首先,我們使用 Protocol Buffers(protobuf)定義服務及其方法,在 ch06/microservices/grpc/payment.proto 檔案中定義了 PaymentService 服務及其方法:

syntax = "proto3";
package payment;

service PaymentService {
  rpc ProcessPayment(PaymentRequest) returns (PaymentResponse) {}
}

message PaymentRequest {
  string order_id = 1;
  double amount = 2;
  string currency = 3;
  string user_id = 4;
}

message PaymentResponse {
  string payment_id = 1;
  string status = 2; // 例如 "SUCCESS" 或 "FAILED"
}

編譯 protobuf 檔案

接下來,我們需要使用 protobuf 編譯器(protoc)將 payment.proto 檔案編譯成 Python 程式碼。命令如下:

python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. payment.proto

這將生成兩個檔案:payment_pb2.pypayment_pb2_grpc.py

實作支付服務邏輯

然後,我們在 payment_service.py 檔案中提供支付服務的邏輯,擴充套件了由 protobuf 編譯器生成的 Python 程式碼:

from concurrent.futures import ThreadPoolExecutor
import grpc
import payment_pb2
import payment_pb2_grpc

class PaymentServiceImpl(payment_pb2_grpc.PaymentServiceServicer):
    def ProcessPayment(self, request, context):
        return payment_pb2.PaymentResponse(payment_id="12345", status="SUCCESS")

def main():
    print("Payment Processing Service ready!")
    server = grpc.server(ThreadPoolExecutor(max_workers=10))
    payment_pb2_grpc.add_PaymentServiceServicer_to_server(PaymentServiceImpl(), server)
    server.add_insecure_port("[::]:50051")
    server.start()
    server.wait_for_termination()

編寫測試客戶端

最後,我們編寫一個測試客戶端,以測試支付服務:

import grpc
import payment_pb2
import payment_pb2_grpc

with grpc.insecure_channel("localhost:50051") as chan:
    stub = payment_pb2_grpc.PaymentServiceStub(chan)
    resp = stub.ProcessPayment(payment_pb2.PaymentRequest(order_id="order123", amount=100.0, currency="USD", user_id="user123"))
    print(resp)

程式碼解密:

  1. 定義服務及其方法:使用 protobuf 定義 PaymentService 服務及其 ProcessPayment 方法。
  2. 編譯 protobuf 檔案:使用 protoc 編譯器將 payment.proto 檔案編譯成 Python 程式碼。
  3. 實作支付服務邏輯:在 payment_service.py 檔案中提供支付服務的邏輯,擴充套件了由 protobuf 編譯器生成的 Python 程式碼。
  4. 編寫測試客戶端:編寫一個測試客戶端,以測試支付服務。

此圖示顯示了微服務架構模式的應用場景與實作:

  graph LR;
    A[客戶端] -->|gRPC| B[支付服務];
    B -->|處理支付| C[資料函式庫];
    C -->|傳回結果| B;
    B -->|傳回結果| A;

**圖表翻譯:**此圖表顯示了客戶端透過 gRPC 與支付服務進行通訊,支付服務處理支付請求並存取資料函式庫,最終傳回結果給客戶端。