Для такого рода вопросов Мартин Фаулер предложил образец спецификации :
... шаблон проектирования, посредством которого бизнес-правила могут быть объединены путем объединения бизнес-правил с использованием логической логики.
Шаблон спецификации описывает бизнес-правило, которое можно комбинировать с другими бизнес-правилами. В этом шаблоне единица бизнес-логики наследует свои функциональные возможности от абстрактного совокупного класса составной спецификации. Класс составной спецификации имеет одну функцию с именем IsSatisfiedBy, которая возвращает логическое значение. После создания спецификации спецификация «сцепляется» с другими спецификациями, что делает новые спецификации легко поддерживаемыми, но в то же время легко настраиваемой бизнес-логикой. Кроме того, после создания экземпляра бизнес-логика может, через вызов метода или инверсию управления, изменить свое состояние, чтобы стать делегатом других классов, таких как постоянное хранилище ...
Выше звучит немного брови (по крайней мере, для меня), но когда я попробовал это в своем коде, все прошло довольно гладко и оказалось легко реализовать и прочитать.
На мой взгляд, основная идея состоит в том, чтобы «извлечь» код, который выполняет проверки, в выделенный метод (ы) / объекты.
В вашем netWorth
примере это может выглядеть примерно так:
int netWorth(Person* person) {
if (isSatisfiedBySpec(person)) {
return person->assets - person->liabilities;
}
log("person doesn't satisfy spec");
return -1;
}
#define BOOLEAN int // assuming C here
BOOLEAN isSatisfiedBySpec(Person* person) {
return Person != NULL
&& person->isAlive
&& person->assets != -1
&& person->liabilities != -1;
}
Ваш случай выглядит довольно простым, так что все проверки выглядят нормально, чтобы поместиться в простой список в пределах одного метода. Мне часто приходится разделять на несколько методов, чтобы лучше читать.
Я также обычно группирую / извлекаю связанные со спецификацией методы в выделенном объекте, хотя ваш случай выглядит нормально без этого.
// ...
Specification s, *spec = initialize(s, person);
if (spec->isSatisfied()) {
return person->assets - person->liabilities;
}
log("person doesn't satisfy spec");
return -1;
// ...
Этот вопрос в Stack Overflow рекомендует несколько ссылок в дополнение к одной из упомянутых выше:
Пример шаблона спецификации . В частности, ответы предлагают Dimecasts «Изучение шаблона спецификаций» для ознакомления с примером и упоминают статью «Спецификации», написанную Эриком Эвансом и Мартином Фаулером .