我正在努力理解为什么需要像[{3}}这样的后期编译器?
我的理解是它只是在原始代码中插入代码,所以为什么开发人员不会自己编写代码呢?
我希望有人会说它更容易编写,因为你可以使用方法上的属性,然后不会混淆它们的样板代码,但这可以使用DI或反射和没有后期编译器的预先考虑。我知道,因为我已经说过反射,性能大象现在会进入 - 但我不关心这里的相对性能,当大多数场景的绝对性能是微不足道的时(亚毫秒到毫秒)。
答案 0 :(得分:22)
让我们尝试在这个问题上采取架构点。假设你是一名建筑师(每个人都想成为一名建筑师;) 您需要为您的团队提供架构: 一组选定的库,架构模式和设计模式。作为设计的一部分,您说:“我们将使用以下设计模式实现缓存:”
string key = string.Format("[{0}].MyMethod({1},{2})", this, param1, param2 );
T value;
if ( !cache.TryGetValue( key, out value ) )
{
using ( cache.Lock(key) )
{
if (!cache.TryGetValue( key, out value ) )
{
// Do the real job here and store the value into variable 'value'.
cache.Add( key, value );
}
}
}
这是一种正确的跟踪方式。开发人员将实现这种模式数千次,因此您编写了一个很好的Word文档,告诉您希望如何实现该模式。是的,Word文档。你有更好的解决方案吗?我怕你没有。经典代码生成器无济于事。功能编程(代表)?它在某些方面工作得相当好,但不是在这里:你需要将方法参数传递给模式。那剩下什么了?用自然语言描述模式,并信任开发人员实现它们。
会发生什么?
首先,一些初级开发人员将查看代码并告诉“Hm。两个缓存查找。有点无用。一个就够了。” (这不是一个笑话 - 向DNN团队询问此问题)。而且你的模式不再是线程安全的。
作为架构师,您如何确保正确应用模式?单元测试?很公平,但你很难用这种方式检测线程问题。代码审查?这可能是解决方案。
现在,你决定改变模式是什么?例如,您检测到缓存组件中的错误并决定使用自己的错误?你要编辑成千上万的方法吗?这不仅仅是重构:如果新组件具有不同的语义会怎样?
如果您决定不再缓存某个方法,该怎么办?删除缓存代码有多难?
AOP解决方案(无论框架是什么)与普通代码相比具有以下优势:
所以,如果你把它们放在一起:
关于这个话题,我有90分钟的谈话,您可以在http://vimeo.com/2116491观看。
同样,AOP的架构优势与您选择的框架无关。框架之间的差异(也在本视频中讨论)主要影响您将AOP应用于代码的程度,这不是这个问题的重点。
答案 1 :(得分:11)
假设您已经拥有一个设计良好,经过良好测试的类等。您希望在某些方法上轻松添加一些时序。是的,您可以使用依赖注入,创建一个代理原始但与每个方法的时间相关的装饰器类 - 但即使该类也将是一堆重复......
...或者您可以在混音中添加反射并使用某些描述的动态代理,这可以让您编写一次定时代码,但需要您将反射代码恰到好处 - 这并不像它可能是,特别是如果涉及泛型。
...或者你可以为你想要定时的每个方法添加一个属性,编写一次时间代码,并将其作为后编译步骤应用。
我知道哪个看起来更优雅 - 在阅读代码时更加明显。它甚至可以在DI 不适合的情况下应用(并且它实际上不适合系统中的每个单个类)并且在其他地方没有其他更改。
答案 2 :(得分:4)
AOP(PostSharp)用于将代码从一个位置附加到应用程序中的各种点,因此您不必将其放在那里。
你无法实现PostSharp可以用Reflection做什么。
在生产系统中,我个人并没有看到它的大用处,因为大多数事情都可以通过其他更好的方式(日志记录等)来完成。
您可能希望查看有关此问题的其他主题:
答案 3 :(得分:3)
方面拿走了所有副本&粘贴 - 代码并更快地添加新功能。
我讨厌的只是,例如,不得不一遍又一遍地编写同一段代码。 Gael在他的网站(www.postsharp.net)上有一个关于INotifyPropertyChanged的非常好的例子。
这正是AOP的用途。忘记技术细节,只需实现您的要求。
从长远来看,我认为我们都应该告诉我们现在编写软件的方式。编写样板代码并手动迭代是一件非常繁琐且显而易见的事。
未来属于声明性的,功能性的风格,由面向对象的框架结合在一起 - 并且交叉关注的问题由方面处理。
我想唯一不会很快得到它的人就是那些仍然需要支付代码行的人。