Условное использование услуг в доменном объекте - PullRequest
1 голос
/ 18 декабря 2009

Итак, предположим, что в моем магазине сэндвичей есть два сервисных интерфейса: IMeatGetter и IVeggiesGetter. Теперь у меня есть доменный объект Sandwich со свойством IsVegetarian. Я хочу метод, чтобы выбрать все возможные начинки, как

void GetToppings
{
  if IsVegetarian
    select (veggies)
  else
    select (veggies union meats)
}

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

Что если IMeatGetter - очень медленная операция? Это изменит ваш ответ?

1 Ответ

2 голосов
/ 18 декабря 2009

Существует школа мысли, связанная с доменно-управляемым дизайном , которая утверждает, что доменные объекты должны быть POCO / POJO, поэтому в соответствии с этой философией класс Sandwich должен быть независимым от сервисов IMeatGetter и IVeggiesGetter. Я лично считаю, что этот подход работает хорошо, несмотря на определенные недостатки.

Если полные списки овощей и мяса имеют смысл в контексте сэндвича (хотя для меня это звучит как потенциальный сэндвич), я бы передал их как часть конструктора. Вот пример C #:

public class Sandwich
{
    private readonly IEnumerable<Ingredient> veggies;
    private readonly IEnumerable<Ingredient> meats;

    public Sandwich(IEnumerable<Ingredient> veggies, IEnumerable<Ingredient> meats)
    {
        if(veggies == null)
        {
            throw new ArgumentNullException("veggies");
        }
        if(meats == null)
        {
            throw new ArgumentNullException("meats");
        }

        this.veggies = veggies;
        this.meats = meats;
    }

    // implement GetToppings as described in OP
}

Если IMeatGetter используется для получения списка мяса, и это очень медленная операция, то нет, это не изменит мой ответ . Таким образом, мы отделили класс Sandwich от логики поиска мяса.

Это позволяет нам пытаться управлять временем жизни списка мяса в другом месте. Мы могли бы, например, написать реализацию IMeatGetter, которая кэширует список мяса в памяти.

Даже если это невозможно, мы все равно можем изменить сам список, чтобы выполнить ленивую оценку. Хотя я не знаю, какую платформу вы используете, в .NET интерфейс IEnumerable<T> позволяет отложенное выполнение ; другими словами, он не извлекает список, пока вы фактически не начнете перечисление.

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

Таким образом, вы передаете ссылку на список в класс Sandwich. Этот список может быть загружен или не загружен в память в то время, но это зависит от вас, независимо от класса Sandwich. Таким образом, класс «сэндвич» соответствует принципу одиночной ответственности , поскольку ему не приходится управлять временем жизни мяса.

...