Android 訪問者模式
Android 設計模式系列文章Android 23種設計模式
前言
訪問者模式是一種把資料操作與資料結構分離的設計模式。通常適用於:
1、物件的結構比較穩定,但又經常要在這個物件上定義新的操作。
2、需要對一個物件進行很多不同且不相關的操作,而需要避免這些操作汙染這個物件類。也不希望在增加新的操作時,修改原本的類。
這種設計模式相對複雜,而當你需要用到訪問者模式的時候,這個模式就很必要了。
訪問者模式定義
表示一個作用於某物件結構中的各元素的操作。它使你可以在不改變各元素類的前提下,定義作用於這些元素的新操作。
例子
我們通過一個例子來學習理解什麼是訪問者模式。這是一個老師訪問學生成績的例子。
1、被訪問者
首先我們定義被訪問者學生。抽象了一個學生基類,隨機數來模擬總成績。
public abstract class Students { public String name; public int totalScore; // 總成績 Students(String aName) { name = aName; totalScore = new Random().nextInt(100); } public abstract void accept(Visitor visitor); }
然後我們定義了兩種學生,當然你可以定義多種。
// 體育生,隨機數模擬成績 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); } } // 美術生,隨機數模擬成績 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); } }
程式碼很簡單就是模擬了一下成績,我們可以看到被訪問者學生,這個類很穩定。不需要再新增其他資訊來“汙染”被訪問者了。
2、訪問者
先抽象出訪問者的訪問方法visit
public interface Visitor { public void visit(ArtStudents artStudents); public void visit(SportsStudents sportsStudents); }
這裡定義兩個訪問者,一個班主任,他關注總成績和特長科目成績。另一個特長老師,他只關心特長科目成績。
// 班主任 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); } } // 特長老師 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); } }
訪問者就比較直觀,目的明確,訪問者只關注他想關注的資訊,不需要多於的操作。這裡體現了訪問操作的不同且不相關。
3、訪問
訪問者和被訪問者我們寫完了。然後我們寫如何訪問。訪問的核心就是一個遍歷。根據不同的訪問者和被訪問者達到不同的操作目的。
先遍歷
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); } } }
這裡模擬把所有學生放到一個list裡面。遍歷的時候,被訪問者students呼叫accept訪問來同意訪問。記得前文的accept方法裡就是呼叫Visitor.visit方法而已。所以我們看最後如何呼叫
4、呼叫
StudentsList list = new StudentsList(); list.showStudentschievement(new HeadmasterTeacherVisitor()); list.showStudentschievement(new SpecialTeacherVisitor());
看輸出結果:
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
訪問者模式小結
訪問者模式核心有如下,記住這三點理解何為訪問者模式:
1、被訪問者不變。
2、根據訪問者和被訪問者的不同,兩兩對應達到不同的目的。
3、遍歷被訪問者實現“訪問”。
總結
最後抖下訪問者模式的優缺點
優點:
1、各角色職責分離,符合單一原則
2、擴充套件十分方便,靈活
3、資料結構和資料結構上的操作解耦
缺點:
1、被訪問者對訪問者公佈了細節,違反迪米特原則
2、被訪問者要改動的時候,修改十分麻煩。
3、訪問者和被訪者為了達到不同的行為目的的時候,為了區分依賴了類的不同,沒有依賴抽象。
所以不光是其它模式還是訪問者模式,我們都要根據具體的情況來評估我們是否真的需要這種設計模式。這裡的訪問者模式就得十分注意我們的被訪問者是否真的足夠穩定。所以一定要多思考,明確它的使用場景。