可变值对象/共享状态(以及啤酒酿造!)
我是一个生锈的程序员,试图再次在该领域博学。 我断断续续地发现,我的自学和正规教育都导致了一些坏习惯。 因此,我正在努力思考好的设计模式,以及——推而广之——当它们是错误的时候。 语言是 Java,这是我的问题:
我正在尝试编写软件来协助啤酒酿造。 在酿造过程中,有时您必须用特定品种的啤酒花代替配方中要求的啤酒花。 例如,您可能有一个需要“阿马里洛”啤酒花的配方,但您只能得到“Cascade”,它具有足够相似的香气来替代; 啤酒花具有一定量的α酸(每给定质量),两种啤酒花之间的比率是替代公式的一部分。 我正在尝试在我的程序中(正确地)对此进行建模。
我最初的目标是拥有两个对象。 一个是 HopVariety
,它具有有关各种啤酒花的一般描述信息,另一个是 HopIngredient
,它是 HopVariety
的特定实例,并且还包括给定食谱中使用的量。 HopIngredient
应该了解其品种,HopVariety
应该了解什么可以用作它的替代品(并非所有替代品都是对称的)。 这看起来是一个不错的 OOP。
问题是这样的:我试图遵循良好的实践并使我的值对象不可变。 (在我看来,我将 HopVariety
和 HopIngredient
分类为值对象,而不是“参与者”。)但是,我需要用户能够更新给定的 HopVariety具有新的可行替代品。 如果我遵循不变性,这些变化将不会传播到单个成分。 如果选择可变性,我的行为就会很糟糕,因为共享可变值对象可能会引入副作用。
因此,选项 B:引入某种 VarietyCollection,并通过名称或唯一标识符将成分和品种松散耦合。 然后是 VarietySubstitutionManager,以便品种不保存对其他品种的引用,只保存它们的 id。 这违背了我想要做的事情,因为持有对品种对象的引用具有直观意义,现在我引入了感觉过度抽象的东西,并且还将函数与数据分离。
那么,我如何在特定实例之间正确共享状态? 解决问题的正确或至少最明智的方法是什么?
I'm a rusty programmer attempting to become learned in the field again. I've discovered, fitfully, that my self-taught and formal education both induced some bad habits. As such, I'm trying to get my mind around good design patterns, and -- by extension -- when they're wrong. The language is Java, and here's my issue:
I'm attempting to write software to assist in beer brewing. In brewing, sometimes you must substitute a particular variety of hop for what's called for in the recipe. For example, you might have a recipe that calls for 'Amarillo' hops, but all you can get is 'Cascade', which has a similar enough aroma for substitution; hops have an Alpha Acid amount (per a given mass), and the ratio between two hops is part of the substitution formula. I'm attempting to model this (properly) in my program.
My initial go is to have two objects. One a HopVariety
, which has general descriptive information about a variety of hop, and one a HopIngredient
, which is a particular instantiation of a HopVariety
and also includes the amount used in a given recipe. HopIngredient
should have knowledge of its variety, and HopVariety
should have knowledge of what can be used as a substitute for it (not all substitutions are symmetric). This seems like good OOP.
The problem is this: I'm trying to follow good practice and make my value objects immutable. (In my head, I'm classifying HopVariety
and HopIngredient
as value objects, not 'actors'.) However, I need the user to be able to update a given HopVariety with new viable substitutions. If I follow immutability, these changes will not propagate to individual ingredients. If choose mutability, I'm Behaving Badly by potentially introducing side-effects by sharing a mutable value object.
So, option B: introduce a VarietyCollection of sorts, and loosely couple the ingredients and the varieties by way of a name or unique identifier. And then a VarietySubstitutionManager, so that varieties don't hold references to other varieties, only to their ids. This goes against what I want to do, because holding a reference to the variety object makes intuitive sense, and now I'm introducing what feels like excessive levels of abstraction, and also separating functions from the data.
So, how do I properly share state amongst what amounts to specific instances? What's the proper, or at least, sanest way to solve the problem?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您确定
HopVariety
应该是一个值对象吗? 在我看来,它就像一个实体 - “阿马里洛”啤酒花品种是唯一的一个,因此它应该是一个唯一可识别的对象。 就像“托尼·伍斯特”只是其中之一(不完全是,但你明白了;))我建议阅读有关 领域驱动设计以及实体和值对象之间的差异。
顺便说一句:DDD 书有很多像你这样的情况的例子以及如何处理它们。
Are you sure
HopVariety
should be a value object? It sounds to me like an entity - "Amarillo" hop variety is one and only one, so it should be a uniquely identifiable object. Just like "Tony Wooster" is only one (well not exactly, but you get the point ;) )I recommend reading about Domain Driven Design and the differences between entities and value objects.
BTW: DDD book has a lot of examples of situations like yours and how to handle them.
我认为您的选择是要么
根据手头的信息,我不清楚哪一个是更适合你的情况。
I think your choices are either to
With the information at hand, it isn't clear to me which is better for your situation.
您有多想避免具有多个引用的对象发生突变?
如果您希望配方集合反映用户对品种集的更新,并且希望避免对象中的可变字段,那么您将强迫自己使用功能对象更新来处理用户输入。
这条路的尽头是 I/O 单子。 你想朝这个方向走多远?
使用允许突变副作用的函数式语言(例如 S/ML 等),您可能会选择保持品种和成分对象纯净,并存储从存储在单个可变中的最新品种集合返回当前品种对象的函数参考细胞。 这似乎是一种合理的分摊方式。
How badly do you want to avoid mutation of objects with multiple references?
If you want the recipe collection to reflect the user updates to the variety set, and you want to avoid mutable fields in your objects, then you'll be forcing yourself into using functional object update to handle user input.
At the end of that road lies the I/O monad. How far do you want to go in that direction?
With a functional language that allows mutation side-effects, e.g. S/ML etc., you might opt to keep your variety and ingredient objects pure, and to store functions that return the current variety object from the latest variety collection stored in a single mutable reference cell. This might seem like a reasonable way to split the difference.