谈谈移动开发编程中的AOP(剖面编程)

Maritza1712 8年前
   <h2><strong>目录</strong></h2>    <ul>     <li> <p>AOP的由来</p> </li>     <li> <p>什么是AOP?</p> </li>     <li> <p>AOP与设计模式</p>      <ul>       <li> <p>AOP与Bridge</p> </li>       <li> <p>AOP与Dynamic-Proxy</p> </li>      </ul> </li>     <li> <p>AOP与Spring</p> </li>     <li> <p>AOP与移动开发</p>      <ul>       <li> <p>Android开发中的AOP</p> </li>       <li> <p>iOS开发中的AOP</p> </li>      </ul> </li>     <li> <p>参考</p> </li>    </ul>    <h2><strong>AOP的由来</strong></h2>    <p>AOP的出现并不是要完全取代OOP, 而是作为OOP的补充</p>    <p>按照OOP的思想, 如果多个类中出现相同的代码, 应该考虑定义一个基类, 将这些相同的代码提取到基类中</p>    <p>通过引入基类实现复用的方法在大多情况下是可行的, 但是对于下面的情况却无能为力</p>    <pre>  <code class="language-java">public class PostService {        private TransactionManager transManager;        private PerformanceMonitor pmonitor;        private TopicDao topicDao;        private ForumDao forumDao;          public void removeTopic(int topicId) {            pmonitor.start(); // ① 性能监控开始            transManager.beginTransaction(); // ② 事务处理开始              topicDao.removeTopic(topicId); // ③ 业务逻辑              transManager.commit(); // ② 事务处理结束            pmonitor.end(); // ① 性能监控结束        }        public void createForum(Forum forum) {            pmonitor.start(); // ① 性能监控开始            transManager.beginTransaction(); // ② 事务处理开始              forumDao.create(forum); // ③ 业务逻辑              transManager.commit(); // ② 事务处理结束            pmonitor.end(); // ① 性能监控结束        }        …    }</code></pre>    <p>由于性能监控, 事务处理的代码依附在业务类方法的流程中, 所以无法抽象到基类中</p>    <p>AOP通过横向抽取机制, 为这类无法通过纵向继承进行抽象的重复性代码提供了解决方案</p>    <p>通过上述AOP的由来不难看出</p>    <p>AOP并不是"万金油", 它一般只适合于那些具有横切逻辑的应用场合: 如性能监测、访问控制、事务管理、日志记录和异常处理等</p>    <p><!--more--></p>    <h2><strong>什么是AOP?</strong></h2>    <p>知道了为什么会有AOP这么个"东西", 那到底什么是AOP呢</p>    <p><a href="/misc/goto?guid=4959716478582935090" rel="nofollow,noindex">百度百科</a> 中的定义如下</p>    <p>AOP为Aspect Oriented Programming的缩写, 意为: 面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术</p>    <p><a href="/misc/goto?guid=4958829948484129733" rel="nofollow,noindex">Wikipedia</a> 中的定义如下</p>    <p>In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code (an advice) without modifying the code itself, instead separately specifying which code is modified via a "pointcut" specification, such as "log all function calls when the function's name begins with 'set'". This allows behaviors that are not central to the business logic (such as logging) to be added to a program without cluttering the code core to the functionality. AOP forms a basis for aspect-oriented software development.</p>    <p>如果说百度百科的解释比较短, 你还能看下去的话, 那么看到Wiki解释的长度你可能就望而却步了</p>    <p>站在"巨人"的肩膀上, 本人对AOP的定义如下</p>    <p>AOP(剖面编程, 对于面对切面编程的翻译表示不喜欢)是指在运行时动态地将代码切入到类的指定方法、指定位置上的编程思想</p>    <p>它有两个非常关键的特征</p>    <ul>     <li> <p>不会修改接口</p> </li>     <li> <p>动态添加实现</p> </li>    </ul>    <h2><strong>AOP与设计模式</strong></h2>    <p><strong>AOP与Bridge</strong></p>    <p>对于Bridge模式</p>    <p><a href="http://www.amazon.cn/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%A7%91%E5%AD%A6%E4%B8%9B%E4%B9%A6-%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F-%E5%8F%AF%E5%A4%8D%E7%94%A8%E9%9D%A2%E5%90%91%E5%AF%B9%E8%B1%A1%E8%BD%AF%E4%BB%B6%E7%9A%84%E5%9F%BA%E7%A1%80-Erich-Gamma/dp/B001130JN8/ref=sr_1_1?s=books&ie=UTF8&qid=1454399351&sr=1-1&keywords=%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F" rel="nofollow,noindex">Big Four的设计模式</a> 中的定义如下</p>    <p>Decouple an abstraction from its implementation so that the two can vary independently</p>    <p>将抽象和实现解耦, 使得两者可以独立地变化</p>    <p>而上面讨论AOP的第一个特点就是</p>    <ul>     <li> <p>不会修改接口</p> </li>    </ul>    <p>所以说, AOP体现了Bridge模式的设计思想</p>    <p>关于Bridge的更多介绍, 详细参考 <a href="/misc/goto?guid=4959716478798680212" rel="nofollow,noindex">设计模式 之 结构型模式</a></p>    <p>AOP与Dynamic Proxy</p>    <p>如果说AOP体现了Bridge模式的设计思想, 那么AOP的实现就要基于Dynamic Proxy了</p>    <p>例如下面在方法调用时打印日志的例子</p>    <pre>  <code class="language-java">final ISubject subject = new RealSubject();  ISubject proxy = (ISubject) Proxy.newProxyInstance(subject.getClass().getClassLoader(), new Class[]{ISubject.class}, new InvocationHandler() {      @Override      public Object invoke(Object o, Method method, Object[] objects) throws Throwable {          Log.i("InvocationHandler", method.getName());          Object result = method.invoke(subject, objects);          return result;      }  });  proxy.request();</code></pre>    <p>关于Dynamic Proxy的介绍, 详细参考 <a href="/misc/goto?guid=4959716478878147009" rel="nofollow,noindex">设计模式 之 Proxy</a></p>    <h2><strong>AOP与Spring</strong></h2>    <p>本文的标题是"谈谈移动开发编程中的AOP(剖面编程)", 为什么要说Spring呢?</p>    <p>啥都不说先, "一言不合"先上个 <a href="/misc/goto?guid=4959716478961048306" rel="nofollow,noindex">Spring官方的架构图</a></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/1602986306870afab3b82f200dcaf541.png"></p>    <p style="text-align:center">aop-programming-in-mobile_01.png</p>    <p><a href="/misc/goto?guid=4959716479045923820" rel="nofollow,noindex">tutorialspoint</a> 关于AOP与Spring的描述准确而全面</p>    <p>为了不丢失信息和保证准确, 直接引用原文如下</p>    <p>One of the key components of Spring Framework is the Aspect oriented programming (AOP) framework</p>    <p>Aspect Oriented Programming entails breaking down program logic into distinct parts called so-called concerns. The functions that span multiple points of an application are called cross-cutting concerns and these cross-cutting concerns are conceptually separate from the application's business logic</p>    <p>There are various common good examples of aspects like logging, auditing, declarative transactions, security, and caching etc</p>    <h2><strong>AOP与移动开发</strong></h2>    <p><strong>Android开发中的AOP</strong></p>    <p>如果要说Android中设计, 模式与AOP的集大成者, 那么非 <a href="/misc/goto?guid=4958964956869128717" rel="nofollow,noindex">Retrofit</a> 莫属了</p>    <p>关于Retrofit的详细分析, 可以参考 <a href="/misc/goto?guid=4959716479163957581" rel="nofollow,noindex">Retrofit分析-经典设计模式案例</a></p>    <p>首先我们先回顾下Retrofit的使用</p>    <pre>  <code class="language-java">public interface TestObjectApi {        @GET("classes/TestObject")      @Headers({              "X-LC-Id: kdWDrbX9k02QyGhLof6Injmi-gzGzoHsz",              "X-LC-Key: h2DtBuFcAd2e8NFCq5LY6V86"      })      public Call<ResponseBody> getTestObjects();    }    public class NetworkUtil {        private static OkHttpClient mOkHttpClient = new OkHttpClient();      private static Converter.Factory mFastJsonConverterFactory = FastJsonConverterFactory.create();        private static TestObjectApi mTestObjectApi;        public static TestObjectApi getTestObjectApi() {          if (mTestObjectApi == null) {              Retrofit retrofit = new Retrofit.Builder()                      .client(mOkHttpClient)                      .baseUrl("https://api.leancloud.cn/1.1/")                      .addConverterFactory(mFastJsonConverterFactory)                      .build();              mTestObjectApi = retrofit.create(TestObjectApi.class);          }          return mTestObjectApi;      }    }</code></pre>    <p>这里只需要定义接口, 对象是在运行时通过Dynamic Proxy动态生成的</p>    <ul>     <li> <p>除了这种Dynamic Proxy的实现方法外</p> </li>     <li> <p>Dependency Injection(依赖注入)也是实现AOP的常用方式</p> </li>    </ul>    <p>关于依赖注入更多可以参考 <a href="/misc/goto?guid=4959641426268701605" rel="nofollow,noindex">依赖注入原理</a></p>    <p>例如这里Retrofit.java中的client方法</p>    <pre>  <code class="language-java">public Builder client(OkHttpClient client) {    return callFactory(checkNotNull(client, "client == null"));  }</code></pre>    <p>就是将实现网络请求的对象注入到Retrofit对象中, 这种依赖注入满足了AOP的两个核心特征</p>    <ul>     <li> <p>在接口不变的情况下, 只要是实现了规定接口的client, 都可以依赖注入到Retrofit作为实际的网络请求对象</p> </li>     <li> <p>如果有优于OkHttp的实现的话, 完全可以自由灵活的切换到新的实现方式</p> </li>    </ul>    <p><strong>iOS开发中的AOP</strong></p>    <p>相比于Android中AOP的两种实现方式(Dynamic Proxy, Dependency Injection), iOS中AOP的实现就显得有点不那么"寻常"</p>    <p>由于Objective-C是基于Smalltalk发展而来的"消息型"语言, 所以Objective-C相比于Java来说实现起来更加自然和简单</p>    <p>没错, 就是基于强大的Runtime</p>    <p>例如在不改变系统接口和实现(当然你也没法改变)的前提, 统计按钮的次数</p>    <pre>  <code class="language-java">#import "UIButton+Extension.h"  #import <objc/runtime.h>    @implementation UIButton (Extension)    + (void)load {      Class buttonClass = [UIButton class];      Method originalMethod = class_getInstanceMethod(buttonClass, @selector(sendAction:to:forEvent:));      Method swizzledMethod = class_getInstanceMethod(buttonClass, @selector(dynamic_sendAction:to:forEvent:));      method_exchangeImplementations(originalMethod, swizzledMethod);  }    - (void)dynamic_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event {      NSLog(@"counter++");      [self dynamic_sendAction:action to:target forEvent:event];  }    @end</code></pre>    <p>关于Runtime和Method Swizzling的更多解释, 可以参考 <a href="/misc/goto?guid=4959716479272861342" rel="nofollow,noindex">iOS开发 之 Runtime</a></p>    <p>很多第三方的iOS库都是基于Runtime来实现AOP, 即在不改变接口的情况下, 动态地修改实现</p>    <p>而且这样的第三方库往往都有一个相同的特点: 不会对原有代码做任何改动</p>    <p>这里我们就拿最近用到的 <a href="/misc/goto?guid=4959716479354163270" rel="nofollow,noindex">MLeaksFinder</a> 来说吧</p>    <p><a href="/misc/goto?guid=4958987581074905592" rel="nofollow,noindex">MLeaksFinder:精准 iOS 内存泄露检测工具 | WeRead团队博客</a> 中对MLeaksFinder实现分析如下</p>    <ul>     <li> <p>在一个ViewController被pop或dismiss一小段时间后, 看看该UIViewController, 它的view, view的subviews等等是否还存在</p> </li>     <li> <p>这里使用了AOP技术, hook掉UIViewController和UINavigationController的pop跟dismiss方法, 关于如何 hook, 请参考 Method Swizzling</p> </li>    </ul>    <h2><strong>参考</strong></h2>    <ul>     <li> <p><a href="/misc/goto?guid=4959716479462647281" rel="nofollow,noindex">Spring AOP之一 ——AOP的概念</a></p> </li>     <li> <p><a href="/misc/goto?guid=4959716478878147009" rel="nofollow,noindex">设计模式 之 Proxy</a></p> </li>     <li> <p><a href="/misc/goto?guid=4959716478798680212" rel="nofollow,noindex">设计模式 之 结构型模式</a></p> </li>     <li> <p><a href="/misc/goto?guid=4959716478961048306" rel="nofollow,noindex">Introduction to the Spring Framework</a></p> </li>     <li> <p><a href="/misc/goto?guid=4959716479586254400" rel="nofollow,noindex">11. Aspect Oriented Programming with Spring</a></p> </li>     <li> <p><a href="/misc/goto?guid=4959716479666032064" rel="nofollow,noindex">Spring Tutorial</a></p> </li>     <li> <p><a href="/misc/goto?guid=4959716479747506225" rel="nofollow,noindex">Aspect Oriented Programming in Android</a></p> </li>     <li> <p><a href="/misc/goto?guid=4958987581074905592" rel="nofollow,noindex">MLeaksFinder:精准 iOS 内存泄露检测工具 | WeRead团队博客</a></p> </li>    </ul>    <p> </p>    <p> </p>    <p>来自:http://www.jianshu.com/p/66a5a2d1d4ad</p>    <p> </p>