如何让Visitor变得可爱1
本文轉自:http://www.cnblogs.com/idior/archive/2005/01/19/94280.html
??? 在wayfarer的文章中提到了如何利用visitor模式實現添加新的功能。如他所說,在實際過程中顯的不是那么可愛。不過他為我們提供了一個可行的解決方案,本文將在此基礎上使其盡量變的可愛。
?
Wayfarer所認為的不可愛之處
1、 假設你需要增加新的一種媒體文件,如WMV文件,在既有Visitor模式的架構下,應該怎樣?你頭疼了,因為實在是太麻煩。你需要定義一個WAV類,繼承VedioMedia。最麻煩的是你必須修改VedioVisitor及其所有子類,因為Visit方法中沒有包含訪問WAV對象的行為。
?
在visitor模式中,很難對原有的類型(被訪問者)進行擴充,這的確是visitor模式的硬傷。
?
正如wayfarer所說 對于媒體播放器,Vistor模式非但不可愛,而且丑陋。從現實角度考慮,我們要做一個媒體播放器,在設計之初,對于各種媒體應具備的行為,通常能夠充分考慮到。且各種媒體的行為是大致相同的。然而對于媒體文件類型,反而是無法估量的。說不定,什么時候又會出現新成果來。從mp3到mp4,再到mpn,你能預知嗎?
?
然而這究竟是我們對visitot模式實現的不夠,還是visitot模式本身的問題。
?
那么讓我們來看看添加新的類型,究竟對原來的結構需要做如何的變化。
?
我們先來看看原有的設計
可以發現在這其中隱藏有“bad smell”,就是每一個的Accept方法是不同的,不同點在于visitor.VisitXXX(this);
這個XXX就是訪問的對象類型,對象類型要發生變化,我們當然要隱藏變化,那么這種類型的變化如何隱藏?
通常情況下我們都是隱藏主語的變化,比如visitor的變化我們很容易利用類的多態來實現,那么賓語的變化如何隱藏呢?可以看到該方法有一個參數,而參數的類型就是被訪問者的類型,這樣我們就可以通過方法的多態來實現類型信息的隱藏。
?
visitor.Visit(MP3?m);
visitor.Visit(WAV?w);
?
通過函數參數的變化來實現方法的多態,在運行期自動綁定。
在這里提醒大家,不要只記得override可以實現多態,overload也是可以實現多態的。
?
這樣每個類的Accrpt方法就統一了
public?override?void?Accept(AudioVisitor?visitor)
{
????visitor.Visit(this);
}
?
既然每個類都同一了那么能否將這個函數提到他們共同的父類當中呢?這也是通常情況下,利用重構來實現的目標,結果非常誘人。
?
但是,不幸的是――答案是否定的,因為那么一來this的類型信息也就失去了,那么方法級的多態就無法實現了。不過以上的工作還是有意義的,對于簡化形式還是起到了幫助,但是由于不能將Accept方法提到父類中,那么意義自然不是太大。
?
下面將在此修改的基礎上我們添加一個類WMA。
?
在原來的AudioMedia中添加一個類是很容易實現的,也體現了open-close 原則,但是對應的在visitor中的變化,就不那么可愛了。
添加WMA后,vistor部分的代碼改變
public?interface?AudioVisitor
{
????void?Visit(WMA?m?);
????void?Visit(MP3?m?);
????void?Visit(WAV?w?);
}
public?class?RenameAudioVistor?:?AudioVisitor?
{
????void?Visit(WMA?m?)
????{
??????//do?something
????}
????void?Visit(MP3?m?)
????{
??????//do?something
????}
????void?Visit(WAV?w?)
????{
??????//do?something
????}
}
?
我們需要手工去改寫每一個Visitor(抽象的,具體的),在其中添加有關WMA的操作,這嚴重違反了open-close 原則,這樣我們看到了在Visitor模式中實現添加新的類型(在此為媒體類型)是多么丑陋的事,但是這是Visitor模式的問題嗎?他能夠解決嗎?還是我們沒有考慮到更好的方法?
?
如何才能實現在AudioMedia中添加新的類型,使得在Vistor結構中也滿足open-close 原則?
?
不僅僅是違反OCP原則,在添加新類型的時候甚至還要去修改抽象類Audio,而保持抽象不變也是我們所追求的。為什么會這樣,究其原因在于在目前的結構下,被訪問對象和訪問對象互相依賴,自然不利于分離變化,必須去掉一層依賴關系。
?
下面給出相應的實現方案。
?
?
在這里AudioVisitor已經退化到沒有任何方法,只是一個空接口供Accept方法使用。
每一個具體類如MP3都有一個對應的Visitor類,在這個類中有Visit方法的聲明,也就是說把原來在AudioVisitor中的聲明工作,交由各個具體的XXXVisitor接口去完成。這樣當添加新類XXX時就添加一個對應的訪問接口XXXVisitor,然后在由RenameAudioVisitor實現這些接口,RenameAudioVisitor的工作與前面一樣沒有什么變化。這樣我們就不會去修改AudioVisitor這個抽象類,只要添加相應的訪問接口并實現它,部分滿足了OCP原則。說是部分那是因為還要去修改RenameAudioVisitor以實現新的接口,但是修改具體類和修改抽象類的代價相比就小的多,使我們面對新的變化也容易的多。
?
同時還帶來了一個好處就是,我們可以有選擇的讓別人訪問。比如MP3不想讓別人visit,那么我們不去創建MP3Visitor就是了。
?
另外注意在Accept方法中有一個轉型操作。通過這個操作我們可以進一步保證類型安全,也是通過它我們可以輕易的實現XXXVisitor的選擇(即要不要讓XXX被訪問)。
{
???MP3Visitor?mp3Visitor=visitor?as?MP3Visitor;
???if?(mp3Visitor!=?null)
????????mp3Visitor.Visit(this);
???else??//error?or?there?is?no?Visitor?for?it
????????DoNothing()
}
?
本文通過對Visitor模式的一系列的調整,讓它更容易適應添加新的類型,不知你是否注意到我為Vistor起的名字(RenameAudioVisitor),wayfarer說visitor模式不適合于媒體功能擴展這個應用。這個說法顯然不夠全面,應該說它不適合擴展諸如Play ,Resize這類的方法。
首先這些基本方法在設計初就應該考慮到,Visitor當然不適合做本來就可以很容易做的事情,其次在wayfarer的例子中沒有使用到ObjectStructure, 而這是Visitor模式的一大特點,利用它可以很方便的完成一些任務。所以不是說Visitor不適合媒體功能擴展,而是不適合什么功能的擴展。擴展什么功能進一步決定了采用何種模式。
?
在接下來的隨筆中,我盡可能舉一個Visitor模式適合于媒體功能擴展的例子,同時在這個例子中發揮ObjectStructure的作用。另外也想研究一下,Visitor模式可以在哪些實際的場景中發揮作用。轉載于:https://www.cnblogs.com/jeriffe/articles/1880876.html
總結
以上是生活随笔為你收集整理的如何让Visitor变得可爱1的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用 JA Transmenu 模块做多级
- 下一篇: 3G门户宣讲+笔试