2015-09-19 3 views
0

Я столкнулся с несколькими случаями, когда у меня есть родительский объект с несколькими объектами и где изменения информации в одном дочернем объекте затрагивают другие.Распространение изменений в одном дочернем объекте на другой

Для примера рассмотрим следующий случай:

interface IUniverse 
{ 
    IStorage ShirtStorage; 
    IList<IHuman> Humans; 
} 

Interface IStorage: 
{ 
    string Location; 
    int Capacity; 
    IList<IShirt> Items; 
} 

Interface IHuman 
{ 
    string Name; 
    int Age; 
    IList<IShirt> Shirts; 
} 

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

Я думал 3 способа сделать это:


Во-первых, мы можем ввести Add(IClothing) и Remove(IClothing) методы к IStorage<T> и IHuman.

interface IUniverse 
{ 
    IStorage ShirtStorage; 
    IList<IHuman> Humans; 
} 

Interface IStorage 
{ 
    string Location; 
    int Capacity; 
    IList<IShirt> Items; 
    **void Add(IShirt);** 
    **void Remove(IShirt);** 
} 

Interface IHuman 
{ 
    string Name; 
    int Age; 
    IList<IShirt> Shirts; 
    **void AddShirts(IShirt);** 
    **void RemoveShirts(IShirts);** 
} 

Затем, реализация указанных интерфейсов не будет иметь ничего под капотом, который удаляет особую рубашку из всех людей, когда он удаляется из ShirtStorage.

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

То есть, программист должен знать точную структуру вселенной, чтобы удалить одну рубашку. В случае, когда структура вселенной становится очень сложной, так что ссылки на определенную рубашку могут появляться больше, чем только в IHuman, это может оказаться ошибочным и утомительным.


Во-вторых, мы так же ввести Add(IShirt) и Remove(IShirt) методы для интерфейсовIStorage и IHuman:

interface IUniverse 
{ 
    IStorage<IShirt> ShirtStorage; 
    IList<IHuman> Humans; 
} 

Interface IStorage 
{ 
    string Location; 
    int Capacity; 
    IList<IShirt> Items; 
    **void Add(IShirt);** 
    **void Remove(IShirt);** 
} 

Interface IHuman 
{ 
    string Name; 
    int Age; 
    IList<ICShirt> Shirts; 
    **void AddShirt(IShirt);** 
    **void RemoveShirt(IShirt);** 
} 

.. Однако на этот раз, мы используем реализацию вышеуказанных интерфейсов таких что под капотом происходит некоторое уведомление. То есть,

class Storage : IStorage 
{ 
    IUniverse parentUniverse; 
    string Location; 
    int Capacity; 
    IList<IShirt> Items; 

    // ... Implementation for Add(IShirt item) is trivial 

    void Remove(IShirt item) 
    { 
     this.Items.Add(item); 
     foreach (IHuman human in this.parentUniverse) 
      foreach(IClothing clothing in human.Clothings) 
       if (clothing == item) 
        human.RemoveClothing(clothing); 
    } 
} 

Действительно, помещая все уведомления в реализации интерфейса, потребители интерфейса не должны пройти через каждую возможную ссылку на конкретный IShirt, когда он хочет, чтобы удалить его от существования, что делает его better в этом смысле по сравнению с предыдущим решением.

Однако недостатком является то, что такая конструкция по своей сути приводит к патологическому лечению и нарушает принцип единой ответственности. Если программист называет Remove(IShirt) на ShirtStorage, он не будет знать, какая ссылка удаляется с того места.

Если программист желает написать графический интерфейс с использованием шаблона посредника, например, он не будет знать, какое уведомление следует отправить.

У каких именно людей удалены рубашки, что требует обновления графического интерфейса для какого-либо компонента, который отражает список рубашек, принадлежащих определенному человеку?Что делать, если у меня есть класс Catalog с именами всех рубашек - не удалялась ли запись, соответствующая удаленной рубашке (под капотом)? Должен ли я также обновить соответствующий компонент графического интерфейса для моих каталогов?


В-третьих, мы вводим Add(IShirt) и Remove(IShirt) методы IUniverse вместо:

interface IUniverse 
{ 
    IStorage ShirtStorage; 
    IList<IHuman> Humans; 
    void Add(IShirt); 
    void Remove(IShirt); 
} 

Interface IStorage: 
{ 
    string Location; 
    int Capacity; 
    IList<IShirt> Items; 
} 

Interface IHuman 
{ 
    string Name; 
    int Age; 
    IList<IShirt> Shirts; 
} 

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

Однако недостатки подобны недостаткам во втором решении. Кроме того, IUniverse экземпляры в конечном итоге превращаются в объект Бога. Каждое место, где мне нужно снять рубашку из вселенной, я должен иметь ссылку на вселенную.

Если конкретный компонент GUI просто хочет отображать информацию для ShirtStorage и разрешить взаимодействие с хранилищем (например, добавление и удаление рубашек), не приведет ли это к некоторой связи между компонентом GUI и Universe, когда единственная связь, которая должна существовать, - это компонент GUI и IStorage?


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

ответ

5

Каждый раз, когда я слышу фразу «Распространение изменений» в контексте oop, я сразу же думаю, что Observer pattern. Поэтому вместо того, чтобы возлагать ответственность на ваши объекты домена на «синхронизацию» добавления и удаления рубашек, я бы представил другой класс, который берет на себя эту ответственность, и ваши объекты домена поднимают соответствующие события. И вы также придерживаетесь принципа единой ответственности.

HTH

Смежные вопросы