通过 IoC 用属性影响 AOP;代码味道还是优雅?

发布于 2024-08-20 21:11:38 字数 593 浏览 1 评论 0原文

我目前正在使用 StructureMap,通常使用基于约定的 (Scan()) 自动配置,并且我希望将基于装饰器的缓存添加到管道。

如果我手动配置它,那很好,但是当您获得大量依赖项时,Scan() 非常方便...我正在考虑缓存关于界面的建议,例如:

public interface IFoo {
    [CacheDuration(20)] // cache for 20 minutes
    string[] DoSomethingReusable();

    SomeType DoSomethingNonReusable(int key); // not cached
}

其想法是,通过在 StructureMap 的扫描中添加自定义“约定”(非常简单),它可以发现一个或多个-更多方法被装饰用于缓存,并自动将生成的缓存装饰器注入到该类型的管道中(从接口/方法名称和参数值生成缓存键)。

从好的方面来说,它使得添加缓存变得非常轻松——只需稍微装饰一下界面即可;但这是代码味道吗?和/或我是否在重复已经解决的问题?

I'm using StructureMap at the moment, generally with convention-based (Scan()) auto-configuration, and I'm looking to add decorator-based caching into the pipeline.

If I configure it manually that is fine, but Scan() is just so convenient when you get lots of dependencies... I'm toying with noting cache suggestions on the interface(s), for example:

public interface IFoo {
    [CacheDuration(20)] // cache for 20 minutes
    string[] DoSomethingReusable();

    SomeType DoSomethingNonReusable(int key); // not cached
}

with the idea being that by adding a custom "convention" to StructureMap's scanning (pretty easy) it can spot that one-or-more methods are decorated for caching, and automatically inject a generated caching decorator into that type's pipeline (generating a cache key from the interface/method name and parameter values).

On the plus side it makes adding caching very painless - just decorate the interface a little; but is is a code smell? And/or am I duplicating something that is already solved?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

生生不灭 2024-08-27 21:11:38

如果您意识到两个主要因素,那么属性就可以了:

  • 本质上是分散的结构,而不是在一个地方,您可以将元数据撒在它接触的每个组件上

  • 严格的编译器常量结构。如果您突然决定缓存持续时间应该为 10,而不是 20,则必须重新编译所有代码。您可以将属性路由到诸如 config 之类的东西,但这是围绕属性工作的,此时您应该真正重新考虑使用它们是否是最好的主意。

只要您意识到这些问题并接受它们,就可以继续。

Attributes are OK if you're aware of two major factors:

  • inherently decentralized structure, instead of in one place you have your metadata sprinkled on each and every component it touches

  • rigid compiler constant structure. If you suddenly decide that your cache duration should be 10, not 20, you have to recompile all your code. You could route attributes to things like config, but that's working around the attributes, and at this point you should really reconsider whether using them was the best idea in the first place.

As long as you're aware of these issues, and OK with them, than go ahead.

絕版丫頭 2024-08-27 21:11:38

据我了解这个问题,您主要考虑添加属性以使容器注册更容易。您仍在使用装饰器设计模式来实现缓存,在我看来,这是实现缓存的正确方法。

我们在 Safewhere 中也做了同样的事情,但我们使用基于约定的注册。我们还使用 Castle Windsor,所以我不知道这是否可以通过 StructureMap 实现,但我们只需在名为 Caching*Repository 的任何内容之后扫描相应的程序集,并将它们注册为真实存储库的装饰器。我们还有一个基于约定的单元测试,用于验证是否存在所有必需的缓存存储库。

添加自定义属性是否有代码异味取决于您对代码的可重用性要求程度。我的经验法则是,我希望能够将所有内容与Poor Man's DI连接起来。我仍然使用 DI 容器,但这条规则为我提供了健全性检查。

一般来说,我不喜欢将我的代码耦合到特定容器,但我无法真正弄清楚您是否在这里这样做。这取决于您是否需要引用 StructureMap 来定义自定义属性。如果你必须参考结构图,我会认为它是一种气味。

As far as I understand the question, you are mainly considering adding the Attributes to make container registration easier. You are still implementing the caches using the Decorator design pattern, which is, IMO, the correct way to implement caching.

We do the same in Safewhere, but we use convention-based registration instead. We also use Castle Windsor, so I don't know if this is possible with StructureMap, but we simply scan the appropriate assemblies after anything named Caching*Repository and register those as Decorators for the real Repositories. We also have a convention-based unit test that verifies that all required Caching Repositories are present.

Whether adding the custom attribute is a code smell or not depends on the degree of reusability you require of your code. My rule of thumb is that I want to be able to wire everything up with Poor Man's DI. I still use a DI Container instead, but this rule provides me with a sanity check.

In general I don't like coupling my code to a particular container, but I can't really figure out whether you are doing that here. It depends on whether or not you need to reference StructureMap to define the custom attribute. If you must reference Structure Map, I would consider it a smell.

微暖i 2024-08-27 21:11:38

我在这里看到的唯一缺点是没有缓存对象的中心位置。通常有一个特定的层来完成缓存。以你的技术,情况并非如此。

我还认为,决定是否应该缓存某些内容是代码的责任,而不是类/接口的责任。

但要回答你的问题,这是代码味道吗?我想说不,但是一旦在很多地方使用了这个系统,新开发人员可能会感到困惑。

The only drawback I see here is that there is no central spot where objects are cached. Usually there is one specific layer where caching is done. With your technique, this is not the case.

I also think it's the responsibility of your code to decide whether something should be cached or not, not that of a class/interface.

But to answer your question, is this a code smell? I'd say no, but it might be confusing for new developers to get the hang of this system once it's used in a lot of places.

套路撩心 2024-08-27 21:11:38

我不能说我完全理解所有内容,但对于我的 2 美分来说,这似乎可以说是没问题;但我只是想知道是否可以在不重新编译的情况下轻松更改它。也许您希望通过配置条目而不是编译来更改缓存持续时间。我可能会将这些数据存储在可以更轻松配置的地方。 (也许是我理解错了……)

I can't say I totally understand everything, but for my 2 cents, it seems arguably okay; but I just wonder whether or not it's easy to change it without recompilation. And maybe you would wish to change that cache duration via config entry, and not a compile. I'd probably store that data somewhere where I can configure easier. (Perhaps I've misunderstood though ...)

随梦而飞# 2024-08-27 21:11:38

我倾向于同意关于缓存必须由代码决定的说法,但我认为您可能需要考虑的另一种情况是“需要缓存什么,整个对象?

您可能想玩弄具有属性来设置您不想缓存对象的哪些成员的想法。

也许是属性

[Cache(Duration=20, Location(...))]

I tend to agree with what was said about the caching having to be decided by your code but another scenario that I see that you might want to take into consideration is the "What needs to be cached, the entire object?

You might want to toy with the idea of having properties to set which members you don't want to cache of an object.

Maybe an Attribute

[Cache(Duration=20, Location(...))]

?

记忆消瘦 2024-08-27 21:11:38

我认为每当使用属性时都需要考虑的是意图与类的耦合。关键是您不能在一个缓存持续时间为 10 秒的地方和另一个缓存持续时间为 60 秒的地方使用该实体。恕我直言,这始终是与任何类型的属性的权衡。

I think something to consider whenever you are using attributes is the coupling of the intent with the class. The point being that you cannot use that entity in once place with a cache duration of 10 seconds and another place with a cache duration of 60 seconds. IMHO this is always the trade-off with attributes of any kind.

浅忆 2024-08-27 21:11:38

我在这个问题上有点晚了,但我最近一直在思考这个问题,总的来说,使用属性进行缓存是一种代码味道

以下是几个原因:

  1. 按属性进行缓存可能会让您陷入“摇尾巴”的情况,因为当您实现适合自己的方法时,它有可能改变代码的设计由缓存属性使用,特别是当您进入复杂的场景时。

  2. 按属性进行缓存是危险的,因为虽然向缓存添加项目的过程通常是相同的(即cache.Add(key, value)),但确定键和要插入的项目的过程并不总是相同相同,并且通常需要某种并不总是通用的逻辑。例如,通常会想到使用参数值来生成缓存键,但考虑使用 DateTime 参数并且调用者传入 DateTime.Now 的场景...返回值可能始终相同,但是参数生成的key会不同,每次调用都会生成一个新的缓存对象。然后,您可以更改该方法的设计,但随后您就会遇到 1

  3. 一般来说,缓存用于优化特定场景,并且很少有原因进入缓存的项目是相同的。假设您有一个很少更改的项目目录,您可能决定在应用程序启动时将整个目录加载到缓存中并将其缓存 12 小时,但您可能决定仅在用户登录后才缓存用户的用户详细信息第一次。检索这些项目然后将它们添加到缓存所需的逻辑完全不同,并且不适合在属性中捕获。

  4. 如果您有缓存属性,那么您还需要一种在缓存项发生更改时使它们过期的方法。假设您有一个缓存属性,您很可能有一个“CacheExpiry”属性,它会遇到上述相同的问题,但是您还必须想出一种在方法之间生成缓存键的通用方法,因为它会用于更新或删除对象的参数极不可能与用于将对象添加到缓存的参数相同。

底线是,从表面上看,按属性缓存似乎是一个好主意,但实际上,缓存解决的问题的本质意味着它不太适合由属性驱动。

I'm a bit late to the party on this one but I've been thinking about this problem recently and in general, using attributes for caching is a code smell.

Here's a couple of reasons why:

  1. Caching by attribute can get you into a bit of a "tail-wagging-the-dog" scenario as it has the potential to change the design of your code as you implement methods that lend themselves to be used by the caching attribute, particularly when you get into complex scenarios.

  2. Caching by attribute is dangerous because, whilst the process of adding items to the cache is generally the same (i.e. cache.Add(key, value)), the process of determining the key and the item to be inserted is not always the same and usually requires some sort of logic which isn't always generic. For example, it's often thought of to generate the cache key by using the parameter values, but consider the scenario where a DateTime parameter is being used and the caller passes in DateTime.Now... the return value might always be the same, but the key generated by the parameters will be different and will generate a new cache object for each call. You could then change the design of the method but then you're getting exactly into the problem described in 1

  3. Generally speaking, caching is used to optimize a specific scenario and it is rare that the reason an item is entered into cache is the same. Suppose you have a catalogue of items that rarely changes, you might decide to load the entire catalogue into cache when the application starts and cache it for 12 hours, but you might decide to cache the user details of a user only after they log in for the first time. The logic required to retrieve these items and then add them to a cache is completely different and doesn't lend itself to being captured in an attribute.

  4. If you had a cache attribute, you would then also need a way to expire cache items when they change. Assuming that you have a cache attribute, you would most likely have a "CacheExpiry" attribute which would have the same problems above, but you'd also have to then come up with a common way of generating cache keys between methods because it'd be highly unlikely that your parameters for updating or deleting an object would be the same as those for adding an object to the cache.

Bottom line is that on the surface Caching by attribute seems like a good idea, but in practice the nature of the problems that caching solves means that it doesn't lend itself nicely to being driven by attributes.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文