Как лучше уменьшить зависимости классов, чтобы легче было их модульное тестирование - PullRequest
1 голос
/ 20 ноября 2010

Я занимаюсь разработкой структуры генетического алгоритма и первоначально принял решение о следующем IIndividual определении:

public interface IIndividual : ICloneable
{
    int NumberOfGenes { get; }
    double Fitness { get; }
    IGene GetGeneAt(int index);
    void AddGene(IGene gene);
    void SetGeneAt(int index, IGene gene);
    void Mutate();
    IIndividual CrossOverWith(IIndividual individual);
    void CalculateFitness();
    string ToString();
}

Все выглядело хорошо, но как только я разработал другие классы, которые использовали IIndividual, я пришел к выводу, что создание юнит-тестов для этих классов было бы довольно болезненно. Чтобы понять почему, я покажу вам график зависимости IIndividual:

alt text

Таким образом, при использовании IIndividual мне приходится также создавать / управлять экземплярами IGene и IInterval. Я могу легко решить эту проблему, переопределив мой IIndividual интерфейс следующим образом:

public interface IIndividual : ICloneable
{
    int NumberOfGenes { get; }
    void AddGene(double value, double minValue, double maxValue);
    void SetGeneAt(int index, double value);
    double GetGeneMinimumValue(int index);
    double GetGeneMaximumValue(int index);
    double Fitness { get; }
    double GetGeneAt(int index);
    void Mutate();
    IIndividual CrossOverWith(IIndividual individual);
    void CalculateFitness();
    string ToString();
}

со следующим графом зависимостей:

alt text

Это будет довольно легко протестировать, за счет некоторого снижения производительности (меня сейчас это не очень беспокоит) и увеличения веса на IIndividual (больше методов). Существует также большая проблема для клиентов IIndividual, так как, если они хотят добавить Джин, им нужно будет добавить все маленькие параметры Джина «вручную» в AddGene(value, minimumValue, maximumValue) вместо AddGene(gene)!

Мой вопрос:

Какой дизайн вы предпочитаете и почему? Кроме того, по каким критериям знать, где остановиться?

Я мог бы сделать то же самое, что и с IIndividual до IGene, поэтому любой, кто использует IGene, не должен знать о Interval. У меня есть класс Population, который будет служить коллекцией IIndividuals. Что мешает мне сделать то же самое, что я сделал с IIndividual до Population? Должна быть какая-то граница, какие-то критерии, чтобы понять, в каких случаях лучше всего просто оставить это (иметь некоторые зависимости), а в каких случаях лучше их скрывать (как во второй реализации IIndividual).

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

Спасибо!

Ответы [ 2 ]

2 голосов
/ 21 ноября 2010

@ andersoj показывает, что вы на правильном пути в отношении книги Пера, но вот более глубокий анализ:

Эрик Эванс Домен-управляемый дизайн относится к типу границы, которую вы обдумываете. бесплатное суммирование InfoQ - хорошее место для начала, так как, по моему скромному мнению, Эванс слишком многословен. То, что Эванс описывает как Совокупные корни, является типом границы, которую, я думаю, вы должны рассмотреть

Индивид выглядит как совокупный корень, подразумевая, что Гены и Интервалы существуют только для класса Индивидуум и могут быть созданы только для него. Предполагая, что тогда эти классы не должны использоваться клиентами, ваш рефакторинг класса Individual для эффективного скрытия или удаления двух других имеет смысл.

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

В любом случае начните с тестов характеристик. Это тесты, которые фиксируют то, что делает код сейчас . Тогда рефакторинг оттуда.

1 голос
/ 20 ноября 2010

(я прошу прощения за то, что не ответил прямо на ваш вопрос, но я не могу не указать вам таким образом ...) Я не могу предложить книгу Эффективная работа с устаревшим кодом (автор Майкл Фезерс) достаточно высоко. Это выдающееся решение проблем, связанных с получением кода при тестировании (модульном, функциональном).

...