惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

罗磊的独立博客
SecWiki News
SecWiki News
酷 壳 – CoolShell
酷 壳 – CoolShell
爱范儿
爱范儿
量子位
M
MIT News - Artificial intelligence
GbyAI
GbyAI
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
TaoSecurity Blog
TaoSecurity Blog
博客园 - 【当耐特】
H
Heimdal Security Blog
腾讯CDC
The Last Watchdog
The Last Watchdog
Security Archives - TechRepublic
Security Archives - TechRepublic
Hacker News: Ask HN
Hacker News: Ask HN
S
Schneier on Security
Microsoft Security Blog
Microsoft Security Blog
WordPress大学
WordPress大学
博客园 - 司徒正美
Recent Commits to openclaw:main
Recent Commits to openclaw:main
C
Cybersecurity and Infrastructure Security Agency CISA
S
SegmentFault 最新的问题
大猫的无限游戏
大猫的无限游戏
Application and Cybersecurity Blog
Application and Cybersecurity Blog
F
Full Disclosure
有赞技术团队
有赞技术团队
T
Tailwind CSS Blog
Engineering at Meta
Engineering at Meta
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Threatpost
月光博客
月光博客
A
Arctic Wolf
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
雷峰网
雷峰网
T
Troy Hunt's Blog
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
The Cloudflare Blog
D
DataBreaches.Net
O
OpenAI News
L
LINUX DO - 最新话题
宝玉的分享
宝玉的分享
小众软件
小众软件
V
Vulnerabilities – Threatpost
A
About on SuperTechFans
人人都是产品经理
人人都是产品经理
T
The Exploit Database - CXSecurity.com
Martin Fowler
Martin Fowler
美团技术团队
P
Privacy International News Feed

博客园 - Do you know, jack?

js继承摘要 Css中position、float和clear整理 基于组织角色的权限设计 oracle最精简客户端(3个文件+1个path变量就搞定oracle客户端) oracle客户端免安装配置、64位机器PL/SQL和VS自带的IIS连接问题 Throw与Throw ex区别,记录日志的方法 oracle数据库连接方式 允许修改Svn注释 ctrl+Enter 自动加上 .com 而不是 .com.cn google chrome服务器hosts设置 Entity Framework关系映射容易出错的地方 CSS文件和JS文件组织 添加NotePad++到右键菜单 960 grid system的一点研究 如何查看NHibernate中生成的SQL web页面数据验证提醒方式 The RPC server is unavailable的解决方法 编写一份代码,支持多种布署方式 Enterprise Library 和 Spring.Net的比较
为什么要使用AOP?
Do you know, jack? · 2011-04-01 · via 博客园 - Do you know, jack?

在网上看到很多文章提到面向方面编程(Aspect-Oriented Programming),但一直没有搞清楚这样做有什么好处,为什么要使用AOP呢?

问题: 尽管面向对象编程与面向过程相比减少了代码的重复,但是它仍然留下了大量的重复代码。面向对象设计有助于最小化应用程序逻辑的代码重复,但是对于实现横切关注点的代码仍然很难实现模块化,例如日志,虽然我们可以使用类似于log4Net这样的类库来灵活的记录日志, 但是记录日志的代码却遍布于程序之中,其中有大量的重复。

使用AOP却可以很好的解决这方面的问题。

举个例子来说明一下这个问题:

假设有一个账户类,里面有存钱和取钱的简单操作,如下  

    /// <summary>
    
/// 账户类
    
/// </summary>
    public class Account 
    {
        
/// <summary>
        
/// 余额
        
/// </summary>
        public decimal Balance { getset; }        /// <summary>
        
/// 存钱
        
/// </summary>
        
/// <param name="amount"></param>
        public void Deposit(decimal amount)
        {
            
this.Balance += amount;
        }
/// <summary>
        
/// 取钱
        
/// </summary>
        
/// <param name="amount"></param>
        public void Withdraw(decimal amount)
        {
            
this.Balance -= amount;
        }

    } 

 现在需要记录一下日志,简单的方法如下,现在只是在Console中输出日志,实际项目中可能使用其它的日志模块来记录。

   /// <summary>
        
/// 存钱
        
/// </summary>
        
/// <param name="amount"></param>
        public void Deposit(decimal amount)
        {
            Console.WriteLine(
"Start Deposit: " + amount.ToString());
            
this.Balance += amount;
            Console.WriteLine(
"End Deposit");

        } 

 如果所有的方法都需要记录日志,则所有的方法需要像上面一样加上这些代码。

这种方法有很多问题: 

  1. 有大量的代码重复,不利于代码的长期维护。
  2. 核心代码和辅助日志代码混合在一起,日志代码分散了对方法核心逻辑的注意力,影响了方法的可读性。
  3. 如果需要移除日志代码,则面临着不小的工作量。
  4. 如果需要记录更多的信息,例如类名,则需要手工添加漏掉的类名。
  5. 如果决定记录异常,也面临着同样的问题,必须在很多地方重复记录。

使用像log4Net的类库来记录日志,虽然可以通过更改配置来关闭日志功能而不用修改任何代码,但是核心代码和辅助日志代码仍然混合在一起。

 使用AOP后, 我们可以将横切代码分离到几个方面中,而且很容易将这些方面应用于需要它们的类和方法上,除此之外, AOP还有大量的功能可以将业务规则从核心逻辑中分离出来。

下面来使用AOP来分离日志

修改后的代码如下     

    public interface IAccount
    {        
        
decimal Balance { getset; }void Deposit(decimal amount);    
    
        
void Withdraw(decimal amount);
    }

    public class Account : Business.IAccount
    {
        
/// <summary>
        
/// 余额
        
/// </summary>
        public decimal Balance { getset; }        /// <summary>
        
/// 存钱
        
/// </summary>
        
/// <param name="amount"></param>
        public void Deposit(decimal amount)
        {
            
this.Balance += amount;
        }
/// <summary>
        
/// 取钱
        
/// </summary>
        
/// <param name="amount"></param>
        public void Withdraw(decimal amount)
        {
            
this.Balance -= amount;
        }

   } 

  除了Account类实现接口IAccount外,什么都没有变化。

 日志功能的实现如下:

    public class ConsoleLogAspect : IMethodInterceptor
    {
        
public object Invoke(IMethodInvocation invocation)
        {
            Console.WriteLine(
"-- Start {0} --", invocation.Method.Name);
            
object returnValue = invocation.Proceed();
            Console.WriteLine(
"-- End {0} --", invocation.Method.Name);
            
return returnValue;
        }

    } 

客户端程序

    class Program
    {
        
static void Main(string[] args)
        {
            IApplicationContext ctx 
= ContextRegistry.GetContext();

            IAccount account 

= (IAccount)ctx.GetObject("MyAccount");

            account.Balance 

= 10;            
            account.Deposit(
15);
            account.Withdraw(
20);

            Console.ReadKey();
        }

    } 

 注意:代码中客户端程序只使用接口,不使用Account类

配置文件内容, 通过配置文件来配置IAccount。

 <configuration>

    <configSections>
        
<sectionGroup name="spring">
            
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        
</sectionGroup>
    
</configSections><spring>
        
<context>
            
<resource uri="config://spring/objects"/>
        
</context>
        
<objects xmlns="http://www.springframework.net" ><object id="ConsoleLogAspect" type="Spring.Aop.Support.NameMatchMethodPointcutAdvisor, Spring.Aop">
                
<property name="Advice">
                    
<object type="Aspects.ConsoleLogAspect, Aspects" />
                
</property>
                
<property name="MappedNames">
                    
<list>
                        
<value>Deposit</value>
                        
<value>Withdraw</value>
                    
</list>
                
</property>
            
</object><object id="MyAccount" type="Spring.Aop.Framework.ProxyFactoryObject">
                
<property name="Target">
                    
<object type="Business.Account, Business" />
                
</property>
                
<property name="InterceptorNames">
                    
<list>
                        
<value>ConsoleLogAspect</value>
                    
</list>
                
</property>
            
</object></objects>
    
</spring>
</configuration>

 运行结果:

 

 日志已经被记录下来了。

 对比一下使用AOP的好处: 

  1. 核心代码中不在包含记录日志的代码, 类更专注于它的职责。
  2. 如果想修改日志的记录方式, 不需要修改Account类,只需要修改相应的配置文件和日志的实现,修改工作量小。
  3. 使用这种方式可以动态的修改业务规则而不用修改Account类, 也不用重新编译发布。例如Account.WithDraw()方法的核心是减少账户余额,假设程序布署后客户又来一新的需求,当账户余额小于50元时,发邮件通知客户,此时只需要像添加日志一样实现这一功能,配置到程序上去就好了。最适合移入方面的业务规则是那些在实现核心逻辑的同时也需要实现的二级逻辑的规则。
  4. 实现AOP的同时需要依赖注入, 这又会减少模块间的依赖。
  5. 依赖注入要用到Ioc容器, 这个又可以代替工厂类,这算是又一个好处吧

 大家觉得呢?

下载 AOP Demo文件