zoukankan      html  css  js  c++  java
  • 第五章 面向方面编程___AOP入门

      上一篇讲了 AOP 和 OOP 的区别,这一次我们开始入门 AOP 。实现面向方面编程的技术,主要分为两大类:

    一是 采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行;

    二是 采用静态织入的方式,引入特定的语法创建 “方面”,从而使得编译器可以在编译期间织入有关 “方面” 的代码。

    然而殊途同归,实现 AOP 的技术特性却是相同的,分别为:

    crosscutting concerns (横切性关注点):一个关注点(concern)就是一个特定的目的,一块我们要完成的区域,一段我们需要的逻辑行为。从技术的角度来说,一个典型的软件系统包含一些核心的关注点和系统级的关注点。举个例子来说,一个银行支付系统的核心关注点是存入/支付处理,而系统级的关注点则是日志、事务完整性、权限、安全及性能问题等,许多关注点(即横切关注点)会在很多个模块中出现。如果使用现有的编程方法,横切关注点会横越多个模块,结果是使系统变得越来越复杂,难以设计和实现。通过面向切面编程的方式能够更好地分离系统关注点,从而提供模块化的横切关注点。

    aspect(切面):横切性关注点的抽象即为切面,与类相似,只是两者的关注点不一样。类是对物体特征的抽象,切面是对横切性关注点的抽象。

    join point(连接点):所谓连接点就是指那些些被拦截到的点。在Spring中这些连接点指的就是方法,因为在Spring中只支持方法类型的连接点。实际上连接点还可以是构造函数或者字段。通俗的说是程序执行中的一个精确执行点,例如类中的一个方法。它是一个抽象的概念,在实现面向切面编程时,并不需要去定义一个join point。

    point cut(切入点):切入点就是指我们要对那些方法进行拦截和定义。

    advice(通知):通知就是指我们拦截到方法之后,要做的事情。Spring中有前置通知,后置通知,异常通知,最终通知,环绕通知。

    target object(目标对象):指包含连接点的对象。也称为被通知或被代理对象。

    weave(织入):将切面应用到目标对象,并导致代理对象创建的过程叫做织入

    Introduction(引入):运行期间,在不修改代码的情况下,动态的为类添加方法和字段。通俗的将就是为对象引入附加的方法或属性。

     

    使用代理模式实现面向切面编程

      下面我们使用代理模式来模拟一下,现面向切面编程。通过上面那幅图,我们看到使用面向切面编程将核心业务和其他业务(日志记录,性能检测等)分离开来。这次我们模拟一下,银行支付中记录日志的问题。

    我们有 两个接口,ICard,ILogger , ICard 表示 卡的接口,ILogger 表示 日志记录接口。

    卡接口:

     1 namespace CnblogLesson_5_1.Interface
     2 {
     3     /// <summary>
     4     /// 5     /// </summary>
     6     interface ICard
     7     {
     8         //存入
     9         void Deposit(double money);
    10 
    11         //支出
    12         void Pay(double money);
    13     }
    14 }

    日志接口:

     1 namespace CnblogLesson_5_1.Interface
     2 {
     3     //日志
     4     interface ILogger
     5     {
     6         /// <summary>
     7         /// 写入日志
     8         /// </summary>
     9         void LogWrite(string message);
    10     }
    11 }

    卡的实现:

     1 using System;
     2 using CnblogLesson_5_1.Interface;
     3 
     4 namespace CnblogLesson_5_1.Impl
     5 {
     6     class Card : ICard
     7     {
     8         //存入
     9         public void Deposit(double money)
    10         {
    11             Console.WriteLine("存入:{0}" ,money);
    12             Logger log = new Logger();
    13             log.LogWrite("日志:" + DateTime.Now.ToShortTimeString() +"存入" + money.ToString());
    14         }
    15 
    16         //支出
    17         public void Pay(double money)
    18         {
    19             Console.WriteLine("支出:{0}", money);
    20             Logger log = new Logger();
    21             log.LogWrite("日志:" + DateTime.Now.ToShortTimeString() + "支出" + money.ToString());
    22         }
    23     }
    24 }

    日志的实现:

     1 using System;
     2 using CnblogLesson_5_1.Interface;
     3 
     4 namespace CnblogLesson_5_1.Impl
     5 {
     6     class Logger : ILogger
     7     {
     8         public void LogWrite(string message)
     9         {
    10             Console.WriteLine("写入到SQL Server 数据库中:" + message);
    11         }
    12     }
    13 }

    调用:

     1 using System;
     2 using CnblogLesson_5_1.Interface;
     3 using CnblogLesson_5_1.Impl;
     4 
     5 namespace CnblogLesson_5_1
     6 {
     7     class Program
     8     {
     9         static void Main(string[] args)
    10         {
    11             ICard card = new Card();
    12 
    13             card.Deposit(100);
    14             card.Pay(100);
    15 
    16             Console.ReadKey();
    17 
    18         }
    19     }
    20 }

    输出结果:

     
    以上方式的缺陷:
      我们的核心业务(存入/取钱)与记录日志本不该彼此纠缠在一起的责任却纠缠在一起,增加了我们系统的复杂性。
     
    下面使用代理模式模拟 AOP 实现 核心业务与日志记录的解耦:
     
    ICard 和 ILogger 接口,还是那些接口,日志的实现还是日志的实现,没有做改动。
    现在,存款和取款的 实现只做自己的业务,无需进行日志记录:
     1 using System;
     2 using 代理模式模拟AOP.Interface;
     3 
     4 namespace 代理模式模拟AOP.Impl
     5 {
     6     class Card : ICard
     7     {
     8         //存入
     9         public void Deposit(double money)
    10         {
    11             Console.WriteLine("存入:{0}" ,money);
    12         }
    13 
    14         //支出
    15         public void Pay(double money)
    16         {
    17             Console.WriteLine("支出:{0}", money);
    18         }
    19     }
    20 }

    现在增加代理类 ProxyCard:

     1 using System;
     2 using 代理模式模拟AOP.Interface;
     3 using 代理模式模拟AOP.Impl;
     4 
     5 namespace 代理模式模拟AOP.Proxy
     6 {
     7     public class ProxyCard
     8     {
     9         private ICard target;
    10 
    11         public ProxyCard(ICard target)
    12         {
    13             this.target = target;
    14         }
    15 
    16         public void Invoke(string method, object[] parameters)
    17         {
    18             if (target != null)
    19             {
    20                 ILogger log = new  Logger();
    21                 double money = double.Parse(parameters[0].ToString());
    22                 switch (method) {
    23                     case "Pay":
    24                         log.LogWrite("日志:" + DateTime.Now.ToShortTimeString() + "支出" + money.ToString());
    25                         break;
    26                     case "Deposit":
    27                         log.LogWrite("日志:" + DateTime.Now.ToShortTimeString() + "存入" + money.ToString());
    28                         break;
    29                 }
    30                 Type type = target.GetType();
    31                 type.GetMethod(method).Invoke(target, parameters);
    32             }
    33         }
    34     }
    35 
    36 }

    调用:

     1 using System;
     2 using 代理模式模拟AOP.Interface;
     3 using 代理模式模拟AOP.Impl;
     4 using 代理模式模拟AOP.Proxy;
     5 
     6 namespace 代理模式模拟AOP
     7 {
     8     class Program
     9     {
    10         static void Main(string[] args)
    11         {
    12             ICard card = new Card();
    13 
    14             ProxyCard proxy = new ProxyCard(card);
    15             proxy.Invoke("Pay", new object[] { 100 });
    16             proxy.Invoke("Deposit", new object[] { 100 });
    17 
    18             Console.ReadKey();
    19 
    20         }
    21     }
    22 }

    执行结果:

     
    通过 ProxyCard 代理对象,我们实现了对方法的拦截,在调用之前进行日志记录的操作。实现了我们的核心业务(存入/支出)与日志记录的分离,从而降低了系统的耦合。
     
  • 相关阅读:
    700. Search in a Binary Search Tree
    100. Same Tree
    543. Diameter of Binary Tree
    257. Binary Tree Paths
    572. Subtree of Another Tree
    226. Invert Binary Tree
    104. Maximum Depth of Binary Tree
    1、解决sublime打开文档,出现中文乱码问题
    移植seetafaceengine-master、opencv到ARM板
    ubuntu16.04-交叉编译-SeetaFaceEngine-master
  • 原文地址:https://www.cnblogs.com/hexu6788/p/2973193.html
Copyright © 2011-2022 走看看