工做須要,要對接阿里媽媽的廣告聚合平臺,簡稱AFP。對於通常的應用而言,想要流量變現,廣告是顯而易見的手段,尤爲是在中國,打開一個千萬級別的用戶,確定有某個地方是有對接廣告的,只不過明不明顯而已。設計模式
阿里媽媽的AFP廣告聚合平臺說穿了,就是一個平臺聚合了多個第三方平臺,像是百度,廣點通,由他們平臺來接入,而後推送相應的廣告,固然也包括自售的廣告。架構
若是咱們不使用這種聚合平臺,又想要提升本身的廣告收入,增長廣告曝光是惟一的選擇,可是廣告的填充是個大問題,由於單一的廣告平臺的物料填充並不能保證百分百,多個平臺的接入,對客戶端自己又是一個很是大的負擔,想一想看,要在肯定百度平臺拉取廣告失敗的時候,再去調用廣點通拉取廣告,或者是同時拉取兩個平臺廣告,而後肯定優先級,誰是首選,誰是備選,這些都是很頭疼的問題,更嚴重的是,多個平臺的SDK的接入,會增長包的體積。oop
阿里媽媽的AFP聚合平臺解決了這個問題,只要接入他們的SDK就行,不用接入其餘平臺的SDK,由於他們本身後臺去調用其餘平臺的SDK獲取對應的廣告。學習
考慮到之後廣告的展現形式是多種的,像是開屏,插屏等,固然,AFP自己的API也已是很簡單了,但不免要根據不一樣的狀況進行對應的配置。若是想要更好的管理,能夠利用工廠模式+策略模式完成這些不一樣狀況的配置問題。優化
咱們的設想很簡單:上層業務不須要理會具體的廣告SDK的實現,甚至連對應的庫都不用導入,他們只要和一個抽象對接就行,這個抽象就是負責調控和管理各類類型廣告。編碼
咱們命名爲AdManager。spa
Android的業務單位是Activity,根據AFP的API,咱們須要傳達的是廣告位ID,對應的廣告展現形式,廣告平臺。設計
AdManager adManager = AdManager.newInstance();
adManager.setup(id, AdManager.ShowType.Welcome, AdManager.Platform.Baidu);
咱們並不對AdManager進行單例處理。並非全部的高級抽象都須要單例處理,相反,單例會形成未知的錯誤,由於程序共享同一個實例,咱們徹底沒法預知會在哪裏這個實例就被修改。code
這裏的使用場景只要想要就直接new一個就行,不過爲了不每次都要寫new代碼,就用一個newInstance方法進行封裝而已。orm
setup的代碼實現是典型的策略模式,由於這裏須要根據傳入的廣告類型返回不一樣的配置。
1 MmuProperties properties = null; 2 Object controller = null; 3 switch (showType) { 4 case Banner: 5 properties = createBannerProperties(activity, slotId, viewGroup, platforms); 6 controller = ((BannerProperties) properties).getController(); 7 break; 8 case Feed: 9 properties = createFeedProperties(activity, slotId, platforms); 10 controller = ((MMUFeedProperties) properties).getController(); 11 break; 12 case Insert: 13 properties = createInsertProperties(activity, slotId, platforms); 14 controller = ((InsertProperties) properties).getController(); 15 break; 16 case LoopImage: 17 properties = createLoopImageProperties(activity, slotId, viewGroup, platforms); 18 break; 19 case Welcome: 20 properties = createWelcomeProperties(activity, slotId, viewGroup, platforms); 21 controller = ((WelcomeProperties) properties).getController(); 22 break; 23 default: 24 break; 25 } 26 27 if (properties == null) { 28 return null; 29 } 30 31 MMUSDKFactory.getMMUSDK().attach(properties); 32 return controller;
咱們不談裏面有關AFP的API調用代碼,這裏爲了實現策略模式,使用了switch+Enum的方式。
在咱們剛學習設計模式的時候,就知道策略模式要解決的代碼場景是大量if-else if-else的使用,但實際上並非全部相似這樣的使用就必須得用策略模式,設計模式的使用初衷應該是爲了讓使用場景具備更好的擴展性,而不是針對某部分代碼結構的優化,這也是爲何有些人蔘考了MVP的設計模式對本身應用的架構進行設計後,發現代碼的編寫更加困難了,並且爲了切合MVP模式,對一些無關痛癢的業務場景也進行了很是重的設計,致使代碼非但沒有更加清晰,反倒更像是某我的的設計模式試驗場,要是更加嚴苛點,就是垃圾場了。
咱們這裏須要根據不一樣的廣告展現類型來進行不一樣的配置,這個場景是符合策略模式的使用場景的。雖然只是咱們我的的小偏見,就是若是不一樣的狀況若是能夠定義爲不一樣的Enum,像是上面的ShowType,就是廣告展現類型的Enum,就能夠配合Switch使用策略模式,由於能夠歸類爲一個Enum,說明每一個Enum實例的確是相同業務場景下須要的一組條件,根據這些條件實施不一樣的策略。
因此咱們這裏使用了策略模式。
這裏咱們能夠注意到一個問題:條件不止一組,而是兩組。
廣告的展現形式是一組,廣告的平臺也是一組。咱們是如何決定哪一組是先決條件呢?
能夠明確的說,這兩組條件並不分先決和後決之分,在代碼組織上,之因此決定展現形式是第一組策略條件,也是第一個判斷的條件,單純只是由於咱們以爲,第一組策略條件若是是比較多的,那麼耐着性子看到第二組的人,發現第二組條件居然如此簡單,心裏可能會有如釋重負的感受,也就是所謂先苦後甜的觀後感吧。
固然,要真想找個客觀的理由,就是AFP它容許咱們自定義第三方平臺,假設咱們並不想要徹底交給他們處理一些問題,像是UI,這點在開屏那裏很是明顯,雖然不知道具體的緣由,可是絕大部分主流的廣告平臺都不容許暴露開屏數據,而是要交給他們去渲染,並且開屏那裏不可避免的一個設計就是跳過按鈕,廣點通以前的版本是不容許自定義(如今的版本已經能夠了),百度是能夠的,因此想要加上本身的跳過按鈕,就要採起數據對接的方式,這在AFP那裏的實現就是讓客戶本身去定義第三方平臺,本身去渲染和添加。
很不幸,咱們就是後面那種狀況,因此在有了須要添加不一樣平臺的適配這個需求後,咱們必需要構建一個廣告平臺的工廠類,來幫助咱們更好的管理不一樣 的平臺。
因此,咱們的一個小小的經驗就是:若是兩組條件,其中某組條件是另外一組條件中的共性,相似廣告的展現形式是每一個平臺都應該具有的,能夠將這組共性的條件放在第一個策略組中。
上面咱們提到了廣告平臺的工廠類,這個類是很重要的,由於咱們須要一個抽象來負責管理這些自定義的平臺的調度,而且AdManager自己提供的應該是實現某種展現的廣告平臺,而根本無需理會這些不一樣廣告平臺是如何產生的,這並非它的職責。
在考慮代碼結構設計時,職責是一個很重要的概念。某個類應該承擔什麼樣的職責,決定了這個類在整個設計中的角色。
AdManager這個抽象咱們賦予的意義就是提供某種展現形式的廣告,至於什麼樣的廣告,這個並非它決定的,根據單一職責的設計要求,它承擔的職責已經足夠了,決定廣告平臺的應該是另外一個類。
咱們將這個職責交給了AdAdapterFactory。
bannerProperties.addCustomAdapter(AdId, (MMUBannerCustomAdapter) AdAdapterFactory.createAdAdapter(platform, ShowType.Banner));
AdAdapterFactory須要產生對應廣告展現形式的不一樣平臺的適配器,因此這裏又涉及到了策略模式。
1 switch (platform) { 2 case Baidu: 3 adAdapter = createBaiduAdAdapter(showType, viewGroup); 4 break; 5 case GDT: 6 adAdapter = createGDTAdAdapter(showType, viewGroup); 7 break; 8 default: 9 break; 10 }
值得注意的是,這裏一樣涉及到廣告展現形式和廣告平臺兩組條件,可是第一組條件的選擇和AdManager是相反的。
咱們的選擇依據其實很是簡單:AdManager注重的是廣告平臺的選擇,而AdAdapterFactory更加註重的是廣告展現形式的選擇。
AdManager要解決的問題實際上是展現什麼平臺的廣告,由於它調度的是不一樣廣告平臺,而AdAdapterFactory要解決的問題是根據須要的廣告平臺去調用他們對應廣告展現形式的API。
因此咱們在選擇策略條件組的前後順序的時候,明確的條件組會放在第一組,而核心條件組放在第二組,強調核心問題永遠都是放在最後最關鍵的地方,保證閱讀代碼的人在看到對應代碼時候,是跟着業務場景中問題的明確度來的。
問題的明確度簡單來說,就是咱們在解決一個問題的時候,就知道該問題的明確條件。
當咱們調用AdManager的時候,咱們就知道須要的廣告展現形式,開屏的位置確定是須要開屏的廣告展現,可是不知道廣告平臺是如何選擇的,因此這裏的明確條件就是廣告展現形式。
調用AdAdapterFactory的時候,咱們也是明確知道要調度的廣告平臺,可是不知道該平臺對應廣告展現形式的代碼。
createBaiduAdAdapter的核心是根據對應的展現形式選擇對應的適配器:
switch (showType) { case Banner: break; case Feed: break; case Insert: break; case LoopImage: break; case Welcome: adapter = new BaiduWelcomeAdapter(viewGroup); break; default: break; }
到了這裏,咱們總體的廣告管理設計系統的大概實現就已經出來,剩下的只是根據具體的狀況作具體的調整。
雖然只是簡單的業務場景代碼,但咱們在編碼的時候,要考慮到業務自己的核心點在哪裏,而後圍繞這個核心點會有什麼樣的問題,如何去解決這樣的問題,代碼設計上如何更好的體現這些問題的解決思路,就是咱們平時作業務時須要考慮的。
不少人都會抱怨本身剛出來工做,只是作一些簡單的業務,相似我這種廣告接入業務,自己就不具備任何技術含量,但編碼質量水平並不取決因而否解決多複雜的問題,也不取決於可以解決更多問題,重要的是立足於當前問題提供更好更方便的思路。