AOP:使用命令模式實現AOP


背景

某位大牛說過,采用命名模式的好處是,你可以將命令按照不同的方式執行,如:排隊、異步、遠程和攔截等等。今天我介紹一下如何攔截命令的執行,這有些AOP的味道。

思路

就是一個管道過濾器而已

實現

先不考慮處理器的實例化和過濾器列表的實例化,如果給你一個命令、一些過濾器和一個處理器,讓你組裝為一個管道應該不是啥大問題。

這部分概念雖然簡單,可是也不見得好理解,因此我基本把全部代碼都貼上了,建議不太明白的同學,自己重寫一遍,加深對管道過濾器的理解。

核心代碼

命令接口

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Happy.Command
 8 {
 9     /// <summary>
10     /// 命令接口。
11     /// </summary>
12     public interface ICommand
13     {
14     }
15 }

命令處理器接口,一個命令只能有一個命令處理器。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Happy.Command
 8 {
 9     /// <summary>
10     /// 命令處理器接口,一個命令只能有一個命令處理器。
11     /// </summary>
12     public interface ICommandHandler<TCommand>
13         where TCommand : ICommand
14     {
15         /// <summary>
16         /// 處理命令。
17         /// </summary>
18         void Handle(TCommand command);
19     }
20 }

命令攔截器,攔截正在被執行的命令。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Happy.Command
 8 {
 9     /// <summary>
10     /// 命令攔截器,攔截正在被執行的命令。
11     /// </summary>
12     [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
13     public abstract class CommandInterceptorAttribute : Attribute
14     {
15         /// <summary>
16         /// 構造方法。
17         /// </summary>
18         /// <param name="order">指示攔截器在管道中的位置</param>
19         protected CommandInterceptorAttribute(int order)
20         {
21             this.Order = order;
22         }
23 
24         /// <summary>
25         /// 攔截正在被執行的命令。
26         /// </summary>
27         /// <param name="context">命令執行上下文</param>
28         public abstract void Intercept(ICommandExecuteContext context);
29 
30         /// <summary>
31         /// 攔截器在管道中的位置。
32         /// </summary>
33         public int Order { get; protected set; }
34     }
35 }

命令執行上下文接口,代表了一次命令的執行過程。

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Happy.Command
 8 {
 9     /// <summary>
10     /// 命令執行上下文接口,代表了一次命令的執行過程。
11     /// </summary>
12     public interface ICommandExecuteContext
13     {
14         /// <summary>
15         /// 命令執行服務。
16         /// </summary>
17         ICommandService CommandService { get; }
18 
19         /// <summary>
20         /// 正在執行的命令。
21         /// </summary>
22         ICommand Command { get; }
23 
24         /// <summary>
25         /// 執行下一個<see cref="CommandInterceptorAttribute"/>,如果已經是最后一個,就會執行<see cref="ICommandHandler{TCommand}"/>26         /// </summary>
27         void ExecuteNext();
28     }
29 }
 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 using Happy.ExtensionMethod.Reflection;
 8 
 9 namespace Happy.Command.Internal
10 {
11     internal sealed class CommandExecuteContext : ICommandExecuteContext
12     {
13         private CommandInterceptorChain _commandInterceptorChain;
14 
15         internal CommandExecuteContext(ICommandService commandService, ICommand command, Action commandExecutor)
16         {
17             this.CommandService = commandService;
18             this.Command = command;
19             _commandInterceptorChain = new CommandInterceptorChain(
20                 this,
21                 command.GetType().GetAttributes<CommandInterceptorAttribute>(),
22                 commandExecutor);
23         }
24 
25 
26         public ICommandService CommandService
27         {
28             get;
29             private set;
30         }
31 
32         public ICommand Command { get; private set; }
33 
34         public void ExecuteNext()
35         {
36             _commandInterceptorChain.ExecuteNext();
37         }
38     }
39 }

管道過濾器的內部實現

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 namespace Happy.Command.Internal
 8 {
 9     internal sealed class CommandInterceptorChain
10     {
11         private ICommandExecuteContext _commandExecuteContext;
12         private CommandInterceptorAttribute[] _commandInterceptors;
13         private Action _commandExecutor;
14         private int _currentCommandInterceptorIndex = -1;
15 
16         internal CommandInterceptorChain(
17             ICommandExecuteContext commandExecuteContext,
18             CommandInterceptorAttribute[] commandInterceptors,
19             Action commandExecutor)
20         {
21             _commandExecuteContext = commandExecuteContext;
22             _commandInterceptors = commandInterceptors.OrderBy(x => x.Order).ToArray();
23             _commandExecutor = commandExecutor;
24         }
25 
26         private CommandInterceptorAttribute CurrentCommandInterceptor
27         {
28             get
29             {
30                 return _commandInterceptors[_currentCommandInterceptorIndex];
31             }
32         }
33 
34         internal void ExecuteNext()
35         {
36             _currentCommandInterceptorIndex++;
37 
38             if (_currentCommandInterceptorIndex < _commandInterceptors.Length)
39             {
40                 this.CurrentCommandInterceptor.Intercept(_commandExecuteContext );
41             }
42             else
43             {
44                 _commandExecutor();
45             }
46         }
47     }
48 }

命令服務,負責執行命令

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Threading;
 7 
 8 using Common.Logging;
 9 using Microsoft.Practices.ServiceLocation;
10 
11 using Happy.DesignByContract;
12 
13 namespace Happy.Command.Internal
14 {
15     internal sealed class DefaultCommandService : ICommandService
16     {
17         private readonly Dictionary<Type, object> _services = new Dictionary<Type, object>();
18 
19         public void Execute<TCommand>(TCommand command)
20             where TCommand : ICommand
21         {
22             command.MustNotNull("command");
23 
24             var context = this.CreateCommandExecuteContext(command);
25 
26             context.ExecuteNext();
27         }
28 
29         public ICommandService AddService<T>(T service)
30         {
31             _services[typeof(T)] = service;
32 
33             return this;
34         }
35 
36         public T GetService<T>()
37         {
38             return (T)_services[typeof(T)];
39         }
40 
41         private CommandExecuteContext CreateCommandExecuteContext<TCommand>(TCommand command)
42             where TCommand : ICommand
43         {
44 
45             return new CommandExecuteContext(this, command, () =>
46             {
47                 this.ExecuteCommandHandler(command);
48             });
49         }
50 
51         private void ExecuteCommandHandler<TCommand>(TCommand command)
52             where TCommand : ICommand
53         {
54             ServiceLocator.Current.MustNotNull("ServiceLocator.Current");
55 
56             var commandHandler = ServiceLocator
57                 .Current
58                 .GetInstance<ICommandHandler<TCommand>>();
59 
60             commandHandler.Handle(command);
61         }
62     }
63 }

事務攔截器

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 using System.Transactions;
 7 
 8 namespace Happy.Command
 9 {
10     /// <summary>
11     /// 事務攔截器。
12     /// </summary>
13     public sealed class TransactionAttribute : CommandInterceptorAttribute
14     {
15         /// <inheritdoc />
16         public TransactionAttribute(int order) : base(order) { }
17 
18         /// <inheritdoc />
19         public override void Intercept(ICommandExecuteContext context)
20         {
21             using (var ts = new TransactionScope())
22             {
23                 context.ExecuteNext();
24 
25                 ts.Complete();
26             }
27         }
28     }
29 }

應用事務攔截器

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Text;
 5 using System.Threading.Tasks;
 6 
 7 using Happy.Domain;
 8 using Happy.Command;
 9 using Happy.DesignByContract;
10 
11 namespace Happy.Application
12 {
13     /// <summary>
14     /// 簡單的創建命令。
15     /// </summary>
16     [Transaction(1)]
17     public abstract class SimpleCreateCommand<TAggregateRoot> : SimpleCommand<TAggregateRoot>
18         where TAggregateRoot : AggregateRoot
19     {
20     }
21 }

執行命令

 1         /// <summary>
 2         /// 創建。
 3         /// </summary>
 4         public ActionResult Create(TAggregateRoot item)
 5         {
 6             this.CurrentCommandService.Execute(new TCreateCommand
 7             {
 8                 Aggregate = item
 9             });
10 
11             return this.NewtonsoftJson(new
12             {
13                 success = true,
14                 items = this.GetById(item.Id)
15             });
16         }

備注

這里的命令模式本質上是一種消息模式,因為命令里沒有任何行為,將行為獨立了出來。像WCF、ASP.NET和ASP.NET MVC本質上也是消息模式,他們也內置了管道過濾器模式。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM