
作者:小傅哥
博客:https:// - 原創(chuàng)系列專(zhuān)題文章
沉淀、分享、成長(zhǎng),讓自己和他人都能有所收獲!😄
一、前言
能力,是你前行的最大保障
年齡會(huì)不斷的增長(zhǎng),但是什么才能讓你不慌張。一定是能力,即使是在一個(gè)看似還很安穩(wěn)的工作中也是一樣,只有擁有能留下的本事和跳出去的能力,你才會(huì)是安穩(wěn)的。而能力的提升是不斷突破自己的未知也就是拓展寬度,以及在專(zhuān)業(yè)領(lǐng)域建設(shè)個(gè)人影響力也就是深度。如果日復(fù)日365天,天天搬磚,一切都沒(méi)有變化的重復(fù)只能讓手上增長(zhǎng)點(diǎn)老繭,歲月又嘆人生苦短。
站得高看的遠(yuǎn)嗎?
站得高確實(shí)能看得遠(yuǎn),也能給自己更多的追求。但,站的高了,原本看的清的東西就變得看不清了。視角和重點(diǎn)的不同,會(huì)讓我們有很多不同的選擇,而腳踏實(shí)地是給自己奠定能攀升起來(lái)的基石,當(dāng)真的可以四平八穩(wěn)的走向山頭的時(shí)候,才是適合看到更遠(yuǎn)的時(shí)候。
數(shù)學(xué)好才能學(xué)編碼嗎
往往很多時(shí)候?qū)W編程的初學(xué)者都會(huì)問(wèn)數(shù)學(xué)不好能學(xué)會(huì)嗎?其實(shí)可以想想那為什么數(shù)學(xué)不好呢?在這條沒(méi)學(xué)好的路上,你為它們付出了多少時(shí)間呢?如果一件事情你敢做到和寫(xiě)自己名字一樣熟悉,還真的有難的東西嗎。從大學(xué)到畢業(yè)能寫(xiě)出40萬(wàn)行代碼的,還能愁找不到工作嗎,日積月累,每一天并沒(méi)有多難。難的你想用最后一個(gè)月的時(shí)間學(xué)完人家四年努力的成績(jī)的。學(xué)習(xí),要趁早。
二、開(kāi)發(fā)環(huán)境
- JDK 1.8
- Idea + Maven
- 涉及工程三個(gè),可以通過(guò)關(guān)注公眾號(hào):
bugstack蟲(chóng)洞棧,回復(fù)源碼下載獲取(打開(kāi)獲取的鏈接,找到序號(hào)18)
| 工程 | 描述 |
|---|
| itstack-demo-design-22-00 | 場(chǎng)景模擬工程;模擬學(xué)生和老師信息不同視角訪問(wèn) |
三、訪問(wèn)者模式介紹

訪問(wèn)者要解決的核心事項(xiàng)是,在一個(gè)穩(wěn)定的數(shù)據(jù)結(jié)構(gòu)下,例如用戶(hù)信息、雇員信息等,增加易變的業(yè)務(wù)訪問(wèn)邏輯。為了增強(qiáng)擴(kuò)展性,將這兩部分的業(yè)務(wù)解耦的一種設(shè)計(jì)模式。

說(shuō)白了訪問(wèn)者模式的核心在于同一個(gè)事物不同視角下的訪問(wèn)信息不同,比如一個(gè)美女手里拿個(gè)冰激凌。小朋友會(huì)注意冰激凌,大朋友會(huì)找自己喜歡的地方觀測(cè)敵情。
四、案例場(chǎng)景模擬

在本案例中我們模擬校園中的學(xué)生和老師對(duì)于不同用戶(hù)的訪問(wèn)視角
這個(gè)案例場(chǎng)景我們模擬校園中有學(xué)生和老師兩種身份的用戶(hù),那么對(duì)于家長(zhǎng)和校長(zhǎng)關(guān)心的角度來(lái)看,他們的視角是不同的。家長(zhǎng)更關(guān)心孩子的成績(jī)和老師的能力,校長(zhǎng)更關(guān)心老師所在班級(jí)學(xué)生的人數(shù)和升學(xué)率{此處模擬的}。
那么這樣學(xué)生和老師就是一個(gè)固定信息的內(nèi)容,而想讓不同視角的用戶(hù)獲取關(guān)心的信息,就比較適合使用觀察者模式來(lái)實(shí)現(xiàn),從而讓實(shí)體與業(yè)務(wù)解耦,增強(qiáng)擴(kuò)展性。但觀察者模式的整體類(lèi)結(jié)構(gòu)相對(duì)復(fù)雜,需要梳理清楚再開(kāi)發(fā)
五、訪問(wèn)者模式搭建工程
訪問(wèn)者模式的類(lèi)結(jié)構(gòu)相對(duì)其他設(shè)計(jì)模式來(lái)說(shuō)比較復(fù)雜,但這樣的設(shè)計(jì)模式在我看來(lái)更加燒氣有魅力,它能闊開(kāi)你對(duì)代碼結(jié)構(gòu)的新認(rèn)知,用這樣思維不斷的建設(shè)出更好的代碼架構(gòu)。
關(guān)于這個(gè)案例的核心邏輯實(shí)現(xiàn),有以下幾點(diǎn);
- 建立用戶(hù)抽象類(lèi)和抽象訪問(wèn)方法,再由不同的用戶(hù)實(shí)現(xiàn);老師和學(xué)生。
- 建立訪問(wèn)者接口,用于不同人員的訪問(wèn)操作;校長(zhǎng)和家長(zhǎng)。
- 最終是對(duì)數(shù)據(jù)的看板建設(shè),用于實(shí)現(xiàn)不同視角的訪問(wèn)結(jié)果輸出。
1. 工程結(jié)構(gòu)
itstack-demo-design-22-00
└── src
├── main
│ └── java
│ └── org.itstack.demo.design
│ ├── user
│ │ ├── impl
│ │ │ ├── Student.java
│ │ │ └── Teacher.java
│ │ └── User.java
│ ├── visitor
│ │ ├── impl
│ │ │ ├── Parent.java
│ │ │ └── Principal.java
│ │ └── Visitor.java
│ └── DataView.java
└── test
└── java
└── org.itstack.demo.design.test
└── ApiTest.java
訪問(wèn)者模式模型結(jié)構(gòu)

以上是視圖展示了代碼的核心結(jié)構(gòu),主要包括不同視角下的不同用戶(hù)訪問(wèn)模型。
在這里有一個(gè)關(guān)鍵的點(diǎn)非常重要,也就是整套設(shè)計(jì)模式的核心組成部分;visitor.visit(this),這個(gè)方法在每一個(gè)用戶(hù)實(shí)現(xiàn)類(lèi)里,包括;Student、Teacher。在以下的實(shí)現(xiàn)中可以重點(diǎn)關(guān)注。
2. 代碼實(shí)現(xiàn)
2.1 定義用戶(hù)抽象類(lèi)
// 基礎(chǔ)用戶(hù)信息
public abstract class User {
public String name; // 姓名
public String identity; // 身份;重點(diǎn)班、普通班 | 特級(jí)教師、普通教師、實(shí)習(xí)教師
public String clazz; // 班級(jí)
public User(String name, String identity, String clazz) {
this.name = name;
this.identity = identity;
this.clazz = clazz;
}
// 核心訪問(wèn)方法
public abstract void accept(Visitor visitor);
}
- 基礎(chǔ)信息包括;姓名、身份、班級(jí),也可以是一個(gè)業(yè)務(wù)用戶(hù)屬性類(lèi)。
- 定義抽象核心方法,
abstract void accept(Visitor visitor),這個(gè)方法是為了讓后續(xù)的用戶(hù)具體實(shí)現(xiàn)者都能提供出一個(gè)訪問(wèn)方法,共外部使用。
2.2 實(shí)現(xiàn)用戶(hù)信息(老師和學(xué)生)
老師類(lèi)
public class Teacher extends User {
public Teacher(String name, String identity, String clazz) {
super(name, identity, clazz);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
// 升本率
public double entranceRatio() {
return BigDecimal.valueOf(Math.random() * 100).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
}
}
學(xué)生類(lèi)
public class Student extends User {
public Student(String name, String identity, String clazz) {
super(name, identity, clazz);
}
public void accept(Visitor visitor) {
visitor.visit(this);
}
public int ranking() {
return (int) (Math.random() * 100);
}
}
- 這里實(shí)現(xiàn)了老師和學(xué)生類(lèi),都提供了父類(lèi)的構(gòu)造函數(shù)。
- 在
accept方法中,提供了本地對(duì)象的訪問(wèn);visitor.visit(this),這塊需要加深理解。 - 老師和學(xué)生類(lèi)又都單獨(dú)提供了各自的特性方法;升本率(
entranceRatio)、排名(ranking),類(lèi)似這樣的方法可以按照業(yè)務(wù)需求進(jìn)行擴(kuò)展。
2.3 定義訪問(wèn)數(shù)據(jù)接口
public interface Visitor {
// 訪問(wèn)學(xué)生信息
void visit(Student student);
// 訪問(wèn)老師信息
void visit(Teacher teacher);
}
- 訪問(wèn)的接口比較簡(jiǎn)單,相同的方法名稱(chēng),不同的入?yún)⒂脩?hù)類(lèi)型。
- 讓具體的訪問(wèn)者類(lèi),在實(shí)現(xiàn)時(shí)可以關(guān)注每一種用戶(hù)類(lèi)型的具體訪問(wèn)數(shù)據(jù)對(duì)象,例如;升學(xué)率和排名。
2.4 實(shí)現(xiàn)訪問(wèn)類(lèi)型(校長(zhǎng)和家長(zhǎng))
訪問(wèn)者;校長(zhǎng)
public class Principal implements Visitor {
private Logger logger = LoggerFactory.getLogger(Principal.class);
public void visit(Student student) {
logger.info("學(xué)生信息 姓名:{} 班級(jí):{}", student.name, student.clazz);
}
public void visit(Teacher teacher) {
logger.info("學(xué)生信息 姓名:{} 班級(jí):{} 升學(xué)率:{}", teacher.name, teacher.clazz, teacher.entranceRatio());
}
}
訪問(wèn)者;家長(zhǎng)
public class Parent implements Visitor {
private Logger logger = LoggerFactory.getLogger(Parent.class);
public void visit(Student student) {
logger.info("學(xué)生信息 姓名:{} 班級(jí):{} 排名:{}", student.name, student.clazz, student.ranking());
}
public void visit(Teacher teacher) {
logger.info("老師信息 姓名:{} 班級(jí):{} 級(jí)別:{}", teacher.name, teacher.clazz, teacher.identity);
}
}
- 以上是兩個(gè)具體的訪問(wèn)者實(shí)現(xiàn)類(lèi),他們都有自己的視角需求。
- 校長(zhǎng)關(guān)注;學(xué)生的名稱(chēng)和班級(jí),老師對(duì)這個(gè)班級(jí)的升學(xué)率
- 家長(zhǎng)關(guān)注;自己家孩子的排名,老師的班級(jí)和教學(xué)水平
2.5 數(shù)據(jù)看版
public class DataView {
List<User> userList = new ArrayList<User>();
public DataView() {
userList.add(new Student("謝飛機(jī)", "重點(diǎn)班", "一年一班"));
userList.add(new Student("windy", "重點(diǎn)班", "一年一班"));
userList.add(new Student("大毛", "普通班", "二年三班"));
userList.add(new Student("Shing", "普通班", "三年四班"));
userList.add(new Teacher("BK", "特級(jí)教師", "一年一班"));
userList.add(new Teacher("娜娜Goddess", "特級(jí)教師", "一年一班"));
userList.add(new Teacher("dangdang", "普通教師", "二年三班"));
userList.add(new Teacher("澤東", "實(shí)習(xí)教師", "三年四班"));
}
// 展示
public void show(Visitor visitor) {
for (User user : userList) {
user.accept(visitor);
}
}
}
- 首先在這個(gè)類(lèi)中初始化了基本的數(shù)據(jù),學(xué)生和老師的信息。
- 并提供了一個(gè)展示類(lèi),通過(guò)傳入不同的
觀察者(校長(zhǎng)、家長(zhǎng))而差異化的打印信息。
3. 測(cè)試驗(yàn)證
3.1 編寫(xiě)測(cè)試類(lèi)
@Test
public void test(){
DataView dataView = new DataView();
logger.info("\r\n家長(zhǎng)視角訪問(wèn):");
dataView.show(new Parent()); // 家長(zhǎng)
logger.info("\r\n校長(zhǎng)視角訪問(wèn):");
dataView.show(new Principal()); // 校長(zhǎng)
}
- 從測(cè)試類(lèi)可以看到,家長(zhǎng)和校長(zhǎng)分別是不同的訪問(wèn)視角。
3.2 測(cè)試結(jié)果
23:00:39.726 [main] INFO org.itstack.demo.design.test.ApiTest -
家長(zhǎng)視角訪問(wèn):
23:00:39.730 [main] INFO o.i.demo.design.visitor.impl.Parent - 學(xué)生信息 姓名:謝飛機(jī) 班級(jí):一年一班 排名:62
23:00:39.730 [main] INFO o.i.demo.design.visitor.impl.Parent - 學(xué)生信息 姓名:windy 班級(jí):一年一班 排名:51
23:00:39.730 [main] INFO o.i.demo.design.visitor.impl.Parent - 學(xué)生信息 姓名:大毛 班級(jí):二年三班 排名:16
23:00:39.730 [main] INFO o.i.demo.design.visitor.impl.Parent - 學(xué)生信息 姓名:Shing 班級(jí):三年四班 排名:98
23:00:39.730 [main] INFO o.i.demo.design.visitor.impl.Parent - 老師信息 姓名:BK 班級(jí):一年一班 級(jí)別:特級(jí)教師
23:00:39.730 [main] INFO o.i.demo.design.visitor.impl.Parent - 老師信息 姓名:娜娜Goddess 班級(jí):一年一班 級(jí)別:特級(jí)教師
23:00:39.730 [main] INFO o.i.demo.design.visitor.impl.Parent - 老師信息 姓名:dangdang 班級(jí):二年三班 級(jí)別:普通教師
23:00:39.730 [main] INFO o.i.demo.design.visitor.impl.Parent - 老師信息 姓名:澤東 班級(jí):三年四班 級(jí)別:實(shí)習(xí)教師
23:00:39.730 [main] INFO org.itstack.demo.design.test.ApiTest -
校長(zhǎng)視角訪問(wèn):
23:00:39.731 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:謝飛機(jī) 班級(jí):一年一班
23:00:39.731 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:windy 班級(jí):一年一班
23:00:39.731 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:大毛 班級(jí):二年三班
23:00:39.731 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:Shing 班級(jí):三年四班
23:00:39.733 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:BK 班級(jí):一年一班 升學(xué)率:70.62
23:00:39.733 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:娜娜Goddess 班級(jí):一年一班 升學(xué)率:23.15
23:00:39.734 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:dangdang 班級(jí):二年三班 升學(xué)率:70.98
23:00:39.734 [main] INFO o.i.d.design.visitor.impl.Principal - 學(xué)生信息 姓名:澤東 班級(jí):三年四班 升學(xué)率:90.14
Process finished with exit code 0
- 通過(guò)測(cè)試結(jié)果可以看到,家長(zhǎng)和校長(zhǎng)的訪問(wèn)視角同步,數(shù)據(jù)也是差異化的。
- 家長(zhǎng)視角看到學(xué)生的排名;
排名:62、排名:51、排名:16、排名:98。 - 校長(zhǎng)視角看到班級(jí)升學(xué)率;
升學(xué)率:70.62、升學(xué)率:23.15、升學(xué)率:70.98、升學(xué)率:90.14。 - 通過(guò)這樣的測(cè)試結(jié)果,可以看到訪問(wèn)者模式的初心和結(jié)果,在適合的場(chǎng)景運(yùn)用合適的模式,非常有利于程序開(kāi)發(fā)。
六、總結(jié)
- 從以上的業(yè)務(wù)場(chǎng)景中可以看到,在嵌入訪問(wèn)者模式后,可以讓整個(gè)工程結(jié)構(gòu)變得容易添加和修改。也就做到了系統(tǒng)服務(wù)之間的解耦,不至于為了不同類(lèi)型信息的訪問(wèn)而增加很多多余的
if判斷或者類(lèi)的強(qiáng)制轉(zhuǎn)換。也就是通過(guò)這樣的設(shè)計(jì)模式而讓代碼結(jié)構(gòu)更加清晰。 - 另外在實(shí)現(xiàn)的過(guò)程可能你可能也發(fā)現(xiàn)了,定義抽象類(lèi)的時(shí)候還需要等待訪問(wèn)者接口的定義,這樣的設(shè)計(jì)首先從實(shí)現(xiàn)上會(huì)讓代碼的組織變得有些難度。另外從設(shè)計(jì)模式原則的角度來(lái)看,違背了迪米特原則,也就是最少知道原則。因此在使用上一定要符合場(chǎng)景的運(yùn)用,以及提取這部分設(shè)計(jì)思想的精髓。
- 好的學(xué)習(xí)方式才好更容易接受知識(shí),學(xué)習(xí)編程的更需要的不單單是看,而是操作。二十多種設(shè)計(jì)模式每一種都有自己的設(shè)計(jì)技巧,也可以說(shuō)是巧妙之處,這些巧妙的地方往往是解決復(fù)雜難題的最佳視角。親力親為,才能為所欲為,為了自己的欲望而努力!
七、推薦閱讀
1. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)工廠方法模式「多種類(lèi)型商品不同接口,統(tǒng)一發(fā)獎(jiǎng)服務(wù)搭建場(chǎng)景」2. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)原型模式「上機(jī)考試多套試,每人題目和答案亂序排列場(chǎng)景」3. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)橋接模式「多支付渠道(微信、支付寶)與多支付模式(刷臉、指紋)場(chǎng)景」4. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)組合模式「營(yíng)銷(xiāo)差異化人群發(fā)券,決策樹(shù)引擎搭建場(chǎng)景」5. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)外觀模式「基于SpringBoot開(kāi)發(fā)門(mén)面模式中間件,統(tǒng)一控制接口白名單場(chǎng)景」6. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)享元模式「基于Redis秒殺,提供活動(dòng)與庫(kù)存信息查詢(xún)場(chǎng)景」7. 重學(xué) Java 設(shè)計(jì)模式:實(shí)戰(zhàn)備忘錄模式「模擬互聯(lián)網(wǎng)系統(tǒng)上線過(guò)程中,配置文件回滾場(chǎng)景」