一位10年Java工作經驗的架構師聊Java和工作經驗

黃勇( 博客),從事近十年的 JavaEE 應用開發工作,現任阿里巴巴公司系統架構師。對分佈式服務架構與大數據技術有深入研究,具有豐富的 B/S 架構開發經驗與項目實戰經驗,擅長敏捷開發模式。國內開源軟件推動者之一,Smart Framework 開源框架創始人。熱愛技術交流,樂於分享自己的工作經驗。著有《架構探險——從零開始寫Java Web框架》一書。

我的十年技術之路

CSDN:請和大家介紹下你和目前所從事的工作。

黃勇:大家好,我是黃勇。

我目前從事分佈式服務架構的設計與開發工作,在阿里的大數據平臺上進行應用程序開發。我們整個系統架構採用了“前後端分離”的思想,前端關注數據展現,後端關注數據生產,通過 REST服務將前後端整合起來,所有的應用都是無狀態的,可以做到水平擴展。我們將整個系統拆分成許多“微服務”,服務之間通過統一的接口來調用,每個服務是通過容器技術進行隔離,此外服務可發佈到統一的服務管理平臺上,可通過該平臺監控每個服務的運行狀態與生命週期事件,併爲服務調用者提供了服務發現的能力,可對服務進行平滑升級。

阿里有許多優秀的中間件與基礎服務,可以快速幫助我們搭建應用系統,而且這些技術在阿里內部全是開源的,大家可以通過源碼和文檔學習到很多有價值的經驗。阿里也提供了濃厚的技術氛圍,每位同學都非常專注於自己的工作領域,大家對工作一絲不苟,相互配合,方向一致。

CSDN:你是如何走上技術這條路的?

黃勇:2006 年大學畢業,我離開了母校武漢理工大學,在院長薛勝軍老師的推薦下,我來到了上海,這個對於我來說非常陌生的地方。我有幸加入了一家名爲“動量軟件”的創業公司,這家公司的老闆曾經是亞信科技的 CTO,他也是普元軟件的創始人兼 CTO,他的名字叫黃柳青,他也是薛老師的大學同學。於是就這樣,我的老闆成爲了我的老師,我習慣叫他黃老師,包括公司其他資深的同事也成爲了我的老師,因爲我很想他們身上學到更多有價值的東西。

剛開始工作的時候我學習了什麼是雲計算?什麼是 SaaS、PaaS、IaaS?我們花了三年時間開發了一款名爲 ODE 的 PaaS 平臺,讓用戶可以在該平臺上量身定製自己的軟件,最終爲客戶提供基於 SaaS 的產品。確實很驕傲,那時我們已經在做雲了,只是沒想到後來雲會在中國得到這麼好的市場,可能當時只有黃老師一個人想到了吧。

在 2008 年,我爲公司拿回了“第一桶金”,這也是我從程序員轉向項目經理的里程碑。當時我帶領團隊遠赴深圳,爲國信證券公司開發經紀人管理系統,這個項目對於我個人而言卻是一筆至高無上的財富,我開始學習如何與人打交道,如何做需求分析,如何將需求轉變爲技術,如何帶領團隊小夥伴一起工作。學到了太多太多,但我依然選擇在我工作第四個年頭裏離開了動量軟件,我剛加入動量軟件的時候,公司只有 5 個人(包括老闆和前臺),當我離開動量軟件的時候,公司已經有 200 人左右了。感謝黃老師!我在他身上學到了很多,他的思想和態度直到今天都還在影響着我。

我的第二份工作還是選擇了我最熟悉的證券金融行業,同樣也是一家創業型公司,在這家公司裏我擔任了技術經理,管理了整個技術團隊,從項目的售前到售後,我都親自帶領團隊來完成。雖然在這家公司我只做了兩年,但在這短短的時間裏,我學會了如何提高開發效率、如何培養技術團隊、如何選拔技術人才、如何建立企業文化。但最後我發現了一個問題,越是想做好,越是很難做好,爲了做成一件事情需要做很多的嘗試,做事情缺乏正確並有效的方法。

回想我工作的前六年時間裏,我一直都是在創業公司裏成長,雖然可以快速學到東西,但似乎很難學到更加規範的做事方法。於是我選擇了新的工作機會,來到了 TCL 通訊,這是一家相當大的公司,公司的研發管理流程來源於法國阿里卡特公司。我在公司擔任 Java 架構師職位,也算是整個 Java 團隊的技術負責人,雖然團隊並不是特別地大。我在這家公司做了三年,學到了如何整合現有資源、如何按標準流程去做事、如何設計系統架構、如何進行異地工作、如何跨團隊工作、如何用英文來溝通。說實話,當時我沒有任何的工作壓力,可以按時上下班,從來都不會加班。雖然自己空閒的時間很多,但我並沒有選擇去浪費時間,而是開始寫點技術博客,也正是因爲這些技術文章,才改變了我後續的職業發展道路。

我清楚的記得,那是在 2013 年 9 月 1 日,我在開源中國(oschina.NET)網站發表了我人生的第一篇博文 《Smart Framework:輕量級 Java Web 框架》 ,這篇文章影響了我後續兩年。其實說句心裏話,當我第一次寫這篇文章時,我心裏是沒底的,這個框架只是根據自己的理解做出來的一個設想,當時甚至連一行代碼都沒寫過。我的想法是先將這個思想發表出來,讓大家討論起來,我會做一個決策,然後再親自做具體實現,最後我會將實現過程通過博文的方式展現給大家,後續大家會對我的實現進行點評,我會基於大家的建議進行改善。整個開源過程正好與敏捷的思想是一致的,有效溝通、小步快跑、擁抱變化、不斷改進。

也許就是我的技術文章吸引了很多廣大讀者,這裏面不排除想邀請我加入的其它公司。我在 2014 年離開了 TCL 通訊,加入了易傳媒。爲什麼我要放棄如此舒適的工作環境,去加入一家還在不斷拼搏的企業呢?其實我看到的是未來互聯網的發展趨勢,廣告程序化交易以及廣告與大數據的結合,未來最值錢的一定是數據。抱着這樣的信心,我加入了易傳媒,擔任系統架構師職位。當時易傳媒正處於技術轉型的初期,需要將 .Net 全部遷移到 Java,這件事情對於我而言是非常有挑戰的。我的做法是:第一步定義開發規範與流程,第二步培養核心技術人員,第三步分階段進行改造。僅半年時間,我們所有的產品成功地遷移到了 Java 平臺,結果出乎大家的想象。公司市場也非常不錯,產品得到了業界的認可,訂單數源源不斷,大家每天都很忙碌,但卻很開心。而易傳媒的“易家人”企業文化,讓我所感動,不管是核心技術部門還是其它支持性部門,大家就像一家人一樣,你的事情就是我的事情。

直到 2015 年初,阿里巴巴與易傳媒建立了合作關係,兩家公司進行了深度合作,易傳媒公司與阿里媽媽事業部進行了整合,新阿里媽媽從此誕生了,於是我也成爲了阿里巴巴的一員,目前負責阿里媽媽大數據品牌營銷產品的系統架構工作。就在兩家公司整合的過程中,我完成了人生中的處女作《架構探險 —— 從零開始寫 Java Web 框架》這本書,目前該書正在各大網上書店售賣,我真心希望這本書能對一些想成爲架構師的程序員們有所幫助,由於我個人水平有限,又是第一次寫書,寫得不好的地方還請大家多多包涵。

CSDN:上面提到,寫博客給你帶來的收穫頗多,能不能分享下技術人如何寫博客?又應該以怎樣的態度對待?

黃勇:我認爲技術人員寫博客需要注意以下幾點:

  1. 思路要清晰,文章要有明確的大綱與標題。
  2. 對於實戰類型的文章,需要分步驟來描述。
  3. 多用短句,少用長句,能一句話說明白,就不用兩句話。
  4. 對於不太好理解的內容,最好能打比方來說明。
  5. 文章末尾需要有總結,用最精闢的語言歸納出這篇文章的主要內容。

寫博客首先是對自己所學知識的一個總結,此外,也爲其他讀者提供了很好的教程,知識得到了廣播與傳遞。

CSDN:技術一條不歸路,選擇了這條路是否有過放棄的想法?

黃勇:做了十年的技術,我從來都沒有放棄過它,相反,我非常熱愛它,因爲我一直以來都很喜歡學習,希望能學到更多的東西,這樣遇到了具體的技術問題,可以隨時從自己積累的知識庫中找到最佳的解決方案。此外,目前我在公司雖然不怎麼寫代碼了,但我還是會利用自己工作閒暇之餘寫一點開源項目或者代碼框架等。

CSDN:你工作過很多大大小小的公司,你認爲公司最值錢的東西是什麼?

黃勇:我認爲是實實在在做事情的程序員們。

他們雖然工資不高,每天坐在位置上敲着代碼,在很多人眼中被稱爲“屌絲”或“宅男”,但我認爲恰恰就是這些人,他們纔是公司最有價值的人。

  • 他們有自己的理想,希望能夠通過自己的努力,從中得到那一點點所謂的成就感;
  • 他們需要理解產品經理真正的意圖,把想法變成現實,讓產品真正落地;
  • 他們更容易把握細節,而這些細節往往決定着產品的命運與成敗;
  • 他們突如其來的跳槽,對我們的項目的交付有直接的影響;
  • 他們在一起工作的氣氛,能體現技術公司的文化與底蘊。

由此看來,對程序員的重視是相當有必要的,我們需要關心每一位程序員的職業發展,讓他們在團隊裏能夠充分地發揮出自己的能力。

我們也需要對他們倍加關注,挖掘出有能力、肯吃苦、敢擔當的人,給他們更多的機會,讓他們成爲技術領袖。

互聯網技術公司需要大量這樣的程序員:

  • 他們是一羣有着技術信仰的人,他們是一羣熱愛編程的人,他們是一羣不解決問題睡不好覺的人;
  •  他們不是打雜的,不是外包,更不是工具;
  • 他們不喜歡被忽悠,不喜歡被冷落,更不喜歡被驅動;
  •  他們需要尊重,需要培養,更需要激情!

CSDN:你能具體說說程序員需要具備哪些素質嗎?

黃勇:我個人是這樣理解真正的程序員的:

  1. 深愛技術,一天不寫代碼手就會癢,就喜歡那種成就感;
  2. 爲了一個問題可以廢寢忘食,有時會在夢中都能寫代碼;
  3. 代碼潔癖症患者,喜歡優雅代碼,寫代碼就像寫詩一樣;
  4. 善於分析問題,能快速看清問題的本質,並動手解決它;
  5. 喜歡研究優秀源碼,學習大師的傑作,善於歸納與總結;
  6. 有自己的開源項目或技術博客,喜歡學習,更喜歡分享;
  7. 會關注技術圈子的新聞動態,時常會參加線下技術沙龍;
  8. 知道軟件開發不是一個人在戰鬥,更需要的是團隊協作;
  9. 保持良好健康的心態,用一顆積極向上的心去擁抱變化。

CSDN:十年的職場之路堅持不易,能夠分享下你的「IT 職場」經驗?

黃勇:時光飛逝,我事業中第一個十年已然結束了。在這十年裏,讓我收穫了很多,跟大家分享一下我在 IT 職場方面的一些個人經驗,不一定對每個人都實用,請大家僅作參考吧。

大家既然都是做技術的,那我們不妨先從技術這個話題開始說起吧。我要與大家分享的第一點經驗就是:

1. 把技術當成工具

技術這東西,其實一點都不神祕,它只不過是一個工具,用這個工具可以幫助我們解決實際問題,就這麼簡單。

我們每天在面對技術,市面上也有很多技術,真的沒有必要把這些技術都拿過來學習一遍,然後想辦法找個場景去應用它。如果真的這樣做了,那麼只能說明技術不是工具,而是玩具,技術不是這樣玩的。

我們應該從另一個角度來看待技術,不妨從自己的實際工作環境出發,現在需要什麼,我們就學什麼,而不要漫無目的的追求一些新技術。當然,對於新技術還是需要有所關注的,至少需要知道這個新技術是幹什麼用的,而且還要善於總結,將有價值的技術收集起來,以備將來使用,當需要使用的時候再來深入研究。

人的精力是有限的,人的生命也是短暫的,要善於利用自己的時間,合理地學習技術。

不要把技術看得那麼重要,別把它當回事兒,把它當工具就行了,它就像我們寫字的筆一樣,用鉛筆能寫字,用鋼筆一樣能寫字。

作爲一名技術人員,除了學習與應用技術以外,還需要爲自己做一個正確的職業規劃,清晰認識自己究竟屬於哪種技術人才,是技術專家類型的,還是技術管理類型的。路到底該怎麼走?需要自己做出決定。

在我們職業路線上,最重要的人莫過於老闆(我指的老闆可以是公司大老闆,也可以是自己的頂頭上司),對待自己的老闆,我也有一些經驗:

2. 把老闆當成情人

大家應該非常清楚,情人是需要浪漫的,浪漫是需要驚喜的。老闆其實跟情人一樣,也是需要驚喜的。我們做下屬的,要懂得找到合適的機會給老闆帶來驚喜。我們跟情人談情說愛,這是一種很好的溝通方式,可別忽略了跟老闆“談情說愛”,我們需要與老闆保持良好的溝通,這種溝通並不僅僅是溜鬚拍馬。

講一個真實的故事吧。記得曾經我的一位同事,技術非常好,做東西非常快,質量也很高,同事們都覺得他是牛人,但他從來都不懂得在老闆面前表現自己,老闆也只是覺得他是可以做事的,但升職加薪的事情往往總是不會優先考慮他。

大家很定會問:怎樣在老闆面前表現自己呢?其實方法有很多,由於篇幅有限,我先提供三招吧:

  • 第一招:在給老闆做程序演示的時候,不要只是單純的演示,不妨先用一個 PPT,簡單表達一下自己的解決方案,然後再做演示,這樣效果會好很多。老闆會認爲自己是花了心思的,是想把事情做得更好的。
  • 第二招:把自己每天的工作簡單記錄一下,每週彙總一次,以郵件的形式發送給老闆,讓老闆知道自己每天在做什麼。每月寫一篇本月工作總結與下月工作計劃,同樣發郵件給老闆。年底可以寫一個年終工作總結,打印出來,悄悄地放在老闆的桌子上。
  • 第三招:借彙報工作爲理由,定期請老闆出去吃飯,製造面對面單獨溝通的機會。在談話過程中,強調自己願意幫助老闆分擔工作壓力。

對待老闆其實很簡單,只要能幫他做事,又能讓他開心,他基本上就搞定了。老闆搞定了,自己的職業發展纔會平步青雲。但千萬別忽略了還有一羣人,他們或許是自己的團隊戰友,或許是自己的競爭對手,沒錯!他們就是同事。如何處理同事關係呢?以下便是我的經驗:

3. 把同事當成小孩

處理與同事關係,其實比處理與老闆關係要稍微複雜一點,因爲同事有多種身份,他們可以是隊友,也可以是對手。如果大家在一起做同一個項目,那麼這樣的同事就是隊友;如果爲了競爭某個項目、崗位、資源,導致同級別的同事之間發生利益上的競爭,那麼這樣的同事就是對手。

對於隊友而言,要學會主動給他們提供幫助,讓大家能夠體會到團隊協作的氣氛,在一起學習,在一起成長,在一起分享。可以時常跟大家一起聚餐,買點零食讓大家品嚐。

隊友關係往往比較好處理,關鍵在於自己能否真正懂得去分享。很多技術人員,最不願意的就是分享,因爲擔心自己花了很多精力學到的知識,分分鐘就被別人學會了,自己失去了優勢。這種心態最好不要在團隊裏產生,這樣只會讓自己變得越來越封閉,越來越渺小,隊友們也會逐漸排擠自己。

對於對手而言,要想辦法讓自己成爲他的兄弟,告訴他,咱們是兄弟,應該相互幫助。如果有機會,可以在老闆面前,當着對手的面,誇獎自己的對手。做出這樣的行爲,其實並不會讓老闆覺得自己不如對手,而會讓老闆認爲自己在用心去容納對手。大家在一起工作,就是一種緣分,都是跟老闆打工的,真的沒有必要搞得不愉快。

其實同事就是自己的小夥伴,不妨把他們當成是單純可愛的小孩吧,用自己的心去“收買”他們。

老闆與同事,他們都是公司內部的人,不管怎麼說,大家都在同一條船上,大家可以關上門吵一架,只要事情能夠解決就行。但對於我們的客戶而言,就需要用另外一種方法來處理好關係了。我是這樣認爲的:

4. 把客戶當成病人

客戶有需求,但沒有技術,而我們有技術、有經驗、有產品,正好可以幫助他們實現需求,從而提高他們的工作效率,這樣客戶纔會心甘情願地把錢放入我們的口袋。所以,在客戶面前,我們要表現出高超的專業精神,不要被客戶牽着我們的鼻子走,我們在客戶面前就是技術權威,就需要這樣的自信。從服裝、言行、郵件、文檔等各個方面,都要做到專業。

我們打算把自己的產品賣給客戶的時候,千萬不要一上來就對自己的產品誇誇其談,這往往會讓客戶感到厭煩。我們不妨先告訴客戶,他們已經“生病”了,而且病得不輕,如果不及時用藥的話,後果將不堪設想。也就是說,要讓客戶意識到自己現在所面臨的困境,讓客戶緊張,當他們正在思考如何應對的時候,我們再告訴他們,“藥”已經準備好了,可以隨時服用。

要讓客戶有種雪中送炭的感覺,這樣就對了,他們一定會主動了解我們的產品。我們要做到這一切,必須花精力來分析行業現狀,揣測客戶老闆們每天在想什麼。如果有機會進入客戶所在的公司工作一段時間,相信自己的感受會更加深入。

Java 會在很長的一段時間內是主流

CSDN:能否先簡答介紹下你的最新力作《架構探險——從零開始寫Java Web框架》?面向的羣體是怎樣的?有什麼特別之處?

黃勇:建議有一定 Java Web 開發經驗的讀者閱讀這本書,當然,如果大家想通過這本書來學習 Java Web 核心技術也是非常不錯的,因爲書中會有大量的實例來講解 Java 必備的基礎技能。此外,建議讀者們能親自動手去實踐,雖然書中所有的源代碼可以自由獲取,但我不建議大家只是看看代碼是怎麼寫的,而錯過了一次很好的練手機會,因爲所有的開發技能都需要不斷地練習,孰能生巧,巧能生輝。

CSDN:《架構探險——從零開始寫Java Web框架》是你撰寫的第一技術書,是什麼原因促使你寫這本的?

黃勇:記得那是在 2014 年 11 月底,我有幸結識了電子工業出版社博文視點編輯部的陳曉猛老師。陳老師建議我寫一本書,但我當時真的不知道該寫什麼,我想可能在 Java Web 方面還可以嘗試寫點東西吧,於是在他的鼓勵與幫助下,我就開始寫書了。陳老師告訴我,寫書其實就像寫博客一樣,當初我真這麼覺得的,可是個人能力和經驗還是非常有限,第一次寫了 50 頁就再也寫不下去了,第二次竟然寫到了 100 頁,最後覺得自己的寫作思路有問題,還是放棄了,直到第三次我才把結構梳理清楚,一氣呵成地寫完了整本書。在這個過程中,是我妻子鼓勵並監督着我,那時我們的寶寶剛出生不久,每天在家裏哭泣,我妻子把我一個人關在房間裏,她獨自一人帶小孩,並操持着所有的家務,就是爲了給我一個安靜的環境,讓我可以敞開思路,寫出更加優秀的作品。在此,請允許我對我妻子說一聲:辛苦了!我永遠愛你!

CSDN:寫書不是一件容易的事情,能不能談談在這段期間的辛酸和收穫?

黃勇:雖然寫書的過程比較艱辛,但對我個人卻有很大的收穫:

  1. 通過寫書這件事情,讓我學會了堅持,想做一件事情很簡單,但想把這件事情做成卻沒那麼容易。
  2. 通過寫書我重新對輕量級 Java Web 框架做更深層次的理解,一個好的框架不是看功能有多強大,而在於它的擴展性有多好。畢竟功能是做不完的,需要有一個“微內核 + 多插件”的思想,核心是非常小的,它僅提供了整個框架的必備功能以及相關的擴展點,然後需要將不同的功能封裝在不同的插件中,併爲更多其他的開發者提供統一的擴展方式。
  3. 我希望這本書不再是教會讀者如何去使用開源框架,而是讓讀者學會如何從零開始去編寫開源框架,並鼓勵讀者發揮自己的力量,一起投身到開源社區中。

CSDN:爲什麼開發Java Web都要用框架?

黃勇:我個人覺得框架有以下幾點作用:

  1. 讓開發更加高效,屏蔽底層技術細節,讓開發人員關注在具體業務上。
  2. 框架實際上也是一種規範,可以讓每位開發人員保持同樣的編碼風格。
  3. 會使用主流框架的開發人員,在人才市場上比較好獲取。

CSDN:現在做Java Web開發都用哪些框架呢?

黃勇:常用的比如spring MVC、Struts2 等,國內的 JFinal、Nutz 等也不錯,當然Smart 也是一個很好的選擇。

CSDN:有一定Web前端開發經驗的人,很多都會有這麼個想法:那些寫框架的人好厲害,什麼時候我才能寫一個自己的框架呢?有時候看看別人的框架代碼,又覺得很複雜,對此你有什麼建議嗎?以及新人學習需要什麼基礎?有哪些好的方法分享?

黃勇:對於接觸 Java 不太久的朋友,建議按照以下幾個步驟來學習:

  1. 學習 Java 基礎語法與核心技術,包括 Servlet、JSP、JDBC 等。
  2. 熟練使用流行開源框架,包括Spring、MyBatis 等。
  3. 研究開源框架源碼,並吸取其中優秀的架構。

此外,在學習的過程當中,建議做學習筆記,最好能通過博客的方式來記錄自己的收穫。

CSDN:使用 Python、Perl、PHP、Ruby 等腳本語言開發 Web 程序,跟使用 Java 開發 Web 程序相比有什麼不同或者優劣?

黃勇:前者屬於動態語言,無需編譯,可通過解釋的方式來運行,而且 Java 需要首先通過編譯,將源文件轉爲字節碼,且載入 Java 虛擬機才能運行,相對來說,Java 對環境的要求較高,但 Java 具備更強的面向對象能力。此外,Java 還擁有較廣的開源社區以及流行的開源中間件。因此,如果是做大型系統,建議使用 Java 來開發,而並非那些腳本語言。

CSDN:針對 Web,Java、PHP、Python、.NET 之中未來發展前景最好的會是什麼?

黃勇:我認爲 Java 在未來還會有一段很長的路,需要在語言本身上做到更加輕量級,用最少的代碼來實現目標功能;PHP 相對來說會比較平穩,它的特點非常突出,上手快且易於開發 Web 項目;Python仍然不會有太大的用戶羣體;.NET 加入開源社區太晚,且較 Java 而言並沒有太強的優勢,可能會走下坡路。

CSDN:在軟件開發中有很多的設計模式,也有一些很高冷,能否談談你對軟件設計的理解,以及讓一些設計原則接地氣?

黃勇:瞭解設計模式的朋友們,想必都聽說過“六大設計原則”吧。其實最經典的 23 種設計模式中或多或少地都在使用這些設計原則,也就是說,設計模式是站在設計原則的基礎之上的。所以在學習設計模式之前,很有必要對這些設計原則先做一下了解。

GoF(四人幫),傳說中的四位大神們,他們聯手搞出了一套設計模式,堪稱 OOD(面向對象設計)的經典之作!震驚了整個軟件開發領域。但這四個老傢伙非常怪異,總是喜歡顯擺一些高深的理論,甚至有時候不說人話,十分讓人費解。

除了最經典的六大設計原則以外,還有一些其他的設計原則也非常重要。我將儘可能地解釋這些晦澀的理論,希望看完之後,會讓您對這些設計原則稍微加深一些理解。若有不正確的地方,懇請大家指正!

  • 六大設計原則

先看一幅圖吧:


這幅圖清晰地表達了六大設計原則,但僅限於它們叫什麼名字而已,它們具體是什麼意思呢?下面我將從原文、譯文、理解、應用,這四個方面分別進行闡述。

1. 單一職責原則(Single Responsibility Principle - SRP)

原文:There should never be more than one reason for a class to change.

譯文:永遠不應該有多於一個原因來改變某個類。

理解:對於一個類而言,應該僅有一個引起它變化的原因。說白了就是,不同的類具備不同的職責,各施其責。這就好比一個團隊,大家分工協作,互不影響,各做各的事情。

應用:當我們做系統設計時,如果發現有一個類擁有了兩種的職責,那就問自己一個問題:可以將這個類分成兩個類嗎?如果真的有必要,那就分吧。千萬不要讓一個類乾的事情太多!

2. 開放封閉原則(Open Closed Principle - OCP)

原文:Software entities like classes, modules and functions should be open for extension but closed for modifications.

譯文:軟件實體,如:類、模塊與函數,對於擴展應該是開放的,但對於修改應該是封閉的。

理解:簡言之,對擴展開放,對修改封閉。換句話說,可以去擴展類,但不要去修改類。

應用:當需求有改動,要修改代碼了,此時您要做的是,儘量用繼承或組合的方式來擴展類的功能,而不是直接修改類的代碼。當然,如果能夠確保對整體架構不會產生任何影響,那麼也沒必要搞得那麼複雜了,直接改這個類吧。

3. 里氏替換原則(Liskov Substitution Principle - LSP)

原文:Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it.

譯文:使用基類的指針或引用的函數,必須是在不知情的情況下,能夠使用派生類的對象。

理解:父類能夠替換子類,但子類不一定能替換父類。也就是說,在代碼中可以將父類全部替換爲子類,程序不會報錯,也不會在運行時出現任何異常,但反過來卻不一定成立。

應用:在繼承類時,務必重寫(Override)父類中所有的方法,尤其需要注意父類的 protected 方法(它們往往是讓您重寫的),子類儘量不要暴露自己的 public 方法供外界調用。

該原則由麻省理工學院的 Barbara Liskov 女士提出,她是美國第一位獲取計算機博士學位的女性,曾經也獲得過計算機圖靈獎。

4. 最少知識原則(Least Knowledge Principle - LKP)

原文:Only talk to you immediate friends.

譯文:只與你最直接的朋友交流。

理解:儘量減少對象之間的交互,從而減小類之間的耦合。簡言之,一定要做到:低耦合,高內聚。

應用:在做系統設計時,不要讓一個類依賴於太多的其他類,需儘量減小依賴關係,否則,您死都不知道自己怎麼死的。

該原則也稱爲“迪米特法則(Law of Demeter)”,由 Ian Holland 提出。這個人不太願意和陌生人說話,只和他走得最近的朋友們交流。

5. 接口隔離原則(Interface Segregation Principle - ISP)

原文:The dependency of one class to another one should depend on the smallest possible interface.

譯文:一個類與另一個類之間的依賴性,應該依賴於儘可能小的接口。

理解:不要對外暴露沒有實際意義的接口。也就是說,接口是給別人調用的,那就不要去爲難別人了,儘可能保證接口的實用性吧。她好,我也好。

應用:當需要對外暴露接口時,需要再三斟酌,如果真的沒有必要對外提供的,就刪了吧。一旦您提供了,就意味着,您將來要多做一件事情,何苦要給自己找事做呢。

6. 依賴倒置原則(Dependence Inversion Principle - DIP)

原文:High level modules should not depends upon low level modules. Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions.

譯文:高層模塊不應該依賴於低層模塊,它們應該依賴於抽象。抽象不應該依賴於細節,細節應該依賴於抽象。

理解:應該面向接口編程,不應該面向實現類編程。面向實現類編程,相當於就是論事,那是正向依賴(正常人思維);面向接口編程,相當於通過事物表象來看本質,那是反向依賴,即依賴倒置(程序員思維)。

應用:並不是說,所有的類都要有一個對應的接口,而是說,如果有接口,那就儘量使用接口來編程吧。

將以上六大原則的英文首字母拼在一起就是 SOLID(穩定的),所以也稱之爲 SOLID 原則。

只有滿足了這六大原則,才能設計出穩定的軟件架構!但它們畢竟只是原則,只是四人幫給我們的建議,有些時候我們還是要學會靈活應變,千萬不要生搬硬套,否則只會把簡單問題複雜化,切記!

  • 補充設計原則

1. 組合/聚合複用原則(Composition/Aggregation Reuse Principle - CARP)

當要擴展類的功能時,優先考慮使用組合,而不是繼承。這條原則在 23 種經典設計模式中頻繁使用,如:代理模式、裝飾模式、適配器模式等。可見江湖地位非常之高!

2. 無環依賴原則(Acyclic Dependencies Principle - ADP)

當 A 模塊依賴於 B 模塊,B 模塊依賴於 C 模塊,C 依賴於 A 模塊,此時將出現循環依賴。在設計中應該避免這個問題,可通過引入“中介者模式”解決該問題。

3. 共同封裝原則(Common Closure Principle - CCP)

應該將易變的類放在同一個包裏,將變化隔離出來。該原則是“開放-封閉原則”的延生。

4. 共同重用原則(Common Reuse Principle - CRP)

如果重用了包中的一個類,那麼也就相當於重用了包中的所有類,我們要儘可能減小包的大小。

5. 好萊塢原則(Hollywood Principle - HP)

好萊塢明星的經紀人一般都很忙,他們不想被打擾,往往會說:Don't call me, I'll call you. 翻譯爲:不要聯繫我,我會聯繫你。對應於軟件設計而言,最著名的就是“控制反轉”(或稱爲“依賴注入”),我們不需要在代碼中主動的創建對象,而是由容器幫我們來創建並管理這些對象。

  • 其他設計原則

1. 不要重複你自己(Don't repeat yourself - DRY)

不要讓重複的代碼到處都是,要讓它們足夠的重用,所以要儘可能地封裝。

2. 保持它簡單與傻瓜(Keep it simple and stupid - KISS)

不要讓系統變得複雜,界面簡潔,功能實用,操作方便,要讓它足夠的簡單,足夠的傻瓜。

3. 高內聚與低耦合(High Cohesion and Low Coupling - HCLC)

模塊內部需要做到內聚度高,模塊之間需要做到耦合度低。

4. 慣例優於配置(Convention over Configuration - COC)

儘量讓慣例來減少配置,這樣才能提高開發效率,儘量做到“零配置”。很多開發框架都是這樣做的。

5. 命令查詢分離(Command Query Separation - CQS)

在定義接口時,要做到哪些是命令,哪些是查詢,要將它們分離,而不要揉到一起。

6. 關注點分離(Separation of Concerns - SOC)

將一個複雜的問題分離爲多個簡單的問題,然後逐個解決這些簡單的問題,那麼這個複雜的問題就解決了。難就難在如何進行分離。

7. 契約式設計(Design by Contract - DBC)

模塊或系統之間的交互,都是基於契約(接口或抽象)的,而不要依賴於具體實現。該原則建議我們要面向契約編程。

8. 你不需要它(You aren't gonna need it - YAGNI)

不要一開始就把系統設計得非常複雜,不要陷入“過度設計”的深淵。應該讓系統足夠的簡單,而卻又不失擴展性,這是其中的難點。 

敏捷開發模式的修煉之道

CSDN:請問你是如何接觸到敏捷開發的?你如何理解敏捷開發?

黃勇:曾經我們開發項目都是採用傳統的“瀑布式”流程進行開發,即需求、設計、開發、測試、上線等階段,其中每個階段都有明確的交付時間點,且每個階段都依賴於它的上個階段,一旦需求有變化,就會影響後續的每個階段,項目管理存在一定的風險。爲了避免這個風險,做到更好地擁抱變化,我們嘗試使用了敏捷開發方法,最爲典型的是 Scrum。我們參考Scrum 的流程結合自身的特點,總結了一套更容易落地的Scrum,後面我會跟大家講到一些相關細節。

我理解的敏捷開發實際上是一個輕量級的項目管理規範,因爲我們可以將整個大的需求範圍拆分成若干迭代週期,我們爲這些迭代週期設置明確的里程碑,且評估完成這些功能需要花費的成本,更重要的是,每次迭代完成以後,我們會對本次迭代進行一個回顧,取其精華,去其糟粕,不斷完善,持續改進。

CSDN:你認爲國內的敏捷開發何時能成爲主流?敏捷開發的未來走向是什麼?

黃勇:我認爲敏捷開發現在已經成爲了主流,傳統開發模式已經出現了明顯的缺陷,隨着互聯網的發展,軟件開發的節奏會越來越快,變化也會越來越頻繁,需要我們能夠快速地發現變化,並進行及時地調整。

我認爲敏捷開發的未來會變得更好,不僅僅在軟件開發行業,而且可能會在其它行業裏也會得到應用,因爲從客戶的角度來看,他們想要的是能通過最短的時間看到自己想要的東西,很多時候不做出一點東西出來,客戶是沒有任何想法的,所以需要將事情分解成多階段,迭代完成每個階段的里程碑,讓客戶滿意,纔是企業最大的收穫。

CSDN:在你的工作生涯中,前期是在創業公司,後來是大公司,有着一套自己的敏捷開發模式,能夠談談在你現在使用的敏捷開發工具或方法?

黃勇:敏捷這個話題大家一直都在談論,也有很多關於敏捷的工具或方法,我個人比較傾向於 Scrum。我理解的敏捷其實是一種思想,Scrum 是對讓這個思想落地的一個參考。也就是說,我們大可不必完全拘泥於 Scrum 定義的規範,只需要參考它並結合自身的條件做適當調整即可。比如說,每日站會這個環節就非常重要,不管是放在每天上午,還是放在每天下午,總之最好要有固定的週期。此外,每次 Sprint(迭代)結束後除了有評審會以外,Scrum Master 不要忘記對本次 Sprint 做一個回顧與總結,哪些是本次迭代中做的好的地方,哪些是做的不好的,再對比上次迭代的的結論,哪些是有改進的,哪些是新的問題。

Scrum 提供了三類角色,分別是:Product Owner(一般由產品經理擔任)、Scrum Master(一般由開發經理擔任)、Scrum Team(包括開發與測試人員),其中,Scrum Master 的角色至關重要,對項目的成敗起決定性作用。

阿里巴巴也在廣泛使用 Scrum 敏捷開發模式,而且整個項目幾十人都可以用 Scrum,只是首先需要將整個團隊拆分成若干小團隊,保證每個小團隊按照 Scrum 進行操作,此外,再將每個小團隊的 Scrum Master 召集在一起,再做一輪 Scrum,這就是所謂的 Scrum of Scrum。過程稍微複雜一點,但可以將敏捷用於更大的團隊規模,並能保證敏捷的效果。

CSDN:你認爲Scrum Master 的角色至關重要,對項目的成敗起決定性作用。那敏捷開發中由產品經理擔任Scrum Master會有什麼問題?

黃勇:我個人不太建議由產品經理來擔當Scrum Master,原因如下:

  1. Scrum Master 關注的是項目開發視角,而產品經理關注的是產品功能視角,兩者關注的視角是不一樣的。
  2. Scrum Master 需要有一定的技術開發功底,需要對開發工作量進行評估,也需要對技術實現進行評審,可能還會有一定的編碼工作,而具有技術功底的產品經理畢竟太少了,即便有的話,可能對技術方面也不會太深入。
  3. 需要有一個人,他來對整個產品負責,這個人就是Product Owner,該角色最好由產品經理來擔任。

CSDN:敏捷開發過程中測試團隊的職責和產出是什麼?

黃勇:在敏捷開發過程中,我認爲測試團隊的職責有以下幾點:

  1. 根據產品需求,定義測試用例。
  2. 針對測試用例進行功能測試,並將測試的結果反饋給開發人員。
  3. 負責搭建系統運行所需的環境,包括軟件安裝、數據初始化等。

CSDN:除了Scrum,還有XP、CM、FDD、ASD、DSDM等敏捷開發方法,如何去選擇一個合適的敏捷開發工具或者方法呢?

黃勇:敏捷開發方法有很多,不僅僅只有Scrum 一種,其實不妨相互借鑑,再結合自身的特點,定義一套適合自己的敏捷開發方法。例如XP 中所提倡的結對編程、持續集成、測試驅動等,這些都是很好的方法,值得借鑑。包括看板也是一個很不錯的工具,可以結合Scrum 來工作。

CSDN:從博客上,你也研究過「使用看板進行敏捷開發」,能不能分享你的研究成果?

黃勇:敏捷開發工具“看板”,該詞彙來自於島國,當我看到看板的英文時,我真的驚呆了,看板竟然就是 Kanban?!

我們可以結合 Scrum 與 Kanban,讓項目管理更加有效,讓資源分配更加合理,讓績效考覈更加公平!

  • 對於項目經理而言,最擔心的就是項目進度不可控,不知道每位開發人員具體的工作進度,有了 Kanban 一切都是那麼地清晰。
  • 對於開發經理而言,最擔心的就是資源分配不合理,忙的人忙死,閒的人閒死,有了 Kanban 一切都是那麼地自然。
  • 對於開發人員而言,最擔心的就是績效考覈不公平,“憑什麼我做的比他多,拿的工資卻比他少?不公平啊!”有了 Kanban 一切都是那麼地公平。

可見,項目經理、開發經理、開發人員擁有了 Kanban,也就擁有了和諧與快樂!

那麼 Kanban 到底是什麼呢?我們先來看看這張表格吧:


下面我們來理解一下這個表格吧!

  • 這個表格有 5 列:Backlog(原始需求)、Selected(被選中的需求)、Develop(開發階段)、Deploy(部署階段)、Live(上線階段)
  • 其中 Develop 階段包括 2 個子階段:Ongoing(進行中)、Done(已完成)
  • 包括 3 中角色:產品經理(紅色小人)、開發人員(藍色小人)、部署人員(綠色小人),其實還有項目經理,只是他/她貫穿於始終,所有就沒有畫出來了。

在 Backlog 中放置了許多小卡片,它們在 Kanban 中被稱爲 WIP(Work In Process,在製品)。對於產品經理而言,WIP 是需求,而對於開發人員與部署人員而言,WIP 卻是任務。

實際這些 WIP 卡片上都帶有一些文字描述,包括:標題、描述、優先級等信息。

需要注意的是,Selected、Develop、Deploy 下方有一個數字,該數字表示此階段中最多可以放置的 WIP 數量。例如,在 Selected 中最多隻能放 2 個 WIP;在 Develop 中(包括它的子階段)最多隻能放置 2 個 WIP。這裏的數字只是一個示例,具體多少可根據團隊實際情況而定。有一個經驗公式可以參考“WIP 上限 = 團隊規模 * 2 - 1”,減 1 表示大家需要協作,例如:4 人的團隊,WIP 上限是 7。

也許有人會提出,爲什麼沒有 Test 階段?—— 這個可以有,這裏只是一個示例而已,你不妨自行加上去。

對於多個項目而言,可以在這張表格中添加更多的泳道(行),每一行相當於一個項目,所有的項目進度清晰明瞭。

好!繼續我們的 Kanban,有意思的事情即將發生!


產品經理挑選了 2 個 WIP 到 Selected 中,此時,由開發經理決定該任務的技術難度,並由項目經理將任務分配到指定的開發人員,也可將同一個任務分配給兩個人,讓他們去結對編程。

開發人員(架構師與程序員)可對 Selected 中的需求進行工作量評估,可採用投票的方式進行,最終給出一個合理的評估值,整個估算過程,項目經理無需參與,主要是開發人員共同完成。

開發經理可以對任務設置一個“分值”,這個分值可直接影響到後續的績效考覈,所以對大家來說,這個分值是公開可見的,誰做的多,誰做得少,一目了 然。當然,開發人員也可以主動承擔具有更具挑戰的任務(爲了鍛鍊自己,也爲了多拿點錢),但任務分配的決定權始終在項目經理手中。


現在假設 A、B 兩個任務已經分別被不同的開發人員處理了,那麼這些任務就應該移動到 Ongoing 中,同時,產品經理可以從 Backlog 中挑選出 2 個優先級較高的需求到 Selected 中。這樣就保證 Selected 與 Develop 都達到了 WIP 的上限。


有人已經把 A 做完了,那麼 A 就可以移動到 Done 中了。隨後,部署人員就可以開始幹活了。


部署人員就可以將 A 從 Done 中移動到 Deploy 中,表示部署人員正在做這件事情。同時,做完了 A 任務的開發人員可以再做其它新任務,只需從 Selected 中移動到 Ongoing 中,移動這件事情不是開發人員隨意操作的,而是有項目經理負責的。產品經理髮現 Selected 中只有一個 D,就可以考慮放入一些新的需求了。


此時,部署人員遇到了問題,發現 A 部署的時候總是報錯,跑不起來了。同時,其他開發人員也完成了 B 任務。


完成了 B 任務的開發人員本來是可以做新需求的,但項目經理髮現 Develop 中只能放 2 個任務,所以肯定是後面的階段出現了問題,導致整個流程受阻了。項目經理可以靈活調度人力資源,集中火力解決現在所遇到的問題。


所以項目經理不得不放棄新的任務,去讓開發人員去幫助部署人員來解決問題。此時,其他的開發人員還在進行 C 任務。


部署的問題還沒來得及解決,此時 C 任務也完成了,同時,產品經理也放入了新的 K 需求,確保 Selected 這個水池是裝滿水的。


整個部署問題看起來比較搞人,所有的開發人員全都上陣了,集中更多人的智慧,解決這個棘手的問題。此時,產品經理不能放入更多的需求,由於此時 Selected 已經滿額了。其實,開發人員面對太多的需求時,往往都會倍感壓力,身心憔悴。


看來這個部署問題,確實夠折騰的,連產品經理都過來了湊熱鬧了。但他或許不懂技術,但多個人多個頭腦吧,正所謂“當局者迷,旁觀者清”,最終經過大家的努力,肯定會攻克這座碉堡!


幾天之後,Kanban 流程依舊是穩定的,大家分工協作,人力資源合理利用。大家是一個團隊,目標就是把項目做好,不會因爲自己的事情做完了就閒置了。

我們不妨將這張表格貼到牆上去吧!讓每個員工都可以看到,讓過路的老闆們也可以看到我們的辛苦努力,這確實是一種非常好的項目管理方法!


CSDN:一個成功的項目,離不開每個人的努力,能夠分享下你曾經的項目管理經驗?

黃勇:給大家提出以下 10 點建議及其目標:

  1. Sprint 第一天,需要將目標定義清楚,並讓團隊所有人都知道「確保建立一致的目標並使之明確」;
  2. 若出現需求變更,則優先排到下次迭代,特殊情況需特殊處理「確保本次迭代可以按時完工」;
  3. Scrum Master 將迭代中的需求分解爲任務,每個任務只能有一個任務負責人,且不超過一個人天「確保每日任務可評估」;
  4. 讓 Product Owner 直接與相關開發人員確定需求,Scrum Master 需一同參與「確保需求與實現不會發生偏差」;
  5. 每日定時站會,時長不超過 15 分鐘,規模不要太大「確保任務完成情況與計劃保持一致」;
  6. 每日進行一次代碼評審,由 Scrum Master 負責,並在次日將評審結果通知給相關開發人員「確保代碼質量不要下降」;
  7. 各個團隊的 Scrum Master 保持每日溝通一次,時間不要超過 15 分鐘「確保項目管理不會出現風險」;
  8. 每次迭代結束,讓大家稍微放鬆一下,可提供一些團隊活動,比如聚餐「確保團隊能夠更加凝聚」;
  9. Scrum Master 需要給團隊一些承諾,比如項目獎金或特殊福利等「確保團隊更加有激情」;
  10. 對於情緒異常的員工,Scrum Master 需及時與其溝通「確保不要讓一個人的情緒影響整個團隊」;

此外,作爲項目管理者,需要不斷在團隊中加強以下 6 點文化:

  1. 方向一致
  2. 當面溝通
  3. 全情投入
  4. 充分信任
  5. 說到做到

真正的開源並非只是代碼的開源,而是思想的開源

CSDN:你在開源方面有着諸多的建樹,例如,你是Smart Framework開源框架創始人,你對「開源」怎麼看?國內的開源的現在如何,對比國外呢?

黃勇:我個人認爲,真正的開源並非只是代碼的開源,而是思想的開源。在做開源項目之前,建議能將自己的想法共享出來,而不是 埋頭閉門造車。我不反對“重造輪子”,因爲我們需要更好的輪子,輪子好了車子才能跑得快。凡是有利也有弊,我們也不能盲目地選擇開源技術,因爲並不是適合 別人的技術就適合自己,而是需要根據自身的需求,選擇最適合的開源技術,搭建恰如其分的架構。

有大量的新技術,我首先會去關注它,瞭解它是做什麼的,可以解決什麼問題,但我一開始絕不會去深入研究它,更不會去看它的源碼,因爲一旦遇到這方面的需求場景,我就會從這個“知識庫”中去尋找最好的解決方案,如果仍然尋找不到最合適的開源技術,我纔會嘗試自己去實現。

CSDN:能夠介紹下你寫的Smart Framework的輕量級 Java Web 開發框架?

黃勇:基於對開源的熱愛,以及上述中我的開源態度。我寫了一款名爲 Smart Framework 的輕量 級 Java Web 開發框架,它基於“微內核 + 多插件”的體系架構,基於 Servlet 3.0 規範,不依賴於 Spring、 hibernate 等開源框架,提供 IOC、AOP、ORM 等輕量級解決方案,並具備良好的可擴展性,前端直接使 用 HTML + CSS + JS 開發模式,同時也兼容 JSP、JSTL、Tag 等技術,後端提供 REST 服務接口(基於 JSON 格 式),沒有任何的 XML 配置文件,真正的零配置。我認爲這些特性足以開發一些簡單的 Web 應用程序,至於複雜的功能,就留給插件去完善吧。

當初寫 Smart 的時候並沒有想到大家會對這個框架會如此感興趣,抱着分享的態度,並不想去推廣這個產品,僅僅只是想找到能夠理解自己開源思想 的同道中人。世事總難料,已經有一些企業和個人開始使用這款框架了,並提供了大量的改造與擴展。我很欣慰,因爲我基本上實現了自己的願望,並希望將來會出 現有更好的 Java Web 框架,丟掉重量級的帽子,披上輕量級的外衣。

技術人的歸途

編者注:在採訪期間,小編和一位同是十年工作經驗的coder聊天,發現他正陷於轉型做管理、深耕技術的泥潭,爲此向黃勇老師請教,得出了一個非常不錯的中肯建議,也整理在這裏,希望對你有所幫助。

CSDN:走技術這條路,歸途是什麼?是否轉型又該如何抉擇呢?

黃勇:至少有好幾條路線是可以走的,比如:深入技術、轉型做產品、轉型做管理等,需要根據自己的特長和性格來選擇,做自己喜歡的事情。

從技術轉管理,對自身的要求比較高,說具體點,需要看自己的情商,爲人處世的經驗,與人溝通的技巧,自己也需要有足夠的胸懷,去包容一些事情,還需要自己有足夠的人格魅力去吸引別人,讓別人願意跟着你一起做事。管理有些東西是很難從書本上學到的,但一些經典的管理理論是必須要去學的。

相比較而言,繼續深入技術或者從技術轉產品會容易一些了,因爲很多時候都不太需要與人打交道

CSDN:關於機遇,是可遇不可求的。比如,當管理,那也是有一定的環境造就,你得有這個機會去試一下,才知道自己是否感興趣做管理,以及是否適合等。

黃勇:沒錯,機遇太重要了,而且有些時候,機遇是可以考自己的努力去得到的,說到底還是與人溝通,讓自己的老闆給自己機會,如果現在的公司給不了自己足夠的機遇,那麼不妨考慮一下外面的機遇。總之,自己需要靈活處理,伴隨公司共同成長才是最好的。

CSDN:程序員相對比較「直」,也就是有啥說啥,事後或許才發現說了不該說的話,情商不高,如果改善這一情況呢?

黃勇:性格比較直,說話容易得罪人,這個很正常了,只不過首先需要向對方闡明自己的觀點,是爲了把這件事情做好,和對方的目標是一致的,也就是說,首先與對方共同的價值觀,然後再說自己的想法,並多聽取對方的意見,儘量多和對方保持相同的看法,最後需要注意的是,自己不擅長的方面,儘量多聽少說,聽也是在學習。

在聽的過程中,可以表達自己的認識,並詢問對方是否這樣理解的。

CSDN:最後,你是怎麼分配一天的時間的?閒暇時,喜歡做些什麼來放鬆自己?

黃勇:平時工作我一般都比較忙,會議佔據了我大部分時間,在自己僅有的工作時間裏我會花更多的時間與團隊主管們進行溝通,讓大家保持一致的方向,這樣每個技術主管也會帶出更適合公司文化的團隊。總之,技術氛圍不是一兩天就能形成的,需要長時間的溝通,這個時間對於技術管理人員是必須要付出的。

在閒暇之餘,我喜歡聽音樂,也喜歡和朋友聊天,朋友是自己的一面鏡子,可以通過這面鏡子來看清自己,其實人這一輩子都是在不斷地看清自己,認識自己。

寫給讀者的話:

非常感謝讀者們能夠花自己寶貴的時間來閱讀本文,其實我自己也和大家一樣,我們都在不斷地學習,不斷地提高自己,真心希望本文能夠幫助大家。此外,我也很期待大家能與我進一步互動,我平時也會在線下組織一些小規模的技術交流活動,希望大家能夠相互認識,相互分享,相互幫助。

發佈了37 篇原創文章 · 獲贊 7 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章