|
正式討論之前,先看看這兩個(gè)問(wèn)題:當(dāng)我們的對(duì)象所涉及的操作不斷增加時(shí),我們是否應(yīng)該:Book.Save,Book.Serialize,Book.Method1,Book.Method2這樣一直增加下去?或者在某個(gè)垂直的邏輯中增加其它邏輯時(shí),不斷的擴(kuò)充Book.Save,要么象有的朋友說(shuō)的那樣分離出去,再回調(diào)?但是Book.Save有理又有在,無(wú)論數(shù)據(jù)->對(duì)象,還是對(duì)象->數(shù)據(jù),考慮到數(shù)據(jù)和對(duì)象經(jīng)常一起變化,恢復(fù)對(duì)象的狀態(tài)這部分確實(shí)應(yīng)該留在對(duì)象內(nèi)部,同時(shí),我也認(rèn)可這本來(lái)就是對(duì)象的職責(zé)。
為了大家所謂的“低耦高聚”的目標(biāo),也為了保持職責(zé)的合理性,希望大家考慮一下,當(dāng)Book沒(méi)有Save時(shí),我們除了屬性賦值是不是就無(wú)路可走了?這就得那些沒(méi)包含在這次討論中的習(xí)慣性做法(比如平時(shí)對(duì).NETFramework和ADO.NET的使用方式)包含近來(lái),看看很多同志指出的美女走光問(wèn)題,除了給美女一個(gè)電棍讓美女負(fù)擔(dān)起警察打擊偷窺者的職責(zé),能不能通過(guò)換件裙子來(lái)解決。
我們平時(shí)用慣了IDataReader給對(duì)象賦值,所以很多人說(shuō)的那種,從外部通過(guò)屬性賦值的情況就廣泛存在,比如CommunityServer。如果換一個(gè)方式呢? 把IDataReader直接交給Book,然后Book自己展開(kāi)數(shù)據(jù)是不是好很多呢?于是有人又說(shuō)了,這跟IDataReader耦合了,不利于移植等等。或者我根本沒(méi)用ADO,所以沒(méi)IDataReader。后者可以通過(guò)給自己的數(shù)據(jù)操作層實(shí)現(xiàn)IDataReader搞定;關(guān)鍵是前者,前者的非法性還表現(xiàn)在,把ADO的接口帶入了邏輯層;等等等等, 反正很多。那么為什么不能自己做一些類似IDataReader的接口,然后把ADO.NET包含的概念作為變化封裝出去呢?
在保存數(shù)據(jù)的時(shí)候也一樣,不是把數(shù)據(jù)全部讀出來(lái)去保存,而是讓Book準(zhǔn)備好需要保存的數(shù)據(jù),總而言之歸它管的一分不落,然后實(shí)現(xiàn)或者返回出一個(gè)統(tǒng)一的接口里面全都是要持久的數(shù)據(jù)。至于如何跑回書(shū)架上,或者被賊給偷跑了,那是別人的事。畢竟某兄弟回復(fù)說(shuō)的,今天加個(gè)Cache明天加個(gè)Log后天加個(gè)Permission最后數(shù)據(jù)庫(kù)都不用了的情況也確實(shí)有。這些都該Book負(fù)責(zé)嗎?不是說(shuō)只添加不修改嗎?難道非得要求必須AOP? 我是Anders的忠實(shí)Fans,我不認(rèn)為AOP解決了什么本質(zhì)問(wèn)題。怎么又說(shuō)回職責(zé)問(wèn)題了,總而言之,現(xiàn)在總不能有人說(shuō)美女走光了吧。
其實(shí)我們所說(shuō)的方法,往往都有學(xué)習(xí)的對(duì)象,大家可以想想各種Control對(duì)于ViewState的使用,它其實(shí)就是這么一個(gè)玩意(關(guān)于Control如何使用和持久化ViewState的文章,園子里就有)。那么我們也可以這樣,數(shù)據(jù)就是數(shù)據(jù),要什么屬性,而且IDataReader出來(lái)的不就是一砣類似字典的東西嗎?正好直接拿字符串和我們自己定義BL層與數(shù)據(jù)層之間對(duì)接的接口對(duì)應(yīng)(與IDataReader不同的是,我們定義的接口是在邏輯層中使用的,除了象一個(gè)數(shù)據(jù)集,不包含任何和數(shù)據(jù)層相關(guān)的內(nèi)容),覺(jué)得不過(guò)癮,把對(duì)應(yīng)關(guān)系保存到配置文件里去。無(wú)論如何,希望讓對(duì)象自己負(fù)責(zé)恢復(fù)狀態(tài),同時(shí)又不希望對(duì)象負(fù)責(zé)存取邏輯的矛盾,并非無(wú)法解決,ViewState的方式只是其中一種解決方法。
我們不妨再看看.NETFramework如何讓Serializer和你的對(duì)象在沒(méi)有聯(lián)系的情況下,通過(guò)增加一個(gè)翟通道,即不介入對(duì)象內(nèi)部邏輯,又負(fù)擔(dān)了對(duì)象持久的:首先實(shí)現(xiàn)ISerializer這一接口,同時(shí)實(shí)現(xiàn)固定方法簽名的構(gòu)造函數(shù)。保存數(shù)據(jù)時(shí),他讓你自己打包然后從你的接口實(shí)現(xiàn)中獲得數(shù)據(jù)對(duì)象,恢復(fù)對(duì)象時(shí),他調(diào)用你那個(gè)特別的構(gòu)造函數(shù),把數(shù)據(jù)字典還給你,然后讓你自己填充。拿Book來(lái)說(shuō),如果增加了需求,難道非得讓Book自己重新實(shí)現(xiàn)一個(gè)Book.Serialize方法嗎?鑒于一些朋友可能不清楚Serializer的工作機(jī)制,我借用一下MSDN的簡(jiǎn)單例子,同時(shí)有所改動(dòng):
[Serializable]
public class MyObject : ISerializable
{
public int N1 { get; private set;} //公開(kāi)取,私有存
public int N2 { get; internal set;} //公開(kāi)讀,內(nèi)部存
public String Str { get; set;} //這些是VS 2008支持的寫(xiě)法,不用自己定義私有變量了
public MyObject()
{
}
protected MyObject(SerializationInfo info, StreamingContext context)
{
//這個(gè)特殊的構(gòu)造函數(shù)會(huì)被自動(dòng)調(diào)用,如果是我們自己實(shí)現(xiàn),就某Manager調(diào)用
//其實(shí)如果沒(méi)有復(fù)雜的構(gòu)造函數(shù)初始化邏輯,比如給readonly變量賦值
//可以將SetObjectData直接實(shí)現(xiàn)于接口,由我們負(fù)責(zé)數(shù)據(jù)存取的部分來(lái)調(diào)用
SetObjectData(info);
}
protected virtual void SetObjectData(SerializationInfo info)
{
N1= info.GetInt32("i"); //由對(duì)象自己將數(shù)據(jù)字典展開(kāi)
N2 = info.GetInt32("j"); //還原對(duì)象狀態(tài)
Str = info.GetString("k"); //這樣就可以把跟對(duì)象無(wú)關(guān)的存儲(chǔ)邏輯外包出去
}
[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("i", N1);//info可以理解為Serializer定義的數(shù)據(jù)字典格式
info.AddValue("j", N2);//這相當(dāng)于持久化的概念進(jìn)入了BL層
info.AddValue("k", Str);//所以當(dāng)我們實(shí)現(xiàn)時(shí),應(yīng)該根據(jù)業(yè)務(wù)邏輯定義自己的數(shù)據(jù)字典
}
}
Serializer:
BinaryFormatter formatter = new BinaryFormatter();
保存至fs:
formatter.Serialize(fs, a1);
從fs讀?。?br />formatter.Deserialize(fs);
it知識(shí)庫(kù):怪怪設(shè)計(jì)論閑談篇:職責(zé)與解耦的矛盾,轉(zhuǎn)載需保留來(lái)源!
鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。