談談移動開發編程中的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閱讀 230,048評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,414評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,169評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,722評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,465評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,823評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,813評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,000評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,554評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,295評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,513評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,722評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,125評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,430評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,237評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,482評論 2 379

推薦閱讀更多精彩內容