AOP现在很火,网上有这许多支持AOP的框架,对于Delphi来说同样也有MeAOP。不过觉得这些框架太复杂了。

现在有一个系统,基本上都快结束了,整体上当然是没有采用什么AOP的框架。对于这样的系统能否用上AOP的一点点好处呢?

项目组提出在现有的系统上加入日志记录的需求。大家一起来看看我是怎么来实现这个功能的吧。

AOP简要说明

根据网上对AOP的解释,它具有下面的特征:

1、将通用功能从不相关类之中分离出来;

2、能够使得很多类共享一个功能,一旦功能发生变化,不必修改很多类,只要修改这个功能就可以了。

AOP的核心在于保持横切关注点的分离。

日志功能

这是一个比较典型的MIS系统,现在编码基本结束。不过某个开发人员接到了一个繁琐又看上去没什么技术含量的任务——实现日志功能。这个开发者就是本人了。

虽然没什么难度,但还是设计一下吧,谁让我是一个自诩为高水平的程序员呢。
 
 

一个设计图就这么做出来了。其中设计一个接口ILog来封装日志实现的细节。模块甲乙丙只需要使用接口ILog就可以。满足了XXX面向对象的设计原则。太完美了!

泡杯茶,然后开始写代码实现这个简单而庞大的任务。

开始编码了!

TLog,ILog实现比较简单,在此略去不谈。稍微修改一下以前模块的代码,将ILog接口传入每一个模块中。

接下来只需要实现日志功能的调用就可以了。

模块甲:

Pascal Code
1
2
3
4
5
6
7
8
9
10
 
procedure TModule1.acAction1Execute(Sender: TObject);
begin 
   //……
  Flog.LogCommand(“模块甲 操作一”);
end;
procedure TModule1.acAction2Execute(Sender: TObject);
begin 
   //……
  Flog.LogCommand(“模块甲 操作二”);
end;

模块乙:

Pascal Code
1
2
3
4
5
 
procedure TModule2.acAction1Execute(Sender: TObject);
begin 
  //……
  Flog.LogCommand(“模块乙 操作一”);
end;

就这样,写了大约二十几个地方,突然觉得自己太可悲了,作为一个高科技人才就干这种体力活吗?

坏味道的出现

在许许多多的地方都出现了Flog.LogCommand这样的函数调用,正是这些函数调用让我崩溃,在这么做下去估计我撑不到周末了。

“CV大法”已经让我觉得羞愧加恼怒,系统中到处出现了这样的重复代码。

无奈之中,我耷拉着脑袋走到一个同事桌前。

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

“怎么回事?”同事善意地问道。

“事情是这样子的……”

通过了一番讨论,我们一致认为这个应该用AOP的思想来解决。但怎样在Delphi中来实现AOP呢,修改整个程序框架是不可能的,我们只能在现有的基础上做。

正当我们要放弃的时候,突然想到了一个突破点:日志中记录的功能在程序实现的时候全部使用Action组件来做的,是否可以考虑在Action上面做文章呢?

曙光啊,曙光!

解决方式——瞒天过海

通俗点理解AOP,就是将一段代码统一“插入”某一类地方。但像Delphi这样的语言是很难实现“插入”代码的这一功能。不过我们可以通过事件机制来实现同样的效果。

Action的执行代码都写在事件OnExecute中,如果能在执行事件之前和之后执行我想要的动作是不是就可以解决了?

Pascal Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 
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扩展之后可以将其他的控件操作也通过这套机制记录到日志中。

很多同行们都埋怨自己做的是体力活,没什么技术含量。同样在刚开始的时候,我也认为这个任务是体力活,但是如果我们能勤于思考新的解决方法,体力活绝对能够变为技术活。只有这样才能不辜负“高科技”这个美誉啊。

上面介绍的方法肯定不是最好的,这次拿出来和大家分享,一方面是将自己的经验献给需要的朋友,另外也特别希望大家能给一点好的建议,一起交流,共同学习。

在Delphi中应用AOP实现日志功能的更多相关文章

  1. .Net中的AOP读书笔记系列之AOP介绍

    返回<.Net中的AOP>系列学习总目录 本篇目录 AOP是什么? Hello,World! 小结 本系列的源码本人已托管于Coding上:点击查看,想要注册Coding的可以点击该连接注 ...

  2. JVM学习--开启应用的gc日志功能

    一.开启方法 For Java 1.4, 5, 6, 7, 8 pass this JVM argument to your application: -XX:+PrintGCDetails -XX: ...

  3. Spring AOP 实现写事件日志功能

    什么是AOP?AOP使用场景?AOP相关概念?Spring AOP组件?如何使用Spring AOP?等等这些问题请参考博文:Spring AOP 实现原理 下面重点介绍如何写事件日志功能,把日志保存 ...

  4. Springboot中使用AOP统一处理Web请求日志

    title: Springboot中使用AOP统一处理Web请求日志 date: 2017-04-26 16:30:48 tags: ['Spring Boot','AOP'] categories: ...

  5. Spring Boot中使用AOP统一处理Web请求日志

    AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...

  6. Spring Boot中使用AOP记录请求日志

    这周看别人写的springboot后端代码中有使用AOP记录请求日志,以前没接触过,因此学习下. 一.AOP简介 AOP为Aspect Oriented Programming的缩写,意为:面向切面编 ...

  7. 46. Spring Boot中使用AOP统一处理Web请求日志

    在之前一系列的文章中都是提供了全部的代码,在之后的文章中就提供核心的代码进行讲解.有什么问题大家可以给我留言或者加我QQ,进行咨询. AOP为Aspect Oriented Programming的缩 ...

  8. Spring MVC 中使用AOP 进行统一日志管理--注解实现

    1.AOP简介 AOP称为面向切面编程 AOP的基本概念 (1)Aspect(切面):通常是一个类,里面可以定义切入点和通知 (2)JointPoint(连接点):程序执行过程中明确的点,一般是方法的 ...

  9. SpringBoot中使用AOP打印接口日志的方法(转载)

    前言 AOP 是 Aspect Oriented Program (面向切面)的编程的缩写.他是和面向对象编程相对的一个概念.在面向对象的编程中,我们倾向于采用封装.继承.多态等概念,将一个个的功能在 ...

随机推荐

  1. Leetcode 605.种花问题

    种花问题 假设你有一个很长的花坛,一部分地块种植了花,另一部分却没有.可是,花卉不能种植在相邻的地块上,它们会争夺水源,两者都会死去. 给定一个花坛(表示为一个数组包含0和1,其中0表示没种植花,1表 ...

  2. 聊聊、Java 命令 第一篇

    网上很多讲 Javac 和 Java 命令的,我觉得还是要自己写一写,做一个自己的总结,也方便以后查询. 开始之前先看看 help 命令,基本上任何一个软件都会提供这个命令. 没有什么比 -help ...

  3. JavaScript: __proto__和prototype

    图来源于:http://www.cnblogs.com/smoothLily/p/4745856.html 个人的理解: 1. 所有对象都有 __proto__属性,返回该对象的原型对象.例如f1由语 ...

  4. Eclipse 4.6(最新版本) js文件不能F3

    解决办法........我是没找到解决4.6版本的办法!不过可以换一个版本!猜想是因为 最新版本强制要求使用jdk1.8所导致的~!  换了一个4.5版本就一切Ok 换上主题一样漂亮护眼

  5. Mysql存储过程从0开始(上)

    1.首先你要明白,mysql也是一种语言,他也可以编写程序,也是支持逻辑判断,if,elseif,else,switch,while等等的判断 2.mysql赋值一个变量的值操作:set @a = 1 ...

  6. 2018 ACM南京网络赛H题Set解题报告

    题目描述 给定\(n\)个数$a_i$,起初第\(i\)个数在第\(i\)个集合.有三种操作(共\(m\)次): 1 $u$ $v$ 将第$u$个数和第$v$个数所在集合合并 2 $u$ 将第$u$个 ...

  7. bzoj 合集 1079 1791 1876 2208 2306

    1079 记忆化瞎搞吧,[a][b][c][d][e][l]表示当前有能涂1次的油漆a个,能涂2次的b个….前一个颜色为l,再搞下转移就行了. 1791 基环树上找直径 1876 高精度 2208 看 ...

  8. 【01】react 之 hello world

    React 起源于 Facebook 的内部项目,因为该公司对市场上所有 JavaScript MVC 框架,都不满意,就决定自己写一套,用来架设 Instagram 的网站.做出来以后,发现这套东西 ...

  9. SQLServer (2005/2008) 日志清理方法

    --数据库日志名称查询 USE DBNAME GO SELECT file_id, name,* FROM sys.database_files; GO------------------------ ...

  10. Maven 的基本用法

    一.Maven 的安装 二.Maven的常用构建命令 1.mvn -v 查看maven版本 2.mvn compile 编译 3.mvn test 测试 4.mvn package 打包 5.Mvn  ...