工做须要,要对接阿里妈妈的广告聚合平台,简称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; }
到了这里,咱们总体的广告管理设计系统的大概实现就已经出来,剩下的只是根据具体的状况作具体的调整。
虽然只是简单的业务场景代码,但咱们在编码的时候,要考虑到业务自己的核心点在哪里,而后围绕这个核心点会有什么样的问题,如何去解决这样的问题,代码设计上如何更好的体现这些问题的解决思路,就是咱们平时作业务时须要考虑的。
不少人都会抱怨本身刚出来工做,只是作一些简单的业务,相似我这种广告接入业务,自己就不具备任何技术含量,但编码质量水平并不取决因而否解决多复杂的问题,也不取决于可以解决更多问题,重要的是立足于当前问题提供更好更方便的思路。