Основы C #, делающие свойства атомарными - PullRequest
9 голосов
/ 12 сентября 2009

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

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

Это, конечно, дизайн, который считается неряшливым, потому что Area зависит от базы и высоты, которые устанавливаются первыми:

class Triangle{
    public double Base {get;set;}
    public double Height {get;set;}
    public double Area {
        get{
            return (Base * Height) / 2;
        }
    }
}

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

class Triangle{
    public Triangle(double b, double h){
        Base = b;
        Height = h;
    }

    public double Base {get;set;}
    public double Height {get;set;}
    public double Area {
        get{
            return (Base * Height) / 2;
        }
    }
}

У вас все еще есть свойство, которое зависит от других свойств. Чтобы быть пуристом, я вижу только несколько подходов (думаю, их можно объединить):

  1. Make Base / Height имеет элементы только для чтения, которые могут быть установлены только в конструкторе

  2. Превратите расчет площади в метод.

  3. Используйте некоторый тип фабричного шаблона + элементы только для чтения, чтобы гарантировать, что, хотя зависимость может существовать, значения могут быть установлены только методом, который создает экземпляр класса Triangle.

Вопросы:

  1. Является ли руководство практическим (нужно ли вам моделировать много сложностей в ваших классах, чтобы их поддерживать)? [например, класс SqlConnection позволяет вам инициализировать свойство строки подключения, но позволяет изменять его отдельные части, такие как время ожидания команды]

  2. Как вам удается поддерживать независимость своих свойств друг от друга?

  3. Дополнительно для людей, которые используют архитектуру типов Silverlight / MVVM. Принимаете ли вы зависимости в свойствах из-за того, как привязка данных работает с объектом? Например, привязка экземпляра треугольника, который показывает высоту, основание и площадь на экране.

Ответы [ 5 ]

15 голосов
/ 12 сентября 2009

Microsoft действительно пытается сказать: «Не создавайте свой класс таким образом, чтобы вызов свойств в произвольном порядке вызывал неожиданное поведение». Пользователи вашего класса не ожидают, что запрос значения (используя свойство) будет иметь неудобные побочные эффекты.

Это подпадает под принцип наименьшего удивления.

Я думаю, что это абсолютно практическое руководство . Свойства не должны иметь неожиданных побочных эффектов.

Вы задали отличный вопрос: «Как вам удается сохранять независимость своих свойств друг от друга?». Очень осторожно! Я исключаю состояние (и соответственно уменьшаю количество свойств). Кроме того, разделение состояния путем разделения классов является еще одной тактикой. Это сделало бы отличный вопрос само по себе ...

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

3 голосов
/ 12 сентября 2009

Самый простой способ - принять базовые значения и значения высоты только для конструктора, а затем выставить их все как свойства только для чтения:

class Triangle
{
   private double base;
   private double height;

   private Triangle() { }

   public Triangle(double base, double height)
   {
      this.base = base;
      this.height = height;
   }

   public double Base 
   {
      get 
      {
         return this.base;
      }
   }

   public double Height 
   {
      get 
      {
         return this.height;
      }
   }

   public double Area 
   {
      get 
      {
         return (this.base * this.height) / 2;
      }
   }
}
2 голосов
/ 12 сентября 2009

Думаю, я бы пошел на класс Triangle, в котором есть конструктор, который принимает в качестве параметров Base & Height, поскольку треугольник не может существовать (imho) без базы или высоты.

Конечно, свойство Area зависит от Base & Height, но я не вижу в этом проблемы? Я считаю, что руководящие указания по РС следует интерпретировать следующим образом:

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

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

Кроме того, ваше свойство Area не может быть установлено, поэтому с ним нет проблем. :)

0 голосов
/ 12 сентября 2009

Выберите опцию 2. Сделайте Area методом и назовите его CalculateArea. Свойства должны использоваться для инкапсуляции состояния, а не поведения. Эта площадь может быть рассчитана из базы и высоты не означает, что она сама должна быть в состоянии; напротив, это сильный аргумент, что Area должна не быть государственной.

class Triangle
{
    public Triangle(double b, double h)
    {
        Base = b;
        Height = h;
    }

    public double Base { get; set; }
    public double Height { get; set; }

    public double CalculateArea()
    {
        return (Base * Height) / 2;
    }
}

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

0 голосов
/ 12 сентября 2009

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

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