Шаблон составного дизайна и общие методы потомков

Я использую составной шаблон, как показано на диаграмме классов ниже. По сути, мой основной лист (распределение) имеет одно свойство, продолжительность, которое я хочу накапливать в различных составных частях.

Я также хочу иметь подсчет распределений и возможность возвращать распределения в виде коллекции, которую я могу использовать для привязки данных в презентациях.

Этот вопрос касается реализации С#, в частности:

  1. Здесь есть интересный пост , у которого есть расширение, позволяющее делать это рекурсивно, а также альтернативный подход, полностью избегающий рекурсии. Есть ли у вас общий способ получить распределение так, как это делаю я? Я еще не решил, кажется ли код, который я написал, неудобным, потому что мне не нравится шаблон, или он неудобен!
  2. Видите ли вы здесь какие-либо простые возможности для кэширования результатов первой итерации и тем самым минимизации дальнейших итераций?

С уважением,
Беррил

эта диаграмма классов

Код в классе 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
}

person Berryl    schedule 24.03.2011    source источник


Ответы (1)


  1. Вы хотите иметь сглаженную коллекцию всех узлов (IEnumerable allNodes)? Если это так, после быстрого просмотра сообщения, на которое вы ссылаетесь, я думаю, что оба решения в порядке. Если соображения, упомянутые LukeH, не относятся к вашему делу, я бы придерживался решения LINQ из-за его ясности, но это зависит от вас.

  2. Кэшировать легко, если вы не изменяете свою структуру. Это становится очень сложным, если имеют место добавление и удаление. В любом случае, чего вы хотите добиться с помощью кэширования? Счет потомков кэшируется? Сами потомки? На каком уровне? Корневой уровень или любой уровень?

Если у вас есть абстрактный класс, было бы более элегантно сделать его методы абстрактными, а не создавать виртуальные исключения или возвращать пустые коллекции.

PS. Диаграмма классов не синхронизирована с кодом (т.е. с размещением свойства Duration)

person dzendras    schedule 24.03.2011
comment
хороший быстрый анализ! Раньше у меня был интерфейс вместо абстрактного класса, который делал вырожденные общие операции в распределении листа. Это совсем не казалось элегантным, поэтому я подумал, что обновился, имея абстрактный класс, что я и сделал, когда писал этот пост (не удивлен, что моя диаграмма классов была немного не так). На самом деле, если бы я сделал эти методы абстрактными, мне пришлось бы вернуть их обратно в лист, не так ли? - person Berryl; 25.03.2011
comment
Я также думал, что использование некоторых из этих уродливых методов было частью платы за предоставление клиенту прозрачности при работе с составной структурой. Они они уродливы, хотя.. - person Berryl; 25.03.2011
comment
да, когда дело доходит до перечисления Allocations, мне нужны сглаженные узлы. Если у вас есть что-то более элегантное, я бы с удовольствием посмотрел - person Berryl; 25.03.2011
comment
да, будут добавления и удаления, но хотелось бы увидеть быстрый пример в коде того, как вы кэшируете счетчик на любом уровне. Ваше здоровье - person Berryl; 25.03.2011
comment
Теперь не уверен, что вы имели в виду о свойстве размещения Duration (установщике?). На самом деле это еще один неудобный бит - лист должен иметь возможность изменять Duration, в то время как для композита это бессмысленно - следовательно, пустой сеттер в абстрактном классе. - person Berryl; 25.03.2011