2008-09-17 3 views
4

У меня есть следующий тип:Своего рода созидательного рисунка необходим в C#

// incomplete class definition 
public class Person 
{ 
    private string name; 

    public string Name 
    { 
     get { return this.name; } 
    } 
} 

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

Этот объект также должен запускать событие каждый раз, когда он обновляется его контроллером/строителем.

В заключение, в соответствии с предыдущим скелета определение типа:

  • Person может быть обработан только конкретным контроллером
  • Этот контроллер может обновление состояние (поле name) Person на в любое время
  • Person необходимо отправить сообщение остальному миру в том виде, в каком это произошло
  • Все остальные типы должны быть только в состоянии чтенияPerson атрибуты

Как мне осуществить это? Я говорю о контроллере/строителе здесь, но все остальные решения приветствуются.

Примечание: Я мог бы положиться на модификатор internal, но в идеале все мои вещи должны быть в одной и той же сборке.

ответ

1

Мне нравится иметь интерфейс только для чтения. Затем строитель/контроллер/все может ссылаться на объект напрямую, но когда вы открываете этот объект снаружи, вы показываете только интерфейс.

1

Используйте интерфейс IPerson и вложенный класс:

public class Creator 
{ 
    private class Person : IPerson 
    { 
     public string Name { get; set; } 
    } 

    public IPerson Create(...) ... 


    public void Modify(IPerson person, ...) 
    { 
     Person dude = person as Person; 
     if (dude == null) 
      // wasn't created by this class. 
     else 
      // update the data. 
    } 
} 
5

Создать интерфейс IReadOnlyPerson который предоставляет только получить аксессоров. Попросите личность реализовать IReadOnlyPerson. Сохраните ссылку на Лицо в своем контроллере. Предоставьте другим клиентам только версию только для чтения.

Это защитит от ошибок, а не от мошенничества, как и большинство функций OO. Клиенты могут запускать время в Личность, если они знают (или подозревают) IReadOnlyPerson реализуется Person.

Update, за комментарий:

Единственный интерфейс пользователя также может подвергнуть делегат события, так же, как и любой другой объект. Идиома, обычно используемая в C#, не мешает клиентам общаться со списком слушателей, но соглашение заключается только в том, чтобы добавлять слушателей, поэтому это должно быть адекватно. Внутри любого аксессуара или функции набора с изменяющимися положением побочными эффектами просто вызовите делегата события с защитой для случая с нулевым (без прослушивания).

+0

Этот ответ может быть хорошим, но это не распространяется на вторую часть вопроса: Человек должен отправить уведомление к остальному миру, когда это произойдет – Seb 2008-09-18 07:51:28

0

Возможно, что-то в этом роде?

public class Person 
{ 
    public class Editor 
    { 
     private readonly Person person; 

     public Editor(Person p) 
     { 
      person = p; 
     } 

     public void SetName(string name) 
     { 
      person.name = name; 
     } 

     public static Person Create(string name) 
     { 
      return new Person(name); 
     } 
    } 

    protected string name; 

    public string Name 
    { 
     get { return this.name; } 
    } 

    protected Person(string name) 
    { 
     this.name = name; 
    } 
} 

Person p = Person.Editor.Create("John"); 
Person.Editor e = new Person.Editor(p); 
e.SetName("Jane"); 

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

1

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

interface IPerson 
{ 
    Name { get; set; } 
} 

и реализовать этот интерфейс в явном виде:

class Person : IPerson 
{ 
    Name { get; private set; } 
    string IPerson.Name { get { return Name; } set { Name = value; } } 
} 

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

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

1

Кажется странным, что, хотя я не могу изменить имя объекта Person, я могу просто захватить его контроллер и изменить его там. Это не лучший способ защитить данные вашего объекта.

Но, несмотря на это, вот способ сделать это:

/// <summary> 
    /// A controlled person. Not production worthy code. 
    /// </summary> 
    public class Person 
    { 
     private string _name; 
     public string Name 
     { 
      get { return _name; } 
      private set 
      { 
       _name = value; 
       OnNameChanged(); 
      } 
     } 
     /// <summary> 
     /// This person's controller 
     /// </summary> 
     public PersonController Controller 
     { 
      get { return _controller ?? (_controller = new PersonController(this)); } 
     } 
     private PersonController _controller; 

     /// <summary> 
     /// Fires when <seealso cref="Name"/> changes. Go get the new name yourself. 
     /// </summary> 
     public event EventHandler NameChanged; 

     private void OnNameChanged() 
     { 
      if (NameChanged != null) 
       NameChanged(this, EventArgs.Empty); 
     } 

     /// <summary> 
     /// A Person controller. 
     /// </summary> 
     public class PersonController 
     { 
      Person _slave; 
      public PersonController(Person slave) 
      { 
       _slave = slave; 
      } 
      /// <summary> 
      /// Sets the name on the controlled person. 
      /// </summary> 
      /// <param name="name">The name to set.</param> 
      public void SetName(string name) { _slave.Name = name; } 
     } 
    } 
Смежные вопросы