Использование аксессоров внутри класса - плохая практика?

Итак, у меня есть простой класс User, который выглядит примерно так (игнорируйте ужасное использование пробелов, хотелось бы, чтобы он был кратким, чтобы читать онлайн):

public class User
{
  private string username;
  public string Username 
  {
    get
    {
      return username;
    }set{
      if(Validate.usernameIsValid(value)){username = value;}
      else{throw new InvalidArgumentException("Username is invalid");}
    }
  }
  //some more fields

  public User(String argUsername)
  {
    this.Username = argUsername;
    //OR validate again before:
    username = argUsername;
  }

}

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


person mbdavis    schedule 30.01.2014    source источник
comment
это нормально, вы используете поле самоинкапсуляции   -  person Grundy    schedule 30.01.2014
comment
Я думаю, что основная работа аксессуара - это только это. В противном случае мы могли бы использовать общедоступные переменные. Просто чтобы проверить ввод, у нас были свойства.   -  person Ashish Charan    schedule 30.01.2014
comment
См. blogs.msdn.com / b / ericlippert / archive / 14.01.2009 /   -  person Eric Lippert    schedule 30.01.2014


Ответы (5)


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

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

person Arun R    schedule 30.01.2014
comment
Это то, о чем я думал, зная, что данные находятся в правильной форме в начале, также могло уменьшить потребность в определенной обработке ошибок в классе. - person mbdavis; 30.01.2014

Ответ зависит от того, откуда поступают данные. Поскольку основной задачей использования установщика является избежание дублирования кода проверки, у вас есть два основных случая:

  • Когда данные поступают извне вашего класса (то есть через параметр метода, как в вашем примере), вы должны вызвать установщик, который будет выполнять проверку за вас.
  • Когда данные были «продезинфицированы» в точке, где вы готовы назначить, переходите прямо к переменной, минуя проверку.

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

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

person Sergey Kalinichenko    schedule 30.01.2014
comment
Это хорошая или плохая практика - бросать незащищенные данные в методы доступа к классу? Или я должен сначала очистить все это, а затем позволить классу назначить напрямую. Единственная проблема, с которой я столкнулся, заключается в том, что если класс был повторно использован, его можно было бы использовать без предварительной очистки ввода, что означало бы, что для обработки ошибок мне пришлось бы добавить гораздо больше обработки ошибок в другие методы класса. - person mbdavis; 30.01.2014
comment
@mbdavis Все прямые назначения должны происходить внутри частных методов. Не должно быть пути для того, чтобы кто-то повторно использовал класс для проталкивания своих данных через очищенный путь к данным - например, не должно быть protected методов, которые присваивают значение без предварительной проверки, чтобы предотвратить описываемые вами ситуации (т. Е. Ошибки из-за неправильного повторное использование класса). - person Sergey Kalinichenko; 30.01.2014
comment
Хорошо, тогда я буду придерживаться аксессуаров. Спасибо за вашу помощь! - person mbdavis; 30.01.2014

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

person Alex Siepman    schedule 30.01.2014

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

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

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

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

Прямое использование вспомогательного поля необходимо скорее для манипуляций с задним ходом.

Но нет общего правила, какой подход выбрать.

person AlexD    schedule 30.01.2014
comment
Спасибо, красиво объяснено, мне нравится ваше сравнение с тем, что можно вызывать общедоступные методы. - person mbdavis; 31.01.2014

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

private readonly ProductManufacturer _productManufacturer = new  ProductManufacturer();
private readonly IList<ProductCategory> _productCategories = new List<ProductCategory>();


public ProductManufacturer ProductManufacturer
{
    get { return _productManufacturer; }
}

public IEnumerable<ProductCategory> ProductCategory
{
    get { return _productCategories; }
}

теперь вы можете получить список категорий продуктов в другом месте, или вы можете выполнить только некоторые другие операции с категорией продуктов, например:

 public void AddProductCategory(ProductCategory productCategory)
        {
            _productCategories.Add(productCategory);
        }

для получения дополнительной информации о полях с самоинкапсуляцией взгляните на это:

http://www.refactoring.com/catalog/selfEncapsulateField.html

person Ehsan    schedule 30.01.2014