模式實踐之工作單元【翻譯】

? ? ? 在2009年4月硝训,我在MSDN發(fā)表了(“持久化模式”)文章,描述了一些當(dāng)你使用ORM技術(shù)持久化業(yè)務(wù)對象時將會碰到的一些基本模式。我認(rèn)為你和你的團隊可能不會根據(jù)提綱實現(xiàn)自己的ORM工具窖梁,但是這些模式對于高效的使用(或者僅僅是選擇)已存在的工具很重要赘风。

? ? ? 在這篇文章中,我想用工作單元(Unit of Work)繼續(xù)討論持久化模式和審視圍繞隱式持久化的問題纵刘。貫穿整篇文章的大部分邀窃,我使用一個泛化的發(fā)票系統(tǒng)作為問題域的例子。

工作單元(Unit of Work)模式

? ? ? 在企業(yè)軟件設(shè)計中最常用的一個設(shè)計模式是工作單元假哎。按照Martin Fowler的說法瞬捕,工作單元模式是“維護一個被業(yè)務(wù)事務(wù)影響的對象列表,協(xié)調(diào)變化的寫入和并發(fā)問題的解決舵抹。

? ? ? 工作單元模式并不需要你自己明確地構(gòu)建肪虎,但是我注意到幾乎每一個持久化工具中它都出現(xiàn)過。NHibernate中的ITransaction接口惧蛹,LINQ to SQL中的DataContext類扇救,還有Entity Framework中的ObjectContext類都是工作單元模式的例子。正是因為這個香嗓,古老的DataSet(venerable dataset)可以作為一個工作單元使用迅腔。

? ? ? 有時,你可能想根據(jù)使用的持久化工具寫自己特定應(yīng)用的工作單元接口或者類用于包裝工作單元的內(nèi)部邏輯靠娱〔琢遥可能有很多原因這樣做。你可能想為事物管理添加特定應(yīng)用的日志像云,跟蹤锌雀,或者錯誤處理∩环眩可能你想根據(jù)應(yīng)用的剩余部分封裝你的持久化工具的特定部分汤锨,想用這個額外的封裝使以后替換持久化技術(shù)更加容易“倏颍或者你想促進你的系統(tǒng)的可測試程度闲礼。很多常用持久化工具的內(nèi)部工作單元實現(xiàn)方式在自動化單元測試場景下很難處理。

? ? ? 如果你構(gòu)建過你自己的工作單元實現(xiàn)铐维,它可能與下面的接口相似:

public interface IUnitOfWork

{

void MarkDirty(object entity);

void MarkNew(object entity);

void MarkDeleted(object entity);

void Commit();

void Rollback();

}

? ? ? 你的工作單元類有一些方法能夠標(biāo)記對象是改變的柬泽,新的,或是刪除的嫁蛇。(在很多實現(xiàn)中锨并,MarkDirty方法不是必須的因為工作單元自身一些自動決定哪些實體被改變的方法。)工作單元也有一些方法用于提交或是回滾所有的改變睬棚。

? ? ? 在某種程度上第煮,你可以認(rèn)為工作單元是一個存放所有管理事務(wù)代碼的地方解幼。工作單元的職責(zé)有:

? ? ? *管理事務(wù)。

? ? ? *命令數(shù)據(jù)庫插入包警,刪除和更新撵摆。

? ? ? *組織重復(fù)更新。在工作單元對象的單個用法中害晦,代碼的不同部分可能標(biāo)記相同的發(fā)票 ? 為已改變狀態(tài)特铝,但是工作單元類會只發(fā)送一個更新命令到數(shù)據(jù)庫。

? ? ? ?使用工作單元模式的價值在于其它代碼不需要關(guān)心這些內(nèi)容壹瘟,因此你可以全神貫注業(yè)務(wù)邏輯鲫剿。

使用工作單元

? ? ? 使用工作單元的一個最好方式是允許完全不同的類和服務(wù)參與到單一的邏輯事務(wù)中。這里的關(guān)鍵點是你要使完全不同的類和服務(wù)仍然彼此不感知稻轨,同時每一個在單個事務(wù)中有支持事務(wù)的能力灵莲。對于傳統(tǒng)方式,你可能使用過事務(wù)協(xié)調(diào)器例如MTS/COM+澄者,或者System.Transaction命名空間笆呆。就我個人而言,我更喜歡使用工作單元模式使不相關(guān)的類和服務(wù)參與到一個邏輯事務(wù)中粱挡,因為我認(rèn)為這樣使代碼更清晰,更容易理解俄精,并且更容易單元測試询筏。

? ? ? 讓我們假設(shè)你的新的發(fā)票系統(tǒng)在發(fā)票的生命周期里面的任何時間都會在已存在的發(fā)票上面執(zhí)行各種離散的動作。業(yè)務(wù)也相當(dāng)頻繁的改變這些動作竖慧,因此你要頻繁地添加或者刪除新的發(fā)票動作嫌套,所以我們可以應(yīng)用命令模式(”Simply Distributed System Design Using the Command Pattern, MSMQ, and .NET”)創(chuàng)建一個IInvoiceCommand的接口,它描述了一個作用于發(fā)票上的動作:

public interface IInvoiceCommand

{

void Execute(Invoice invoice, IUnitOfWork unitOfWork);

}

? ? ? IInvoiceCommand接口有一個簡單的Execute方法圾旨,它使用Invoice和IUnitOfWork執(zhí)行不同類型的動作踱讨。任何實現(xiàn)IInvoiceCommand的對象都應(yīng)該使用IUnitOfWork參數(shù)在在這個邏輯事務(wù)中持久化任何變化到數(shù)據(jù)庫中。

? ? ? 足夠簡單砍的,但是命令模式加工作單元模式并不會獲得好處直到你把多個IInvoiceCommand對象放在一起(看圖1)

圖1 使用IInvoiceCommand

public class InvoiceCommandProcessor

{

private readonly IInvoiceCommand[] _commands;

private readonly IUnitOfWorkFactory _unitOfWorkFactory;

public InvoiceCommandProcessor(IInvoiceCommand[] commands, IUnitOfWorkFactory unitOfWorkFactory)

{

_commands = commands;

?_unitOfWorkFactory = unitOfWorkFactory;

}

public void RunCommands(Invoice invoice)

{

IUnitOfWork unitOfWork = _unitOfWorkFactory.StartNew();

try

{

// Each command will potentially add new objects

// to the Unit of Work for insert, update, or delete

foreach (IInvoiceCommand command in _commands)

{

command.Execute(invoice, unitOfWork);

}

unitOfWork.Commit();

}

catch (Exception)

{

unitOfWork.Rollback();

}

}

}

? ? ? 通過工作單元的這種方法痹筛,你可以愉快地在你的發(fā)票系統(tǒng)中通過添加和刪除業(yè)務(wù)規(guī)則混合和組合不同的IIncoiceCommand的實現(xiàn),同時任然維持事務(wù)的完整性廓鞠。

? ? ? 在我的經(jīng)驗中帚稠,業(yè)務(wù)人員似乎更關(guān)心晚的,未支付的發(fā)票床佳,你可能將要必須添加新的IInvoiceCommand類滋早,在一個發(fā)票被認(rèn)為晚了的時候通過公司的代理商。下面是這個規(guī)則的可能實現(xiàn)方法:

public class LateInvoiceAlertCommand : IInvoiceCommand

{

public void Execute(Invoice invoice, IUnitOfWork unitOfWork)

{

bool isLate = isTheInvoiceLate(invoice);

if (!isLate) return;

AgentAlert alert = createLateAlertFor(invoice);?

?unitOfWork.MarkNew(alert);

}

}

? ? ? 這個設(shè)計的優(yōu)美之處是LateInvoiceAlertCommand完全可以不依賴數(shù)據(jù)庫進行開發(fā)和測試砌们,甚至不依賴同一個事務(wù)中的其它IInvoiceCommand對象杆麸。首先搁进,為了測試使用IUnitOfWork的IInvoiceCommand對象的交互,我可以創(chuàng)建一個IUnitOfWork的嚴(yán)格地偽實現(xiàn)保證測試的精確性昔头,我可以調(diào)用StubUnitOfWork拷获,一個記錄stub。

public class StubUnitOfWork : IUnitOfWork

{

public bool WasCommitted;

public bool WasRolledback;

public void MarkDirty(object entity)

{

throw new System.NotImplementedException();

}

public ArrayList NewObjects = new ArrayList();

public void MarkNew(object entity)

{

NewObjects.Add(entity);

}

}

? ? ?現(xiàn)在你得到一個獨立于數(shù)據(jù)庫可以運行的工作單元的偽實現(xiàn)减细,LaterInvoiceAlertCommand的測試設(shè)置可能類似于圖2的代碼:

圖2 LaterInvoiceAlertCommand的測試固定設(shè)置(Test Fixture)

[TestFixture]?

public class when_creating_an_alert_for_an_invoice_that_is_more_than_45_days_old?

{

?private StubUnitOfWork theUnitOfWork;?

private Invoice theLateInvoice;

?[SetUp]?

public void SetUp()?

{??

?// We're going to test against a "Fake" IUnitOfWork that?

?// just records what is done to it??

theUnitOfWork = new StubUnitOfWork();?

?// If we have an Invoice that is older than 45 days and NOT completed theLateInvoice = new Invoice?

{?

InvoiceDate = DateTime.Today.AddDays(-50),? Completed = false

};?

// Exercise the LateInvoiceAlertCommand against the test Invoice new LateInvoiceAlertCommand().Execute(theLateInvoice, theUnitOfWork);?

}??

[Test]

public void the_command_should_create_a_new_AgentAlert_with_the_UnitOfWork()?

?{?

// just verify that there is a new AgentAlert object?

// registered with the Unit of Work? ? theUnitOfWork.NewObjects[0].ShouldBeOfType();?

}?

[Test]?

public void the_new_AgentAlert_should_have_XXXXXXXXXXXXX()?

{?

var alert = theUnitOfWork.NewObjects[0].ShouldBeOfType();

// verify the actual properties of the new AgentAlert object

// for correctness

}

}

原文鏈接:Patterns in Practice - The Unit Of Work Pattern And Persistence Ignorance

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末匆瓜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子未蝌,更是在濱河造成了極大的恐慌驮吱,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件萧吠,死亡現(xiàn)場離奇詭異左冬,居然都是意外死亡,警方通過查閱死者的電腦和手機纸型,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門拇砰,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人狰腌,你說我怎么就攤上這事除破。” “怎么了琼腔?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵瑰枫,是天一觀的道長。 經(jīng)常有香客問我丹莲,道長光坝,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任甥材,我火速辦了婚禮盯另,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘洲赵。我一直安慰自己鸳惯,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布板鬓。 她就那樣靜靜地躺著悲敷,像睡著了一般。 火紅的嫁衣襯著肌膚如雪俭令。 梳的紋絲不亂的頭發(fā)上后德,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天,我揣著相機與錄音抄腔,去河邊找鬼瓢湃。 笑死理张,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的绵患。 我是一名探鬼主播雾叭,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼落蝙!你這毒婦竟也來了织狐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤筏勒,失蹤者是張志新(化名)和其女友劉穎移迫,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體管行,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡厨埋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了捐顷。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荡陷。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖迅涮,靈堂內(nèi)的尸體忽然破棺而出废赞,到底是詐尸還是另有隱情,我是刑警寧澤逗柴,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布蛹头,位于F島的核電站,受9級特大地震影響戏溺,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜屠尊,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一旷祸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧讼昆,春花似錦托享、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽既峡。三九已至羡榴,卻和暖如春运敢,著一層夾襖步出監(jiān)牢的瞬間校仑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工迄沫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留稻扬,地道東北人。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓羊瘩,卻偏偏與公主長得像,于是被迫代替她去往敵國和親尘吗。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法摇予,類相關(guān)的語法汽绢,內(nèi)部類的語法,繼承相關(guān)的語法侧戴,異常的語法宁昭,線程的語...
    子非魚_t_閱讀 31,623評論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理酗宋,服務(wù)發(fā)現(xiàn)积仗,斷路器蜕猫,智...
    卡卡羅2017閱讀 134,651評論 18 139
  • 小編費力收集:給你想要的面試集合 1.C++或Java中的異常處理機制的簡單原理和應(yīng)用。 當(dāng)JAVA程序違反了JA...
    八爺君閱讀 4,587評論 1 114
  • 一. Java基礎(chǔ)部分.................................................
    wy_sure閱讀 3,810評論 0 11
  • 搞不懂D字開頭和G字開頭的列車有什么區(qū)別隆圆,明明內(nèi)部設(shè)施差不多翔烁,明明還是很多人渺氧,明明還是有塞著耳機托著腮幫蹬屹,傻傻看向...
    Daisy_vw閱讀 312評論 0 1