《設(shè)計(jì)模式》訪問(wèn)者模式

定義

封裝某些作用于某種數(shù)據(jù)結(jié)構(gòu)中各元素的操作前鹅,它可以在不改變數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些元素的新的操作。

介紹

  • 訪問(wèn)者模式屬于行為型模式聂渊。
  • 訪問(wèn)者模式是一種將數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)操作分離的設(shè)計(jì)模式。
  • 訪問(wèn)者模式比較復(fù)雜,而且實(shí)際使用的地方并不多祠挫。
  • 訪問(wèn)者模式適用于數(shù)據(jù)結(jié)構(gòu)穩(wěn)定的元素操作上,一旦數(shù)據(jù)結(jié)構(gòu)易變窗看,則不適用茸歧。

UML類(lèi)圖

訪問(wèn)者模式UML類(lèi)圖

角色說(shuō)明:

  • Visitor(抽象訪問(wèn)者):接口或者抽象類(lèi),為每一個(gè)元素(Element)聲明一個(gè)訪問(wèn)的方法显沈。
  • ConcreteVisitor(具體訪問(wèn)者):實(shí)現(xiàn)抽象訪問(wèn)者中的方法软瞎,即對(duì)每一個(gè)元素都有其具體的訪問(wèn)行為。
  • Element(抽象元素):接口或者抽象類(lèi)拉讯,定義一個(gè)accept方法涤浇,能夠接受訪問(wèn)者(Visitor)的訪問(wèn)。
  • ConcreteElementA魔慷、ConcreteElementB(具體元素):實(shí)現(xiàn)抽象元素中的accept方法只锭,通常是調(diào)用訪問(wèn)者提供的訪問(wèn)該元素的方法。
  • Client(客戶端類(lèi)):即要使用訪問(wèn)者模式的地方院尔。

實(shí)現(xiàn)

以我們平時(shí)聽(tīng)歌看視頻為例蜻展,音樂(lè)視頻網(wǎng)站都會(huì)提供在線播放和下載的功能,當(dāng)我們有空的時(shí)候往往就選擇了在線播放邀摆,比較忙的時(shí)候就選擇先下載下來(lái)纵顾,有空的時(shí)候再去觀看。其中栋盹,音樂(lè)視頻網(wǎng)站就是具體的要訪問(wèn)的元素施逾,閑人和忙人就是具體的訪問(wèn)者,閑人和忙人的訪問(wèn)行為是不一樣的例获。

1汉额、創(chuàng)建抽象訪問(wèn)者。定義一個(gè)抽象的受訪問(wèn)方法以及其他公共的方法:

public abstract class Web {
  public String name;

  public Web(String name) {
    this.name = name;
  }

  // 定義一個(gè)抽象的受訪問(wèn)方法
  public abstract void accept(Visitor visitor);

  // 下載資源
  public abstract void download();

  public String getName() {
    return name;
  }
}

2榨汤、創(chuàng)建具體元素蠕搜。實(shí)現(xiàn)抽象元素中的accept()方法,通常是調(diào)用訪問(wèn)者提供的訪問(wèn)該元素的方法件余。下面創(chuàng)建音樂(lè)類(lèi)以及視頻類(lèi)讥脐,他們都有一個(gè)download()方法遭居,但是其具體下載的內(nèi)容是不一樣的,同時(shí)他們也存在各自獨(dú)有的方法playMusic()和playVideo():

// 音樂(lè)類(lèi)
public class Music extends Web {
  public Music(String name) {
    super(name);
  }

  // 接受訪問(wèn)者的訪問(wèn)
  @Override
  public void accept(Visitor visitor) {
    visitor.visit(this);
  }

  // 實(shí)現(xiàn)父類(lèi)中的公共方法
  @Override
  public void download() {
    System.out.println("下載音樂(lè)~~");
  }

  // 音樂(lè)類(lèi)獨(dú)有方法
  public void playMusic() {
    System.out.println("播放音樂(lè)ing");
  }
}

// 視頻類(lèi)
public class Video extends Web {
  public Video(String name) {
    super(name);
  }

  // 接受訪問(wèn)者的訪問(wèn)
  @Override
  public void accept(Visitor visitor) {
    visitor.visit(this);
  }

  // 實(shí)現(xiàn)父類(lèi)中的公共方法
  @Override
  public void download() {
    System.out.println("下載視頻~~");
  }

  // 視頻類(lèi)獨(dú)有方法
  public void playVideo() {
    System.out.println("播放視頻ing");
  }
}

3旬渠、創(chuàng)建抽象訪問(wèn)者俱萍。為每一個(gè)元素聲明一個(gè)訪問(wèn)的方法:

public interface Visitor {
  // 訪問(wèn)音樂(lè)類(lèi)
  void visit(Music music);
  // 訪問(wèn)視頻類(lèi)
  void visit(Video video);
}

4、創(chuàng)建具體訪問(wèn)者告丢。實(shí)現(xiàn)抽象訪問(wèn)者中的方法枪蘑,即對(duì)每一個(gè)元素都有其具體的訪問(wèn)行為。下面以閑人和忙人為例:

// 閑人
public class Idler implements Visitor {
  private String name;

  public Idler(String name) {
    this.name = name;
  }

  @Override
  public void visit(Music music) {
    System.out.println(name + "瀏覽音樂(lè)網(wǎng)站:" + music.getName());
    music.playMusic();
  }

  @Override
  public void visit(Video video) {
    System.out.println(name + "瀏覽視頻網(wǎng)站:" + video.getName());
    video.playVideo();
  }
}

// 忙人
public class Busy implements Visitor {
  private String name;

  public Busy(String name) {
    this.name = name;
  }

  @Override
  public void visit(Music music) {
    System.out.println(name + "瀏覽音樂(lè)網(wǎng)站:" + music.getName());
    music.download();
  }

  @Override
  public void visit(Video video) {
    System.out.println(name + "瀏覽視頻網(wǎng)站:" + video.getName());
    video.download();
  }
}

5岖免、創(chuàng)建對(duì)象結(jié)構(gòu)岳颇。另外,為了方便訪問(wèn)多個(gè)元素颅湘,創(chuàng)建一個(gè)對(duì)象結(jié)構(gòu)话侧,在其內(nèi)部管理元素集合,并且可以迭代這些元素供訪問(wèn)者訪問(wèn):

public class Websites {
  // 元素集合
  List<Web> list = new ArrayList<>();

  public void accept(Visitor visitor) {
    Iterator<Web> iterator = list.iterator();
    while (iterator.hasNext()) {
      iterator.next().accept(visitor);
    }
  }

  public void addWeb(Web web) {
    list.add(web);
  }
}

6闯参、客戶端測(cè)試

public void test() {
  // 創(chuàng)建不同的元素
  Music wangyiyue = new Music("網(wǎng)易云音樂(lè)");
  Music kugou = new Music("酷狗");
  Video youku = new Video("優(yōu)酷");
  Video iqiyi = new Video("愛(ài)奇藝");

  // 放入對(duì)象結(jié)構(gòu)中
  Websites websites = new Websites();
  websites.addWeb(wangyiyue);
  websites.addWeb(kugou);
  websites.addWeb(youku);
  websites.addWeb(iqiyi);

  // 集合接受idler1的訪問(wèn)
  Visitor idler1 = new Idler("閑人1號(hào)");
  websites.accept(idler1);

  System.out.println("-------------------------------------");

  // 集合接受busy1的訪問(wèn)
  Visitor busy1 = new Busy("忙人2號(hào)");
  websites.accept(busy1);
}

輸出結(jié)果:

閑人1號(hào)瀏覽音樂(lè)網(wǎng)站:網(wǎng)易云音樂(lè)
播放音樂(lè)ing
閑人1號(hào)瀏覽音樂(lè)網(wǎng)站:酷狗
播放音樂(lè)ing
閑人1號(hào)瀏覽視頻網(wǎng)站:優(yōu)酷
播放視頻ing
閑人1號(hào)瀏覽視頻網(wǎng)站:愛(ài)奇藝
播放視頻ing
-------------------------------------
忙人2號(hào)瀏覽音樂(lè)網(wǎng)站:網(wǎng)易云音樂(lè)
下載音樂(lè)~~
忙人2號(hào)瀏覽音樂(lè)網(wǎng)站:酷狗
下載音樂(lè)~~
忙人2號(hào)瀏覽視頻網(wǎng)站:優(yōu)酷
下載視頻~~
忙人2號(hào)瀏覽視頻網(wǎng)站:愛(ài)奇藝
下載視頻~~

應(yīng)用場(chǎng)景

  • 對(duì)象結(jié)構(gòu)比較穩(wěn)定瞻鹏,很少改變,但是經(jīng)常需要在此對(duì)象結(jié)構(gòu)上定義新的操作行為時(shí)鹿寨。
  • 需要對(duì)一個(gè)對(duì)象結(jié)構(gòu)中的對(duì)象進(jìn)行很多不同的并且不相關(guān)的操作新博,它可以在不改變這個(gè)數(shù)據(jù)結(jié)構(gòu)的前提下定義作用于這些元素的新的操作。

優(yōu)缺點(diǎn)

優(yōu)點(diǎn)

  • 各種角色各司其職脚草,符合單一職責(zé)原則赫悄。
  • 原有的類(lèi)上新增操作只需實(shí)現(xiàn)一個(gè)具體訪問(wèn)者就可以,不必修改整個(gè)類(lèi)層次馏慨,符合開(kāi)閉原則埂淮。
  • 良好的擴(kuò)展性,新增訪問(wèn)操作變得簡(jiǎn)單写隶。
  • 數(shù)據(jù)操作和數(shù)據(jù)結(jié)構(gòu)解耦同诫。

缺點(diǎn)

  • 具體元素對(duì)訪問(wèn)者公布了實(shí)現(xiàn)細(xì)節(jié),破壞了類(lèi)的封裝性樟澜,違反了迪米特原則。
  • 違反了依賴(lài)倒置原則叮盘,為了達(dá)到區(qū)別對(duì)待依賴(lài)了具體而不是抽象秩贰。
  • 具體元素修改的成本太大。
  • 新增具體元素困難柔吼,需要在抽象訪問(wèn)者角色中增加一個(gè)新的抽象操作毒费,違反了開(kāi)閉原則。

其他

訪問(wèn)者模式實(shí)際使用中比較少愈魏,但是真正需要用到時(shí)觅玻,還是很有用的想际。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市溪厘,隨后出現(xiàn)的幾起案子胡本,更是在濱河造成了極大的恐慌,老刑警劉巖畸悬,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件侧甫,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蹋宦,警方通過(guò)查閱死者的電腦和手機(jī)披粟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)冷冗,“玉大人守屉,你說(shuō)我怎么就攤上這事≥镎蓿” “怎么了拇泛?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)须板。 經(jīng)常有香客問(wèn)我碰镜,道長(zhǎng),這世上最難降的妖魔是什么习瑰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任绪颖,我火速辦了婚禮,結(jié)果婚禮上甜奄,老公的妹妹穿的比我還像新娘柠横。我一直安慰自己,他們只是感情好课兄,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布牍氛。 她就那樣靜靜地躺著,像睡著了一般烟阐。 火紅的嫁衣襯著肌膚如雪搬俊。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,856評(píng)論 1 290
  • 那天蜒茄,我揣著相機(jī)與錄音唉擂,去河邊找鬼。 笑死檀葛,一個(gè)胖子當(dāng)著我的面吹牛玩祟,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播屿聋,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼空扎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼藏鹊!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起转锈,我...
    開(kāi)封第一講書(shū)人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盘寡,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后黑忱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體宴抚,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年甫煞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了菇曲。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡抚吠,死狀恐怖常潮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情楷力,我是刑警寧澤喊式,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布,位于F島的核電站萧朝,受9級(jí)特大地震影響岔留,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜检柬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一献联、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧何址,春花似錦里逆、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至偎血,卻和暖如春诸衔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背颇玷。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工署隘, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人亚隙。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像违崇,于是被迫代替她去往敵國(guó)和親阿弃。 傳聞我的和親對(duì)象是個(gè)殘疾皇子诊霹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

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