共计 3307 个字符,预计需要花费 9 分钟才能阅读完成。
上一篇主要提到 Web Service 相關的概念,但僅有這些概念其實還不構,要撰寫出擁有高效能和淺顯易懂的程式碼是需要良好的架構來輔助,本篇主要論述目前主流提到的概念,若有錯誤不吝指教。
三層架構
User Interface 表現層
主要實現傳統的 MVC 架構,即是所謂的 模型 (Model)、視圖 (View)、控制器 (Controller),如果作為一個 API 基本上不需要使用 View,直接透過 Controller 將資料輸出成 Json 格式即可,要注意的是 Controller 主要負責將使用者輸入的資料引導到指定的「業務邏輯層」而非「資料訪問層」,因此這層理論上不應該出現任何對資料庫存取的動作!
本層在我的理解範圍中應該只是負責流程控管,指定要呼叫的業務,如:使用者資料驗證、資料存取、或者是將資料輸出到 View 亦可輸出 Json 格式。
Business Logical Layer 業務邏輯層
藉由分層將真正需要進行判斷或者是和其他 API 串街的部份抽離 UI 層,本層會呼叫 DAL 層上定義的倉儲庫 (Repository) 進行資料的 增加、刪除、修改、查詢,因為 Controller 所需要的資料可能來自不同的 表單 (Table) 因此我們可能同時對多個倉儲庫做查詢並藉由 BO 物件重新封裝後輸出給 UI 層。
Data Access Layer 資料訪問層
本層主要會定義 PO 物件來對應資料庫中的每個表單,因此亦可將 PO 稱為 實體類 (Entity),另外定義 倉儲庫 (Repository) 用來進行對資料庫的 增加、刪除、修改、查詢,每個倉儲庫都會對應到自己的一個實體類,若實體之間有關連,我們也可以藉由參照 (Join) 的方式取得資料。
分割出資料訪問層主要是便於二次開發,難免會遇上更換資料庫的窘境,雖然實際上的情況幾乎不太會遇到,但維持良好的習慣,將資料查詢和業務邏輯分開也有助於程式的可讀性。
我們可以將專案的開發當作是經營一間餐廳,表現層好比服務員,需要將客人所點的每道菜呈現到餐桌前;業務邏輯層好比廚師,負責依照客戶的需求來製作餐點;資料訪問層好比採購員,負責將廚師所需要的食材進行採購。
為什麼我們要分層?無非就是要讓每個人「各司其職,各安其位」,達到所謂的關注點分離 (Separation of concerns,SOC)
為什麼要分資料夾?藉由特定的規範讓程式更加好閱讀,是建立這架構的主要目的,落實 習慣取代配置 (Convention over Configuration)
物件概念
Persistent Object 持久化物件
通稱 PO,主要用於對應資料庫中的每個表單,因此亦可稱為實體類 (Entity),在 Java 中又可稱為 POJO (Plain Ordinary Java Object),而在 C# 中也有 POCO (Plain Old CLR Object),此外還有常見的 DAO (Data Access Object) 亦可稱資料訪問物件,其實指的東西都是同件事。
資料持久化
資料持久化就是將記憶體中的資料模型轉換為儲存模型, 以及將儲存模型轉換為記憶體中的資料模型的統稱. 資料模型可以是任何資料結構或物件模型, 儲存模型可以是關係模型、XML、二進位制流等。
使用資料持久化有以下好處:
1、程式程式碼重用性強,即使更換資料庫,只需要更改配置檔案,不必重寫程式程式碼。
2、業務邏輯程式碼可讀性強,在程式碼中不會有大量的 SQL 語言,提高程式的可讀性。
3、持久化技術可以自動優化,以減少對資料庫的訪問量,提高程式執行效率。
資料持久化物件的基本操作有:儲存、更新、刪除、查詢等。
Value Object 值物件
通稱 VO,有時候我們透過物件的關聯來取值,這個結果可能是來自多個表單的某些特定欄位,因此藉由此物件來存放,進行和業務邏輯層的傳遞,因此亦可稱為 DTO (Data Transfer Object) 資料傳輸物件。
Business Object 業務物件
通稱 BO,從不同數據邏輯層取回來的物件,透過不同的邏輯規則重新組成的物件,藉此傳遞到表現層。
View Object 圖物件
通稱 VO,不同的 View 可能會需要不同的 VO 來對應 BO 上面的值,最後將值藉由模板工具書輸出到 UI 上面。
我們可以將物件想像成餐廳中的餐點,不同層級代表不同身分,所需負責的東西也不同,好比客人最後拿到的可能是 BO 或者是 VO (View Object) 而廚師拿到的則是 PO 或 VO (Value Obejct),因此會有人將這些物件全部獨立抽出來放到 Model 中方便各層級參考。
這裡稍微補充一般開發過程中我們會將常用的工具放到 Common 當中,或者針對各層級使用的通用工具 (Utility) 簡稱 util 統一調用。
參考
正確理解 DTO、值對象和 POCO
淺談 VO,POJO,JavaBean,DTO
重要概念
為了物件導向的概念,我們會將東西全部寫成接口 (Interface) 給上層進行調用,然後背後在自己實現因此者些實現類會建立一個 Impl 資料夾存放。
Inversion of Control 控制反轉 (IoC)
傳統的程式運作是流程開始後,依照程式碼裡面的需求去創造一個物件或者是執行其他流程,好比呼叫不同的齒論一樣,這樣缺乏彈性,每次當需求改變我們就必須改變程式碼,改呼叫其他齒輪來完成需求。
但遇到大型軟體開發,會講求所謂的低耦合高內聚,也就是每個齒論的開發者專於自己的功能設計,每個功能都是一個模塊能拆開來獨立運作來達到所謂低耦合,高內聚指的是每個功能裡面程式碼的關聯性,不做多餘的呼叫。
為了讓每個齒論設計者都能專注自己的開發,我們設計出一個容器,讓每個功能都在主程式一開始執行的時候就先做初始化,依照不同的需求調用,我們可以從圖中發現箭頭的位置改變了,從原本的主程式呼叫轉變成齒輪主動注入,這就是所謂「控制反轉」,程式的執行權轉到各別功能上,而不是主程式自己進行呼叫。
Aspect Oriented Programming 面向切面編程 (AOP)
透過三層架構將每個功能都拆分到三個不同的專案上面,但某些功能像是日誌模組和驗證模組,這種功能是希望全局能共用而且在維護的時候可以透過單一類或者是專案去進行管理而不用各自將其拆分到不同專案上,這樣的概念就是縱切的,即是所謂的「面向切面編程」。
Dependency Injection 依賴注入 (DI)
我們藉由齒輪來描述程式架構,假設每個層級或者元件都是一塊齒輪,彼此應該是可以拆開獨立運作,不會因為一個齒輪損毀就造成整個程式崩潰,並且可以依照不同的需求使用不同的齒輪。
但程式設計中,傳統方法不論調用一個方法或者是創造一個物件都需要主程式自己進行呼叫,但透過框架我們可以先建立一個容器 (Container) 將這些齒輪在程式建構的時候就先實例化,等到需要的時候在注入使用。
藉由容器將我們所需要的齒輪進行實例化後,還需要透過界面 (Interface) 的幫忙來取得這些功能,這樣的好處是我們只需要呼叫特定的接口,而不須去在意背後的實現,落實前面所說的關注點分離。
即使所謂的依賴注入,依賴即是說明我需要你,注入就是說明調用這件事,合起來就是我需要你來幫忙,大概是這樣的概念。而非我叫你來執行這件事情,這樣差異在於前者是完全讓你執行這件事後者則是我告訴你執行這件事。
打個比方來說,餐廳的例子,控制反轉就是我身為廚師,我交待採購員要買什麼食材即可,至於他要去哪買,我根本不管我只要拿到食材就好,相對顧客來說亦是如此,我只管我的餐點至於他是什麼東西做成的,我不需要管。
但是要落實控制反轉就必須透過界面 (Interface) 來達成,統一呼叫接口,每層接口定義所需要的參數後,背後自己再去實現,並且可以獨立針對每個接口進行測試,彼此不會互相干擾,達到低耦合高內聚。
延伸閱讀
控制反轉 (IoC) 與 依賴注入 (DI)
依賴倒置原則 (Dependency-Inversion Principle, DIP)
Object Relation Mapping 對象關係映射 (ORM)
作用是在關聯式資料庫和業務實體物件之間作一個對映,這樣,我們在具體的操作業務物件的時候,就不需要再去和複雜的 SQL 語句打交道,只需簡單的操作物件的屬性和方法。
延伸閱讀