Как адаптировать шаблон спецификации для оценки комбинации объектов? - PullRequest
2 голосов
/ 24 февраля 2010

Я знаю, что шаблон Спецификации описывает, как использовать иерархию классов, реализующую ISpecification<T>, чтобы оценить, соответствует ли объект-кандидат типа T определенной спецификации (= удовлетворяет бизнес-правилу).

Моя проблема: бизнес-правило, которое я хочу реализовать, должно оценивать несколько объектов (например, Заказчик и Контракт).

Мой двойной вопрос:

  • Существуют ли типичные адаптации шаблонов спецификаций для достижения этой цели? Я могу думать только о том, чтобы удалить реализацию ISpecification<T> моим классом спецификации и взять столько параметров, сколько я хочу в методе isSatisfiedBy(). Но, делая это, я теряю способность комбинировать эту спецификацию с другими.

  • Эта проблема обнаруживает недостаток в моем дизайне? (то есть, что мне нужно оценить, используя Заказчика, а Контракт должен оцениваться по другому объекту, например, по Подписке, которая может содержать всю необходимую информацию)?

Ответы [ 4 ]

4 голосов
/ 25 февраля 2010

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

Пример:

public class ShouldCreateEmailAccountSpecification : ISpecification<Customer>
{
    public ShouldCreateEmailAccountSpecification(Contract selectedContract)
    {
       SelectedContract = selectedContract;
    }

    public Contract SelectedContract { get; private set; }

    public bool IsSatisfiedBy(Customer subject)
    {
        return false;
    }
}
2 голосов
/ 20 июня 2013

Решение Пако, согласно которому один объект рассматривается как субъект, а другой - как параметр, с использованием инжекции конструктора, может иногда работать, но если оба объекта создаются после объекта спецификации, это делает вещи довольно сложными.

Одним из решений этой проблемы является использование объекта параметра, как в этом предложении по рефакторингу: http://sourcemaking.com/refactoring/introduce-parameter-object.

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

public class ParameterObject  
{
    public Customer Customer { get; set; }
    public Contract Contract { get; set; }
}

Тогда ваша общая спецификация становится для этого типа:

public class SomeSpecification : ISpecification<ParameterObject>
{
    public bool IsSatisfiedBy(ParameterObject candidate)
    {
        return false;
    }
}
2 голосов
/ 24 февраля 2010

Ваша проблема в том, что ваш интерфейс спецификации использует параметр универсального типа, который не позволяет использовать его для объединения логики оценки между различными специализациями (Заказчик, Контракт), поскольку ISpecification фактически является интерфейсом, отличным от ISpecification . Вы можете использовать подход Джеффа, описанный выше, который избавляет от параметра типа и передает все как базовый тип (объект). В зависимости от того, какой язык вы используете, вы также можете повысить уровень и объединить спецификации с логической логикой с помощью делегатов. Пример C # (не особенно полезен, как написано, но может дать вам некоторые идеи для фреймворка):

ISpecification<Customer> cust_spec = /*...*/
ISpecification<Contract> contract_spec = /*... */
bool result = EvalWithAnd( () => cust_spec.IsSatisfiedBy(customer), () => contract_spec.IsSatisfiedBy( contract ) );

public void EvalWithAnd( params Func<bool>[] specs )
{
    foreach( var spec in specs )
    {
       if ( !spec() )
          return false; /* If any return false, we can short-circuit */
    }
    return true; /* all delegates returned true */
}
0 голосов
/ 27 марта 2010

Не знаю, понял ли я ваш вопрос.

Если вы используете одну и ту же спецификацию как для Заказчика, так и для Контракта, это означает, что вы можете отправлять одинаковые сообщения им обоим. Эту проблему можно решить, заставив их обоих реализовать интерфейс и использовать этот интерфейс в качестве типа T. Я не знаю, имеет ли это смысл в вашей области.

Извините, если это не ответ на ваш вопрос.

...