2014-01-06 3 views
5

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

Следующая иллюстрация включает в себя один Child и один класс Parent. Фактически, я описываю общую ситуацию во всем приложении. Мое решение по этой проблеме затронет многочисленные классы, поэтому я задаю этот вопрос.

public sealed class Child : INotifyPropertyChanged 
{ 
    private Child() 
    { 
     //initialize basic state 
    } 

    public Child(Parent parent) 
     : this() //invoke parameterless contructor to initialize basic state 
    { 
     this.Parent = parent; //the last thing before the public ctor exits 
    } 

    public Parent Parent 
    { 
     get { return parent; } 
     set 
     { 
      if (value != parent) { 
       parent = value; 
       // initialize state that depends on the presence of a Parent instance 
       parent.Children.Add(this); //hand off to parent class 
       OnPropertyChanged("Parent"); 
      } 
     } 
    } 

    // INotifyPropertyChanged impl... 
} 

Уведомление в инкубаторе в Parent имущества, что я передаю от экземпляра к Parent класса. Также обратите внимание, что я вызываю этот сеттер из публичного конструктора.

По состоянию на earlier question I asked, он пришел к выводу, что передача экземпляра, который не полностью инициализирован, является анти-шаблоном. Можно утверждать, что это то, что происходит здесь. Но я бы сказал, что это не так, поскольку базовое состояние инициализируется в беспараметричном конструкторе, который вызывается до того, как он принимает параметр Parent.

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

Как я могу пойти с нарушением описанного анти-шаблона в этих конкретных обстоятельствах? Или есть какое-то соображение, которое я пропустил?

+0

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

+0

Невозможно создать метод Add для класса Parent, который добавляет Child к себе? –

+0

@AdamHouldsworth У меня есть [API, который обрабатывает все это] (http://www.qnomad.com/wpf/corehelpers/) (см. «Двунаправленные ассоциации»). – HappyNomad

ответ

1

Как я могу пойти с нарушением описанного анти-шаблона в этих конкретных обстоятельствах?

Я думаю, что вы хороши.

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

Или есть некоторые соображения, которые я пропустил?

Одно отличие состоит в том, что если свойство set вызывает исключение, то объект не строится. Это имеет значение, если ребенок является одноразовым.

using (Child child = new Child()) 
{ 
    Child.Parent = null; // throws an exception 
    ... etc ... 
} // child.Dispose is invoked 

против

using (Child child = new Child(null)) // throws an exception 
{ 
    ... etc ... 
} // child.Dispose is not invoked 
0

Это путает пользователя вашей библиотеки.

+1

И что, если есть только один потребитель, этот класс документирован, и этот метод не доступен из-за пределов библиотеки? –

+0

Выбейте себя тогда. –

3

Вы правы, leaking this during the constructor определенно то, что вам следует избегать, если это возможно.

В этом случае его вероятно тонкой (потому что вы пометили класс как sealed), но почему-то, что выглядит как анти-паттерна, когда вы можете сделать это лучший способ вместо этого?

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

var child = new Child(); 
parent.Children.Add(child); 

И есть Children свойства установить Parent на ребенке при добавлении.

В качестве альтернативы, если вы действительно хотели сохранить вышеуказанный логический поток, тогда я, вероятно, сделаю что-то подобное.

public sealed class Child : INotifyPropertyChanged 
{ 
    private Child() { } 

    public static Child CreateAndAddChild(Parent parent) 
    { 
     var child = new Child(); 
     child.Parent = parent; 
    } 

    public Parent Parent 
    { 
     get { return parent; } 
     set 
     { 
      parent = value; 
      parent.Children.Add(this); 
      OnPropertyChanged("Parent"); 
     } 
    } 
} 

Оба эти решения полностью избежать утечки this в конструктор.

+1

+1 для перемещения OnPropertyChanged до конца реализации set-property. – ChrisW

+0

«На сколько это проблема?» раздел http://msmvps.com/blogs/jon_skeet/archive/2010/09/02/don-t-let-this-get-away.aspx предполагает, что шаблон часто используется на практике. – ChrisW

+0

@ Justin - Спасибо за отзыв. Но как, в частности, ваша вторая альтернатива лучше, чем код в моем вопросе (кроме перемещения «OnPropertyChanged»)? Раньше я думал, что так делаю, но также и метод ctor. Пока «опасный» код приходит последним в публичном ctor, я не вижу, как лучше «CreateAndAddChild». – HappyNomad

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