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