Должны ли методы класса принимать параметры или использовать свойства класса - PullRequest
5 голосов
/ 05 марта 2010

Рассмотрим следующий класс

public class Class1
{
    public int A { get; set; }
    public int B { get; set; }

    public int GetComplexResult()
    {
        return A + B;
    }
}

Чтобы использовать GetComplexResult, потребитель этого класса должен знать, что нужно установить A и B перед вызовом метода. Если GetComplexResult обращается ко многим свойствам для вычисления его результата, это может привести к неправильным возвращаемым значениям, если потребитель сначала не установит все соответствующие свойства. Таким образом, вы могли бы написать этот класс, как это вместо этого

public class Class2
{
    public int A { get; set; }
    public int B { get; set; }

    public int GetComplexResult(int a, int b)
    {
        return a + b;
    }
}

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

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

Ответы [ 3 ]

5 голосов
/ 05 марта 2010

Если вы используете пример из реального мира, ответ становится более ясным.

public class person
{
    public string firstName { get; set; }
    public string lastName { get; set; }

    public string getFullName()
    {
        return firstName + " " + lastName;
    }
}

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

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

while (person.hasAnotherQuestion()) {
  person.answerNextQuestion();
}
1 голос
/ 05 марта 2010

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

Если значения по умолчанию для A и B, то есть 0, не являются допустимым состоянием, которое дает действительный результат из GetComplexResult, вам следует использовать конструктор, который инициализировал A и B для проверки состояние.

0 голосов
/ 05 марта 2010

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

Например:

public Builder {
    private int a;
    private int b;

    public Class1 create() {
        // some validation logic goes here
        // to make sure we have everything and
        // either fill in defaults or throw an error
        // if needed
        return new Class1(a, b)
    }

    public Builder a(int val) { a = val; }
    public Builder b(int val) { b = val; }
}

Этот построитель можно затем использовать следующим образом.

Class1 obj1 = new Builder().a(5).b(6).create();

Builder builder = new Builder();
// do stuff to find value of a
builder.a(valueOfA);
// do stuff to find value of b
builder.b(valueOfB);
// do more stuff
Class1 obj2 = builder.create();
Class2 obj3 = builder.create();

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

...