复合设计模式与设计常见的后代方法
我正在使用复合模式,如下面的类图所示。基本上,我的主叶(分配)有一个属性“持续时间”,我想将其累积在各种复合材料中。
我还希望拥有分配计数,以及将分配作为集合返回的能力,我可以将其用于演示文稿中的数据绑定。
这个问题是关于 C# 实现的,具体来说:
- 有一个有趣的 在这里发布,它有一个递归执行此操作的扩展,以及完全避免递归的替代方法。您有一个通用的方法来像我一样获取分配吗?我还没有决定我写的代码是否因为我对模式感到不舒服而感到尴尬,或者它是否很尴尬!
- 您是否看到这里有任何简单的机会来缓存第一次迭代的结果,从而最大限度地减少进一步的迭代?
干杯,
Berryl
Composite 类中的代码
public class AllocationComposite : AllocationNode
{
protected readonly IList<AllocationNode> _children;
public AllocationComposite() { _children = new List<AllocationNode>(); }
#region Implementation of AllocationNode
public override void Adopt(AllocationNode node) ...
public override void Adopt(IEnumerable<AllocationNode> nodes)...
public override void Orphan(AllocationNode node)...
public override void Orphan(IEnumerable<AllocationNode> nodes)...
public override IEnumerable<AllocationNode> Descendants
{
get
{
return _children.Concat(_children.SelectMany(child => child.Descendants));
}
}
public override IEnumerable<Allocation> Allocations
{
get
{
return Descendants.OfType<Allocation>();
}
}
public override TimeSpan Duration {
get {
return Allocations.Aggregate(TimeSpan.Zero, (current, child) => current + child.Duration);
}
}
public override int Count { get { return Allocations.Count(); } }
#endregion
public override string ToString() {
return String.Format("{0} for {1}",
"allocation".PluralizeWithCount(Count),
"hour".PluralizeWithCount(Duration.TotalHours, "F2"));
}
}
}
抽象组件中的默认实现
public abstract class AllocationNode: Entity, IAllocationNode
{
#region Implementation of AllocationNode
public virtual TimeSpan Duration { get { return TimeSpan.Zero; } set { } }
public virtual void Adopt(AllocationNode node) { throw new InvalidOperationException(...)); }
public virtual void Adopt(IEnumerable<AllocationNode> nodes) { throw new InvalidOperationException(...)); }
public virtual void Orphan(AllocationNode node) { throw new InvalidOperationException(...)); }
public virtual void Orphan(IEnumerable<AllocationNode> nodes) { throw new InvalidOperationException(...)); }
public virtual int Count { get { return 1; } }
public virtual IEnumerable<AllocationNode> Descendants { get { return Enumerable.Empty<AllocationNode>(); } }
public virtual IEnumerable<Allocation> Allocations { get { return Enumerable.Empty<Allocation>(); } }
#endregion
}
I am using a composite pattern as shown in the class diagram below. Basically my primary leaf (an Allocation) has a single property, Duration, that I want to accumulate in various composites.
I also want to have a count of allocations, and the ability to return the allocations as a collection I can use for data binding in presentations.
This question is about the C# implementation, specifically:
- There is an interesting post here that has an extension to do this recursively, as well as an alternative approach that avoids recursion altogether. Would you have a common method to get at the Allocations the way I am doing? I haven't decided yet if the code I wrote feels awkward because I am not comfortable with pattern, or if it is awkward!
- Do you see any easy opportunities here to cache the results of the first iteration and thereby minimize further iteration?
Cheers,
Berryl
Code in the Composite class
public class AllocationComposite : AllocationNode
{
protected readonly IList<AllocationNode> _children;
public AllocationComposite() { _children = new List<AllocationNode>(); }
#region Implementation of AllocationNode
public override void Adopt(AllocationNode node) ...
public override void Adopt(IEnumerable<AllocationNode> nodes)...
public override void Orphan(AllocationNode node)...
public override void Orphan(IEnumerable<AllocationNode> nodes)...
public override IEnumerable<AllocationNode> Descendants
{
get
{
return _children.Concat(_children.SelectMany(child => child.Descendants));
}
}
public override IEnumerable<Allocation> Allocations
{
get
{
return Descendants.OfType<Allocation>();
}
}
public override TimeSpan Duration {
get {
return Allocations.Aggregate(TimeSpan.Zero, (current, child) => current + child.Duration);
}
}
public override int Count { get { return Allocations.Count(); } }
#endregion
public override string ToString() {
return String.Format("{0} for {1}",
"allocation".PluralizeWithCount(Count),
"hour".PluralizeWithCount(Duration.TotalHours, "F2"));
}
}
}
Default implementation in abstract Component
public abstract class AllocationNode: Entity, IAllocationNode
{
#region Implementation of AllocationNode
public virtual TimeSpan Duration { get { return TimeSpan.Zero; } set { } }
public virtual void Adopt(AllocationNode node) { throw new InvalidOperationException(...)); }
public virtual void Adopt(IEnumerable<AllocationNode> nodes) { throw new InvalidOperationException(...)); }
public virtual void Orphan(AllocationNode node) { throw new InvalidOperationException(...)); }
public virtual void Orphan(IEnumerable<AllocationNode> nodes) { throw new InvalidOperationException(...)); }
public virtual int Count { get { return 1; } }
public virtual IEnumerable<AllocationNode> Descendants { get { return Enumerable.Empty<AllocationNode>(); } }
public virtual IEnumerable<Allocation> Allocations { get { return Enumerable.Empty<Allocation>(); } }
#endregion
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您想要扁平化所有节点的集合(IEnumerable allNodes)吗?如果是这样,快速浏览一下您链接的帖子后,我认为这两种解决方案都可以。如果 LukeH 提到的考虑事项不适用于您的情况,我会坚持使用 LINQ 解决方案,因为它很清晰,但这取决于您。
如果不修改结构,缓存很容易。如果进行添加和删除,事情就变得非常复杂。
不管怎样,你想通过缓存实现什么目的?后代计数是否已缓存?自己的后代?在哪个层面上?根级别还是任何级别?
如果您有一个抽象类,那么使其方法抽象而不是虚拟抛出异常或返回空集合会更优雅。
附言。类图与代码不同步(即 Duration 属性的放置)
Do you want to have a collection of all nodes flattened (IEnumerable allNodes)? If so, after a quick look at the post you linked, I think that both solutions are OK. If the cosiderations LukeH mentions don't apply to your case, I would stick to LINQ solution because of its clarity, but it's up to you.
Caching is easy if you don't modify your structure. It gets very complicated if addition and deletions take place.
Anyway, what do you want to achieve with caching? Have the descendants count cached? Descendants themselves? On which level? Root level or any level?
If you have an abstract class, it would be more elegant to make its methods abstract rather than virtual throwing exceptions or returning empty collections.
PS. The class diagram is not synchronized with the code (i.e. placement of Duration property)