Android面試集錦系列(2)——設計模式

設計模式
問簡單的吧,單列模式,人人都會。繼續問Android的單列模式怎麼才能保證一定是單個實列,感覺又有點吹毛求疵。
所以,我很少問設計模式,光談某某模式很有紙上談兵的味道,設計模式是要“付諸實踐”的面試題,光靠介紹還有點難於判斷。關於設計模式,我比較欣賞的是Thoughtworks面試的方式: 結對程式設計 !面試前先給面試者佈置“家庭作業”,然後Thoughtworks會派工程師和麵試者進行結對程式設計,程式設計的內容和麵試者之前的作業相關,讓面試者通過測試驅動和程式碼重構表現他/她的程式設計規範、設計和重構的能力。這個時候可以很容易看到面試者對設計模式的掌握和運用情況。
傳言:在國內,ThoughtWorks被稱為“最難面試的IT公司”。貌似在國外也被評為全球最難面試的IT司。
但是一般公司顯然沒有這樣的條件來選拔面試者。關於結對程式設計下來我和ThoughtWorks的朋友做個交流再和大家分享,還是回到設計模式的面試題,這章就講一下我在求職的過程中印象還比較深刻的一道題吧。
面試題:回撥函式和觀察者模式的區別?
當時聽到這樣的題時我也是一臉懵逼,暗歎出題人居心叵測,冷不丁的還真難回答這樣的問題,這哪裡是考設計模式,完全是考反應能力嘛。
觀察者模式
網上很容易查到觀察者模式的定義:
觀察者模式定義了物件間的一種一對多依賴關係,使得每當一個物件改變狀態,則所有依賴於它的物件都會得到通知並被自動更新。
Android中大量的使用了觀察者模式。你可能已經用過ListView的adapter.notifyDataSetChanged來觸發ListView的列表介面進行更新。notifyDataSetChanged的內部實現就是基於觀察者模式。

跟進這段程式碼你會發現:BaseAdapter中的DataSetObserver(觀察者)實現Observer介面,DataSetObservable(被觀察者)繼承Observable類。
標準的觀察者模式的寫法應該照下面的UML圖:

有幾個概念(抽象主題(Subject)、具體主題(ConcreteSubject)、抽象觀察者(Observer)和具體觀察者(ConcreteObserver)),好在Java幫我實現了相關的程式碼,可以通過Observable類和Observer介面實現了觀察者模式。Observer物件是觀察者,Observable物件是被觀察者。
還有EventBus, RxJava等常見的開源庫也是居於觀察者模式設計的,只是它們實現的方式各有不同。
回撥函式
那回調函式和這又有什麼關係呢?看看這段再熟悉不過的程式碼片段:
button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // do something } });
View的Listener監聽會通過setOnClickListener給View傳遞一個Listener物件,當相關的事件發生時是觸發onClick(回撥onClick)。這其實也是一種觀察者模式,OnClickListener是觀察者,View是被觀察者,當View收到Click事件是會通知觀察者執行onClick()。
關於設計模式的反思
模式的外在形式其實是“套路”,這些套路來源於現實中生產實踐的總結,但要清楚認識到不是所有“套路”都會合適你的。
設計模式是一套被反覆使用、多數人知曉的、經過分類編目的、程式碼設計經驗的總結。使用設計模式是為了可重用程式碼、讓程式碼更容易被他人理解、保證程式碼可靠性。
設計模式的初衷是用經過檢驗的“套路”來提高程式碼的生產效率,人們也容易理解約定成俗的“套路”。從面向物件設計的角度來看,其實就是要做到高內聚低耦合。
所以,在考慮使用什麼樣的模式或模式組合時,我們不妨先冷靜下來回憶一下面向物件設計的SOLID原則,我們要遵循一定的原則,而不是為了模式而模式。
面向物件設計的SOLID原則:
- S 單一功能原則:物件應該僅具有一種單一功能。
- O 開閉原則:軟體體應該是對於擴充套件開放的,但是對於修改封閉的。
- L 里氏替換原則:程式中的物件應該是可以在不改變程式正確性的前提下被它的子類所替換的。
- I 介面隔離原則:多個特定客戶端介面要好於一個寬泛用途的介面。
- D 依賴反轉原則:依賴於抽象而不是一個例項,依賴注入是該原則的一種實現方式。
“標準答案”
有讀者反映我之前的面試題總是列出了試題卻沒有直接給出答案,而是說些有的沒的“廢話”,“很浪費”大夥的時間。我用題“騙來”讀者,卻讓讀者自己去思考怎麼回答,如果從面試刷題的角度來說我確實是“罪大惡極”。
反省了一下,這些讀者的要求不無道理,我決定做一個改善吧。從今天這題開始,“Android面試一天一題”的最後一節都會奉上相關的面試題和我認為的“標準答案”,需要節省時間的讀者可以直接翻到最後。
注意:標準答案都加了“”,我不能保證自己的理解和描敘百分百正確,參考時可以留個“心眼”。
面試題: 回撥函式和觀察者模式的區別?
“標準答案”:觀察者模式定義了一種一對多的依賴關係,讓多個觀察者物件同時監聽某一個主題物件。觀察者模式完美的將觀察者和被觀察的物件分離開,一個物件的狀態發生變化時,所有依賴於它的物件都得到通知並自動重新整理。
回撥函式其實也算是一種觀察者模式的實現方式,回撥函式實現的觀察者和被觀察者往往是一對一的依賴關係。
所以最明顯的區別是觀察者模式是一種設計思路,而回調函式式一種具體的實現方式;另一明顯區別是一對多還是多對多的依賴關係方面。
面試題:Android的單列模式如何保證一定單列的情況?
分析:如下程式碼,我們一般寫單列模式是這樣的:
public class Singleton{ private static Singleton instance; private Singleton() {}; public static Singleton getInstance() { if (instance==null) instance=new Singleton(); return instance; } }
如果不同的執行緒同時執行“if (instance==null)”,因為instance還未賦值,是會存在多個instance例項的。所以保險的一點的寫法:
public class Singleton{ private static Singleton instance; private Singleton() {}; public static Singleton getInstance() { if (instance==null) { synchronized(Singleton.class) { if (instance==null) instance=new Singleton(); } } return instance; } }
但這種兩次判斷的方式還是有可能出問題。因為“instance=new Singleton();”這段程式碼並不是一條唯一的指令,實際上這段程式碼會編譯成多條指令,大致上做了3件事:
(1)給Singleton例項分配記憶體
(2)呼叫Singleton()建構函式,初始化成員欄位
(3)將instance物件指向分配的記憶體
而且上面的(2)和(3)的順序無法得到保證的,虛擬機器可能先初始化例項欄位再把instance指向具體的記憶體例項,也可能先把instance指向記憶體例項再對例項進行初始化成員欄位。
當然這請情況很少見,不過我還是聽一個同事講過,他有遇到了用這種兩次判斷的方法還是有多個例項。
標準答案:我們可以在兩次判斷的基礎上,使用“volatile”關鍵字來修飾instance,保證instance例項的唯一。
public class Singleton{ private volatile static Singleton instance; private Singleton() {}; public static Singleton getInstance() { if (instance==null) { synchronized(Singleton.class) { if (instance==null) instance=new Singleton(); } } return instance; } }
面試題: Android較常用到的設計模式?
標準答案:
介面卡模式:GridView、ListView的Adapter;
建造者模式:AlertDialog.Builder;
觀察者模式:ListView的adapter.notifyDataSetChanged;
責任鏈模式:View的事件分發;
(當然還有很多,列出你熟悉的就好。)
最後
針對於上面的面試題我總結出了網際網路公司Android程式設計師面試涉及到的絕大部分面試題及答案做成了文件和架構視訊資料免費分享給大家【 包括高階UI、效能優化、架構師課程、NDK、Kotlin、混合式開發(ReactNative+Weex)、Flutter等架構技術資料 】,希望能幫助到您面試前的複習且找到一個好的工作,也節省大家在網上搜索資料的時間來學習。
資料獲取方式:加入Android架構交流QQ群聊:513088520 ,進群即領取資料!!!
點選連結加入群聊【Android移動架構總群】: 加入群聊

資料大全