Мне нужно получить важное значение, учитывая 7 потенциальных входов. Дядя Боб призывает меня избегать функций с таким количеством параметров, поэтому я извлечен класс. Все параметры, теперь являющиеся свойствами, у меня остается метод вычисления без аргументов.
"Это, я думаю," может быть собственностью, но я не уверен, что этот идиоматический С# ".
Должен ли я отображать конечный результат как свойство или как метод без аргументов? Может ли средний программист на С# находить свойства запутанными или оскорбительными? Как насчет толпы Alt.Net?
decimal consumption = calculator.GetConsumption(); // obviously derived
decimal consumption = calculator.Consumption; // not so obvious
Если последнее: должен ли я объявлять промежуточные результаты как [частные] свойства, также? Благодаря тяжелой извлечению метода, у меня есть несколько промежуточных результатов. Многие из них не должны быть частью публичного API. Некоторые из них могут быть интересными, и мои выражения выглядели бы более чистыми, если бы я мог получить к ним доступ как свойства:
decimal interim2 = this.ImportantInterimValue * otherval;
Счастливый экспериментальный отдел:
Во время отладки моего кода в VS2008 я заметил, что я постоянно наводил указатель мыши на вызовы методов, которые вычисляют промежуточные результаты, ожидая наведения с возвращаемым значением. После того, как все методы были добавлены в свойства, я обнаружил, что отображение промежуточных результатов по мере того, как свойства значительно облегчали отладку. Мне это очень нравится, но у меня есть проблемы с читабельностью.
Заявления промежуточных значений выглядят более беспорядочно. Выражения, однако, легче читать без скобок. Мне больше не нужно начинать имя метода с помощью глагола. Для сравнения:
// Clean method declaration; compulsive verby name; callers need
// parenthesis despite lack of any arguments.
decimal DetermineImportantInterimValue() {
return this.DetermineOtherInterimValue() * this.SomeProperty;
}
// Messier property declaration; clean name; clean access syntax
decimal ImportantInterimValue {
get {
return this.OtherInterimValue * this.SomeProperty;
}
}
Я должен объяснить, что я кодировал в Python в течение десятилетия. У меня осталась тенденция тратить дополнительное время на то, чтобы мой код стал проще звонить, чем писать. Я не уверен, что сообщество Python будет рассматривать этот стиль, ориентированный на свойства, как приемлемо "Pythonic", однако:
def determineImportantInterimValue(self):
"The usual way of doing it."
return self.determineOtherInterimValue() * self.someAttribute
importantInterimValue = property(
lambda self => self.otherInterimValue * self.someAttribute,
doc = "I'm not sure if this is Pythonic...")
Важный вопрос здесь выглядит следующим образом:
Какой из них дает более четкий, поддерживаемый код для вас в долгосрочной перспективе?
По моему личному мнению, выделение отдельных вычислений как свойств имеет несколько отличительных преимуществ над одним вызовом монолотного метода:
Вы можете видеть вычисления, как они выполняются в отладчике, независимо от метода класса, в котором вы находитесь. Это преимущество производительности при отладке класса.
Если вычисления дискретны, свойства будут выполняться очень быстро, что означает (на мой взгляд), они соблюдают правила проектирования свойств. Абсурдно думать, что руководство для дизайна должно рассматриваться как смирительная рубашка. Помните: нет серебряной пули.
Если вычисления отмечены как частные, так и внутренние, они не добавляют излишнюю сложность для потребителей этого класса.
Если все свойства достаточно дискретны, встраивание компилятора может решить проблемы с производительностью для вас.
Наконец, если окончательный метод, который возвращает ваш окончательный расчет, намного проще поддерживать и понимать, потому что вы можете его прочитать, это совершенно убедительный аргумент сам по себе.
Одна из лучших вещей, которые вы можете сделать, - это подумать о себе и осмелиться бросить вызов предвзятому единообразному значению "Один размер подходит всем" наших сверстников и предшественников. Есть исключения из каждого правила. Этот случай вполне может быть одним из них.
Постскриптум: Я не считаю, что мы должны отказаться от стандартного дизайна собственности в подавляющем большинстве случаев. Но есть случаи, когда требуется отклонение от стандарта (TM), поскольку имеет смысл сделать это.
Я обычно понимаю, что будет делать метод или свойство. Если это займет немного времени, я буду использовать метод. Если это очень быстро или имеет очень небольшое количество операций, происходящих за кулисами, я сделаю это свойством.
Лично я предпочел бы, если вы создадите свой общедоступный API как метод вместо свойства. В С# свойства должны быть как можно более быстрыми. Подробнее об этом обсуждении: Свойства vs Методы
Внутри GetConsumption может использовать любое количество частных свойств для получения результата, выбор за вами.
Вы говорите, что вы получаете значение из семи входов, вы внедрили семь свойств, по одному для каждого ввода, и у вас есть свойство getter для результата. Некоторые вещи, которые вы, возможно, захотите рассмотреть, следующие:
Что произойдет, если вызывающий абонент не сможет установить один или несколько из семи свойств ввода? Имеет ли смысл все еще смысл? Будет ли выбрано исключение (например, деление на ноль)?
В некоторых случаях API может быть менее уязвимым. Если я должен вызвать метод, который принимает семь параметров, я знаю, что я должен предоставить все семь параметров, чтобы получить результат. И если некоторые из параметров являются необязательными, разные перегрузки метода дают понять, какие из них.
В отличие от этого, возможно, не так ясно, что перед доступом к свойству "result" я должен установить семь свойств, и его легко забыть.
Если у вас есть метод с несколькими параметрами, вы можете более легко получить более надежную проверку. Например, вы можете вызвать ArgumentException, если "параметр A и параметр B равны нулю".
Если вы используете свойства для своих входов, каждое свойство будет установлено независимо, поэтому вы не сможете выполнить проверку при вводе данных - только тогда, когда свойство результата разыменовывается, что может быть менее интуитивно понятным.
Я использую методы для обозначения любых действий над объектом или изменения состояния объекта. поэтому в этом случае я бы назвал функцию CalculateConsumption(), которая вычисляет значения из других свойств.