前言
訪問者模式是一種把數(shù)據(jù)操作與數(shù)據(jù)結(jié)構(gòu)分離的設(shè)計(jì)模式。通常適用于:
1、對(duì)象的結(jié)構(gòu)比較穩(wěn)定,但又經(jīng)常要在這個(gè)對(duì)象上定義新的操作。
2、需要對(duì)一個(gè)對(duì)象進(jìn)行很多不同且不相關(guān)的操作,而需要避免這些操作污染這個(gè)對(duì)象類。也不希望在增加新的操作時(shí),修改原本的類。
這種設(shè)計(jì)模式相對(duì)復(fù)雜,而當(dāng)你需要用到訪問者模式的時(shí)候,這個(gè)模式就很必要了。
訪問者模式定義
表示一個(gè)作用于某對(duì)象結(jié)構(gòu)中的各元素的操作。它使你可以在不改變各元素類的前提下,定義作用于這些元素的新操作。
例子
我們通過一個(gè)例子來(lái)學(xué)習(xí)理解什么是訪問者模式。這是一個(gè)老師訪問學(xué)生成績(jī)的例子。
1、被訪問者
首先我們定義被訪問者學(xué)生。抽象了一個(gè)學(xué)生基類,隨機(jī)數(shù)來(lái)模擬總成績(jī)。
public abstract class Students {
public String name;
public int totalScore; // 總成績(jī)
Students(String aName) {
name = aName;
totalScore = new Random().nextInt(100);
}
public abstract void accept(Visitor visitor);
}
然后我們定義了兩種學(xué)生,當(dāng)然你可以定義多種。
// 體育生,隨機(jī)數(shù)模擬成績(jī)
public class SportsStudents extends Students {
public int sports;
public SportsStudents(String aName) {
super(aName);
sports = new Random().nextInt(100);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
// 美術(shù)生,隨機(jī)數(shù)模擬成績(jī)
public class ArtStudents extends Students {
public int art;
public ArtStudents(String aName) {
super(aName);
art = new Random().nextInt(100);
}
@Override
public void accept(Visitor visitor) {
visitor.visit(this);
}
}
代碼很簡(jiǎn)單就是模擬了一下成績(jī),我們可以看到被訪問者學(xué)生,這個(gè)類很穩(wěn)定。不需要再添加其他信息來(lái)“污染”被訪問者了。
2、訪問者
先抽象出訪問者的訪問方法visit
public interface Visitor {
public void visit(ArtStudents artStudents);
public void visit(SportsStudents sportsStudents);
}
這里定義兩個(gè)訪問者,一個(gè)班主任,他關(guān)注總成績(jī)和特長(zhǎng)科目成績(jī)。另一個(gè)特長(zhǎng)老師,他只關(guān)心特長(zhǎng)科目成績(jī)。
// 班主任
public class HeadmasterTeacherVisitor implements Visitor {
private static String TAG = ArtStudents.class.getSimpleName();
@Override
public void visit(ArtStudents artStudents) {
Log.d(TAG,"name = " + artStudents.name);
Log.d(TAG,"totalScore = " + artStudents.totalScore);
Log.d(TAG,"art = " + artStudents.art);
}
@Override
public void visit(SportsStudents sportsStudents) {
Log.d(TAG,"name = " + sportsStudents.name);
Log.d(TAG,"totalScore = " + sportsStudents.totalScore);
Log.d(TAG,"sports = " + sportsStudents.sports);
}
}
// 特長(zhǎng)老師
public class SpecialTeacherVisitor implements Visitor {
private static String TAG = SpecialTeacherVisitor.class.getSimpleName();
@Override
public void visit(ArtStudents artStudents) {
Log.d(TAG,"name = " + artStudents.name);
Log.d(TAG,"art = " + artStudents.art);
}
@Override
public void visit(SportsStudents sportsStudents) {
Log.d(TAG,"name = " + sportsStudents.name);
Log.d(TAG,"sports = " + sportsStudents.sports);
}
}
訪問者就比較直觀,目的明確,訪問者只關(guān)注他想關(guān)注的信息,不需要多于的操作。這里體現(xiàn)了訪問操作的不同且不相關(guān)。
3、訪問
訪問者和被訪問者我們寫完了。然后我們寫如何訪問。訪問的核心就是一個(gè)遍歷。根據(jù)不同的訪問者和被訪問者達(dá)到不同的操作目的。
先遍歷
public class StudentsList {
List<Students> list = new LinkedList<Students>();
public StudentsList() {
list.add(new ArtStudents("jack"));
list.add(new ArtStudents("john"));
list.add(new SportsStudents("lily"));
list.add(new SportsStudents("sky"));
}
public void showStudentschievement(Visitor visitor) {
for (Students students : list) {
students.accept(visitor);
}
}
}
這里模擬把所有學(xué)生放到一個(gè)list里面。遍歷的時(shí)候,被訪問者students調(diào)用accept訪問來(lái)同意訪問。記得前文的accept方法里就是調(diào)用Visitor.visit方法而已。所以我們看最后如何調(diào)用
4、調(diào)用
StudentsList list = new StudentsList();
list.showStudentschievement(new HeadmasterTeacherVisitor());
list.showStudentschievement(new SpecialTeacherVisitor());
看輸出結(jié)果:
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: name = jack
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: totalScore = 13
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: art = 52
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: name = john
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: totalScore = 98
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: art = 12
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: name = lily
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: totalScore = 51
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: sports = 23
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: name = sky
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: totalScore = 24
01-21 14:37:31.729 26555-26555/com.yink.designpattern.designpattern D/ArtStudents: sports = 30
01-21 14:37:31.739 26555-26555/com.yink.designpattern.designpattern D/SpecialTeacherVisitor: name = jack
01-21 14:37:31.739 26555-26555/com.yink.designpattern.designpattern D/SpecialTeacherVisitor: art = 52
01-21 14:37:31.739 26555-26555/com.yink.designpattern.designpattern D/SpecialTeacherVisitor: name = john
01-21 14:37:31.739 26555-26555/com.yink.designpattern.designpattern D/SpecialTeacherVisitor: art = 12
01-21 14:37:31.739 26555-26555/com.yink.designpattern.designpattern D/SpecialTeacherVisitor: name = lily
01-21 14:37:31.739 26555-26555/com.yink.designpattern.designpattern D/SpecialTeacherVisitor: sports = 23
01-21 14:37:31.739 26555-26555/com.yink.designpattern.designpattern D/SpecialTeacherVisitor: name = sky
01-21 14:37:31.739 26555-26555/com.yink.designpattern.designpattern D/SpecialTeacherVisitor: sports = 30
訪問者模式小結(jié)
訪問者模式核心有如下,記住這三點(diǎn)理解何為訪問者模式:
1、被訪問者不變。
2、根據(jù)訪問者和被訪問者的不同,兩兩對(duì)應(yīng)達(dá)到不同的目的。
3、遍歷被訪問者實(shí)現(xiàn)“訪問”。
總結(jié)
最后抖下訪問者模式的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1、各角色職責(zé)分離,符合單一原則
2、擴(kuò)展十分方便,靈活
3、數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)結(jié)構(gòu)上的操作解耦
缺點(diǎn):
1、被訪問者對(duì)訪問者公布了細(xì)節(jié),違反迪米特原則
2、被訪問者要改動(dòng)的時(shí)候,修改十分麻煩。
3、訪問者和被訪者為了達(dá)到不同的行為目的的時(shí)候,為了區(qū)分依賴了類的不同,沒有依賴抽象。
所以不光是其它模式還是訪問者模式,我們都要根據(jù)具體的情況來(lái)評(píng)估我們是否真的需要這種設(shè)計(jì)模式。這里的訪問者模式就得十分注意我們的被訪問者是否真的足夠穩(wěn)定。所以一定要多思考,明確它的使用場(chǎng)景。