程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 在Delphi中應用AOP實現日志功能

在Delphi中應用AOP實現日志功能

編輯:Delphi

AOP現在很火,網上有這許多支持AOP的框架,對於Delphi來說同樣也有MeAOP。不過覺得這些框架太復雜了。

現在有一個系統,基本上都快結束了,整體上當然是沒有采用什麼AOP的框架。對於這樣的系統能否用上AOP的一點點好處呢?

項目組提出在現有的系統上加入日志記錄的需求。大家一起來看看我是怎麼來實現這個功能的吧。

AOP簡要說明

根據網上對AOP的解釋,它具有下面的特征:

1、將通用功能從不相關類之中分離出來;

2、能夠使得很多類共享一個功能,一旦功能發生變化,不必修改很多類,只要修改這個功能就可以了。

AOP的核心在於保持橫切關注點的分離。

日志功能

這是一個比較典型的MIS系統,現在編碼基本結束。不過某個開發人員接到了一個繁瑣又看上去沒什麼技術含量的任務——實現日志功能。這個開發者就是本人了。

雖然沒什麼難度,但還是設計一下吧,誰讓我是一個自诩為高水平的程序員呢。

一個設計圖就這麼做出來了。其中設計一個接口ILog來封裝日志實現的細節。模塊甲乙丙只需要使用接口ILog就可以。滿足了XXX面向對象的設計原則。太完美了!

泡杯茶,然後開始寫代碼實現這個簡單而龐大的任務。

開始編碼了!

TLog,ILog實現比較簡單,在此略去不談。稍微修改一下以前模塊的代碼,將ILog接口傳入每一個模塊中。

接下來只需要實現日志功能的調用就可以了。

模塊甲:

procedure TModule1.acAction1Execute(Sender: TObject);
begin
……
Flog.LogCommand(“模塊甲 操作一”);
end;
procedure TModule1.acAction2Execute(Sender: TObject);
begin
……
Flog.LogCommand(“模塊甲 操作二”);
end;

模塊乙:

procedure TModule2.acAction1Execute(Sender: TObject);
begin
……
Flog.LogCommand(“模塊乙 操作一”);
end;

就這樣,寫了大約二十幾個地方,突然覺得自己太可悲了,作為一個高科技人才就干這種體力活嗎?

壞味道的出現

在許許多多的地方都出現了Flog.LogCommand這樣的函數調用,正是這些函數調用讓我崩潰,在這麼做下去估計我撐不到周末了。

“CV方法”已經讓我覺得羞愧加惱怒,系統中到處出現了這樣的重復代碼。

無奈之中,我耷拉著腦袋走到一個同事桌前。

“嘿,救救我吧,我想解脫”

“怎麼回事?”同事善意地問道。

“事情是這樣子的……”

通過了一番討論,我們一致認為這個應該用AOP的思想來解決。但怎樣在Delphi中來實現AOP呢,修改整個程序框架是不可能的,我們只能在現有的基礎上做。

正當我們要放棄的時候,突然想到了一個突破點:日志中記錄的功能在程序實現的時候全部使用Action組件來做的,是否可以考慮在Action上面做文章呢?

曙光啊,曙光!

解決方式——瞞天過海

通俗點理解AOP,就是將一段代碼統一“插入”某一類地方。但像Delphi這樣的語言是很難實現“插入”代碼的這一功能。不過我們可以通過事件機制來實現同樣的效果。

Action的執行代碼都寫在事件OnExecute中,如果能在執行事件之前和之後執行我想要的動作是不是就可以解決了?

procedure TModule1.acAction1Execute(Sender: TObject);
begin
// do something
end;
procedure TActionHook.RegisterAction(Action: TAction);
begin
// 記錄Action與原始的OnExecute事件
FActionList.Add(Action);
SetLength(FActionEvents, Length(FActionEvents) + 1);
FActionEvents[High(FActionEvents)] := Action.OnExecute;
// 瞞天過海,偷換事件
Action.OnExecute := HookActionExecute;
end;
procedure TActionHook.HookActionExecute(ASender: TObject);
begin
DoBeforeActionExecute(TAction(ASender));
// 觸發原始事件
FActionEvents[FActionList.IndexOf(ASender)](ASender);
DoAfterActionExecute(TAction(ASender));
end;
procedure TActionHook.DoAfterActionExecute(Action: TAction);
begin
// 所有的Action執行完畢後調用此處
FLog.LogCommand(Action.Caption);
end;

相關的UML圖如下:

采用這樣的方式後,很明顯我們不需要將日志相關代碼分散到系統的各個地方,只需要在一個統一的地方將所有Form上的Action組件注冊到TActionHook中就可以了。

擴展思考

在Delphi中可以通過事件的機制實現代碼注入技術,當然同樣在其他支持事件的語言中也可以實現。相比之下這種方法實現AOP比較簡單,並且不需要在系統的整體結構上作什麼調整,完全通過語言層面支持。

例子中針對Action的組件來處理日志功能,將TActionHook擴展之後可以將其他的控件操作也通過這套機制記錄到日志中。

很多同行們都埋怨自己做的是體力活,沒什麼技術含量。同樣在剛開始的時候,我也認為這個任務是體力活,但是如果我們能勤於思考新的解決方法,體力活絕對能夠變為技術活。只有這樣才能不辜負“高科技”這個美譽啊。

上面介紹的方法肯定不是最好的,這次拿出來和大家分享,一方面是將自己的經驗獻給需要的朋友,另外也特別希望大家能給一點好的建議,一起交流,共同學習。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved