使用擴充套件方法和靜態門面類實現偽領域物件

2021-09-08 14:18:27 字數 4463 閱讀 1760

領域物件,在此特指充血的領域物件模型,在解決什麼是偽領域物件之前,需要事先解釋何為充血的領域物件。在此後的介紹中,假設我們存在物件模型

employee—department。

在物件導向的實體類建模的發展歷史上,有著

2家分歧,其中部分人認為實體類應保證本身的純潔性,只需維護資料,而無需知道資料的**以及資料的查詢方法,這被稱為「貧血」模型,在此模型下,乙個

department

的表示如下

class

department

public

decimal basesalary

} 其中department

只維護了屬於自己的屬性「名稱」和「基礎工資」,而完全不需要知道屬於他的

employee

的存在,對於

employee

和department

的查詢會交給專門的資料訪問類

(dao模式)

如idepartmentdao

,此介面可能表述如下

inte***ce

idepartmentdao

貧血模型的優點是將實體與實體的資料訪問完全解耦,實體可以在系統各層之間穿越,僅保留最基本的屬性,而對實體的資料訪問操作全部交由

dao物件,這樣

dao物件可以由多型性支援,使用各種模式提供靈活性,並且符合物件導向的職責單一原則。

而另一部分人推薦的「充血」模型則認為「部門應當保留有自己的員工資訊」,所以在實體類中應該帶有相應的查詢的方法,從而可以更接近現實世界地對系統進行建模,在充血模型下,

department

的表示可能如下

class

department

public

decimal basesalary

public

ilist

getemployees(); }

在實體類中加入的資料訪問的方法,因此呼叫的體驗有所改進,建模也更接近現實世界。

「充血」模型有著其固有的優勢,但也無法迴避一些問題,最大的問題是實體與資料訪問的雙向迴圈依賴,在沒有框架支援的情況下無法做到透明地將資料訪問的實現注入到實體中,同時迴圈依賴也是設計中的大忌,因此「充血」模型並沒有得到廣泛的應用。

充血模型下的領域物件與普通的實體物件最大的區別在於,物件帶有資料查詢的方法。

但是要使物件可以進行資料的查詢,就需要讓物件依賴於資料層,這不是乙個好的設計。幸而

.net 3.5

框架提供了乙個非常便利的功能,可以動態地為物件「擴充套件」功能,這就是擴充套件方法。

對於擴充套件方法,已經有不少文章作了介紹,簡單來說,擴充套件方法就是靜態方法,通過編譯期的特殊手法將此「作為」物件的例項方法進行呼叫。

由於擴充套件方法是靜態方法,靜態方法本身不支援物件導向中的多型性,無法響應變化,因此在使用擴充套件方法的時候,必須保證方法的呼叫過程沒有變化,不需要由呼叫者顯式地決定呼叫的途徑,比如說以下方法顯然不能作為靜態方法使用

iuserdal

dal = new

sqlserveruserdal();

這一段**顯式地指定了

iuserdal

的實現,因此當我們的資料庫產品從

sql server

更換為oracle

時,由於沒有多型性的支援,就需要顯式地修改此語句,這並不符合物件導向的開閉原則。

對於變化的封裝,可以用工廠模式、反射等方法完成,最終以乙個門面類對外提供粗粒度的介面。門面類往往是乙個層的整體代表,其提供的介面穩定、變化少,在下層完成了較好的封閉的情況下,將門面類作為靜態類使用,我稱之為「靜態門面」。

通過此門面類提供擴充套件方法,即可以將方法「注入」到實體物件中。

在普通的三層架構中,業務層的方法是需要作為靜態擴充套件方法注入到實體物件中的,在這個層次的抽象中,我們先忽略掉資料訪問層來看問題

(訪問層被業務層隱藏了)。

首先我們需要有業務層的介面,在介面的約束之下才可以實現自由的變化。

隨後我們還需要工廠,工廠通過反射等「萬能」方案生產介面的具體實現,封裝變化,這裡必須封裝得很完美,使實現變化的時候**不需要發動,不然靜態方法就完蛋了~

最後我們需要乙個門面類,門面類提供擴充套件方法。

整個系統的結構如下圖所示

其中的businessfactory

這裡為了簡化,可以使用反射簡單工廠,

businessfacade

通過擴充套件方法,將

iuser

、ibook

和iborrow

的方法「注入」到

entity

中。在此以乙個圖書管理系統為例,此系統有三個實體

userinfo

,bookinfo

和borrowinfo

,功能如下

1.使用者的增刪改,通過

id獲取使用者資訊

2.書籍的增刪改,通過

isbn

和作者名獲取書本資訊

3.通過使用者獲取借閱歷史

4.通過書本

isbn

獲取借閱歷史

設計userinfo

、bookinfo

和borrowinfo

三個實體類,實體類的設計依舊按照「貧血」模型設計,不包含任何查詢方法,在此不列出**

~設計三個介面

iuser

,ibook

和iborrow

作為業務層的介面,其**如下

inte***ce

iuser

inte***ce

ibook

inte***ce

iborrow

工廠類**如下,在此偷懶就不寫反射例項化的**了,具體可以通過

petshop

等非常多的示例進行學習,總之工廠要求建立出

iuser

、ibook

和iborrow

的具體實現的物件

static

class

businessfactory

public

static

ibook getbook()

public

static

iborrow getborrow()

} 門面類必須是靜態的,這樣才能進行擴充套件方法的定義。

因為工廠已經封裝了變化,在門面類中就大膽地使用吧,不會再有需要多型性支援的地方了~

在此以部分**為例,**如下

static

class

businessfacade

public

static

void insert(this

bookinfo book)

public

static

void update(this

userinfo user)

public

static

void update(this

bookinfo book)

注意到insert

和update

方法的引數中的

this

關鍵字,這就是擴充套件方法的關鍵所在。

擴充套件方法依舊屬於門面類,只是可以通過例項物件呼叫

在此只以簡單地示例表示客戶端的呼叫過程,因此建立了乙個

假設邏輯是,首先獲取id為

3的使用者資訊,然後查詢此使用者的所有借閱記錄,所以有如下**

此時使用門面類,可以看到門面類中所有的方法都有智慧型提示,擴充套件方法旁邊跟了乙個藍色的箭頭。

隨後通過擴充套件方法,使用實體物件查詢借閱記錄,**如下

可以看到

vs的智慧型提示可以尋找到擴充套件方法,因此使用時的體驗就和「充血」模型一樣啦

~首先,對於真正的「充血」模型,可能會有如下的**

userinfo

user = userinfo.getbyid(3);

但這是無法通過擴充套件方法實現的,因為擴充套件方法只能擴充套件到例項物件,而不能擴充套件為另乙個類的靜態方法……

其次,「充血」模型在設計時就會給

userinfo

加上getborrowhistory

方法,這種對設計的支援在擴充套件方法的情況下不能很好地實現,乙個建議的方法是設計完

business

介面後即刻設計門面類,第一時間給實體物件加上擴充套件方法,這樣至少在單元測試的編寫等方面可以有智慧型提示的支援。

最後,不得不提醒的是,擴充套件方法是靜態方法,在下層的封裝不完善的情況下一定要慎用!

隨便乙個**,裡面只有介面的定義和門面類的應用,對介面沒有實現,工廠類也沒有實現,這個應該不難,呵呵~

C 擴充套件方法和靜態類詳解

1.靜態方法屬於類,而普通方法則屬於物件,因此靜態方法可以用類名.靜態方法 來呼叫,而普通方法則必須用new來例項化後呼叫 2.靜態類中只能有 靜態的方法,屬性和變數 3.普通類中能有普通方法,也能有靜態類。在呼叫裡面的靜態方法時,可以使用普通類名.靜態方法來呼叫,不需要例項化類 擴充套件方法 擴充...

類方法和靜態方法

通過靜態方法和類方法能夠把相關的函式封裝到乙個類裡面,有效的將 組織起來,提高 的可維護性 class date object def init self,year,month,day self.year year self.month month self.day day 普通方法 def ech...

靜態類 靜態欄位和靜態方法

1.靜態類 永遠也不需要例項化的類,這種類唯一的作用就是將一組相關的成員組合到一起。static 關鍵字只能用於類,不能用於結構,因為clr 總是允許值型別例項化,沒辦法阻止 by clr via c 靜態類有如下特點 1 不能實現任何介面 因為只有類的例項才可以呼叫類的介面方法 2 內部只能定義靜...