Должны ли члены объекта автоматически устанавливаться в классе? - PullRequest
1 голос
/ 30 октября 2008

Если у вас есть сложное свойство, вы должны создать его экземпляр или оставить его пользователю для его создания?

Например (C #)

A)

 class Xyz{
     List<String> Names {get; set;}
 }

Когда я пытаюсь использовать, я должен установить его.

...
Xyz xyz = new Xyz();
xyz.Name = new List<String>();
xyz.Name.Add("foo");
...

Где, как будто я изменяю код

В)

 class Xyz{
     public Xyz(){
         Names = new List<String>();
     }
     List<String> Names {get; }
 }

, что в этом случае я могу сделать список только для чтения.

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

С)

 class Xyz{
     String Name {get; set;}
 }

Я бы назвал плохой практикой инициализацию.

Есть ли практические правила для таких сценариев?

Ответы [ 6 ]

3 голосов
/ 30 октября 2008

Как правило (имеется в виду, что из этого правила всегда будут исключения) установите ваш элемент в конструкторе. Для этого и нужен конструктор.

Помните, что одна из идей ОО - это абстракция. Требование к «пользователю» (то есть программисту, который хочет создать экземпляр вашего объекта в своем исходном коде) сделать это нарушение принципа абстракции. Еще одна вещь, о которой пользователь должен подумать, прежде чем использовать ваш объект, следовательно, еще один потенциальный источник ошибок.

2 голосов
/ 30 октября 2008

Целью конструктора является конструкция объекта. Никаких дополнительных настроек не требуется. Если есть некоторая информация, которую знает вызывающий объект, то она должна быть передана конструктору:

Неправильно:

MyClass myc = new MyClass();
myc.SomeProp = 5;
myc.DoSomething();

Справа:

MyClass myc = new MyClass(5);
myc.DoSomething();

Это особенно верно, если необходимо установить SomeProp, чтобы myc находился в допустимом состоянии.

2 голосов
/ 30 октября 2008

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

Некоторые правила о вашем коде:

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

public bool IsValid(object input){
  foreach(var validator in this.Validators)
    if(!validator.IsValid(input)
      return false;
  return true;
}

Этот код работает независимо от того, является ли коллекция валидаторов пустой или нет. Если вы хотите проверки, добавьте валидаторы в коллекцию. Если нет, оставьте коллекцию пустой. Просто. Если для свойства collection задано значение NULL, эта вонючая версия кода приведена выше:

public bool IsValid(object input){
  if(this.Validators == null) 
    return false;
  foreach(var validator in this.Validators)
    if(!validator.IsValid(input)
      return false;
  return true;
}

Больше строк кода, менее элегантно.

Во-вторых, для ссылочных типов ДРУГИЕ, чем коллекции, вы должны учитывать поведение объекта при определении, хотите ли вы установить значения свойств. Существует ли единственное очевидное значение по умолчанию для свойства? Или нулевое значение для свойства имеет допустимое значение?

В вашем примере вы можете всегда проверять в установщике значение Name и устанавливать его по умолчанию "(имя не указано)", когда ему присваивается значение NULL. Это может облегчить привязку этого объекта к пользовательскому интерфейсу. Или имя может быть нулевым, поскольку вы ТРЕБУЕТЕ правильное имя, и вы будете проверять его и генерировать исключение InvalidOperationException, когда вызывающая сторона пытается выполнить действие над объектом без предварительной установки имени.

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

2 голосов
/ 30 октября 2008

Это мое нормальное решение:

class XYZ 
{
   public XYZ () { Names = new List<string>(); }
   public List<string> Names { get; private set; }
}

(Обратите внимание, что он не работает с XmlSerialization, поскольку вам нужны методы получения и установки для всех свойств XmlSerialized.) (Вы можете переопределить это, но это кажется слишком большой работой для небольших усилий).

Как указал OregonGhost - вам нужно добавить [XmlArray], чтобы это работало с XmlSerialization.

Это все еще нарушает правила инкапсуляции, так как если бы вы хотели быть полностью корректным, вы бы получили:

class XYZ 
{
   public XYZ () { AllNames = new List<string>(); }
   private List<string> AllNames { get; set; }
   public void AddName ( string name ) { AllNames.Add(name); }
   public IEnumerable<string> Names { get { return AllNames.AsReadOnly(); } }
}

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

Однако это дает дополнительное преимущество: XYZ может отслеживать изменения в своей коллекции имен, и что XYZ - единственное место, где можно изменить коллекцию имен.

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

1 голос
/ 30 октября 2008

Это действительно зависит.

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

И, если вы не хотите, чтобы свойство было изменено после строительства, вы можете сделать его приватным

class Xyz
{
     public Xyz(string name)
     {
         this.Name = name;
     }
     String Name 
     {
          get; 
          private set;
     }
}
0 голосов
/ 07 августа 2010

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

public class Xyz
{
   private List<String> _names = new List<String>(); // could also set in constructor

   public IEnumerable<String> Names
   {
       get
       {
           return _names;
       }
   }

   public void AddName( string name )
   {
       _names.Add( name );
   }
}

Теперь не имеет значения, используете ли вы List, HashTable, Dictionary и т. Д.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...