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

目录

AOP的由来

AOP的出现并不是要完全取代OOP, 而是作为OOP的补充

按照OOP的思想, 如果多个类中出现相同的代码, 应该考虑定义一个基类, 将这些相同的代码提取到基类中

通过引入基类实现复用的方法在大多情况下是可行的, 但是对于下面的情况却无能为力

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(); // ① 性能监控结束  
    }  
    …  
}

由于性能监控, 事务处理的代码依附在业务类方法的流程中, 所以无法抽象到基类中

AOP通过横向抽取机制, 为这类无法通过纵向继承进行抽象的重复性代码提供了解决方案

通过上述AOP的由来不难看出

AOP并不是"万金油", 它一般只适合于那些具有横切逻辑的应用场合: 如性能监测、访问控制、事务管理、日志记录和异常处理等

什么是AOP?

知道了为什么会有AOP这么个"东西", 那到底什么是AOP呢

百度百科中的定义如下

AOP为Aspect Oriented Programming的缩写, 意为: 面向切面编程, 通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术

Wikipedia中的定义如下

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.

如果说百度百科的解释比较短, 你还能看下去的话, 那么看到Wiki解释的长度你可能就望而却步了

站在"巨人"的肩膀上, 本人对AOP的定义如下

AOP(剖面编程, 对于面对切面编程的翻译表示不喜欢)是指在运行时动态地将代码切入到类的指定方法、指定位置上的编程思想

它有两个非常关键的特征

  • 不会修改接口

  • 动态添加实现

AOP与设计模式

AOP与Bridge

对于Bridge模式

Big Four的设计模式中的定义如下

Decouple an abstraction from its implementation so that the two can vary independently

将抽象和实现解耦, 使得两者可以独立地变化

而上面讨论AOP的第一个特点就是

  • 不会修改接口

所以说, AOP体现了Bridge模式的设计思想

关于Bridge的更多介绍, 详细参考设计模式 之 结构型模式

AOP与Dynamic Proxy

如果说AOP体现了Bridge模式的设计思想, 那么AOP的实现就要基于Dynamic Proxy了

例如下面在方法调用时打印日志的例子

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();

关于Dynamic Proxy的介绍, 详细参考设计模式 之 Proxy

AOP与Spring

本文的标题是"谈谈移动开发编程中的AOP(剖面编程)", 为什么要说Spring呢?

啥都不说先, "一言不合"先上个Spring官方的架构图

aop-programming-in-mobile_01.png

tutorialspoint关于AOP与Spring的描述准确而全面

为了不丢失信息和保证准确, 直接引用原文如下

One of the key components of Spring Framework is the Aspect oriented programming (AOP) framework

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

There are various common good examples of aspects like logging, auditing, declarative transactions, security, and caching etc

如果没有Spring的流行, 及其核心的IoC(Inversion of Control), DI(Dependecy Injection), AOP等思想, 这些概念也不会如此快如此广地被人们熟知

更多关于Spring与AOP, 可以参考11. Aspect Oriented Programming with Spring

AOP与移动开发

Android开发中的AOP

如果要说Android中设计, 模式与AOP的集大成者, 那么非Retrofit莫属了

关于Retrofit的详细分析, 可以参考Retrofit分析-经典设计模式案例

首先我们先回顾下Retrofit的使用

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;
    }

}

这里只需要定义接口, 对象是在运行时通过Dynamic Proxy动态生成的

  • 除了这种Dynamic Proxy的实现方法外

  • Dependency Injection(依赖注入)也是实现AOP的常用方式

关于依赖注入更多可以参考依赖注入原理

例如这里Retrofit.java中的client方法

public Builder client(OkHttpClient client) {
  return callFactory(checkNotNull(client, "client == null"));
}

就是将实现网络请求的对象注入到Retrofit对象中, 这种依赖注入满足了AOP的两个核心特征

  • 在接口不变的情况下, 只要是实现了规定接口的client, 都可以依赖注入到Retrofit作为实际的网络请求对象

  • 如果有优于OkHttp的实现的话, 完全可以自由灵活的切换到新的实现方式

如果想了解AOP在android中的应用, 可以参考Aspect Oriented Programming in Android

iOS开发中的AOP

相比于Android中AOP的两种实现方式(Dynamic Proxy, Dependency Injection), iOS中AOP的实现就显得有点不那么"寻常"

由于Objective-C是基于Smalltalk发展而来的"消息型"语言, 所以Objective-C相比于Java来说实现起来更加自然和简单

没错, 就是基于强大的Runtime

例如在不改变系统接口和实现(当然你也没法改变)的前提, 统计按钮的次数

#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

关于Runtime和Method Swizzling的更多解释, 可以参考iOS开发 之 Runtime

很多第三方的iOS库都是基于Runtime来实现AOP, 即在不改变接口的情况下, 动态地修改实现

而且这样的第三方库往往都有一个相同的特点: 不会对原有代码做任何改动

这里我们就拿最近用到的MLeaksFinder来说吧

MLeaksFinder:精准 iOS 内存泄露检测工具 | WeRead团队博客中对MLeaksFinder实现分析如下

  • 在一个ViewController被pop或dismiss一小段时间后, 看看该UIViewController, 它的view, view的subviews等等是否还存在

  • 这里使用了AOP技术, hook掉UIViewController和UINavigationController的pop跟dismiss方法, 关于如何 hook, 请参考 Method Swizzling

参考

更多文章, 请支持我的个人博客

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容