Считается ли плохим дизайном делать длинные операции в конструкторе? - PullRequest
8 голосов
/ 06 ноября 2008

Я реализую класс для сравнения деревьев каталогов (в C #). Сначала я реализовал фактическое сравнение в конструкторе класса. Как это:

DirectoryComparer c = new DirectoryComparer("C:\\Dir1", "C:\\Dir2");

Но нехорошо делать возможную длительную операцию в конструкторе. Альтернативный способ - сделать конструктор закрытым и добавить статический метод, подобный этому:

DirectoryComparer c = DirectoryComparer.Compare("C:\\Dir1", "C:\\Dir2");

Что ты думаешь? Вы ожидаете, что конструктор будет "быстрым"? Второй пример лучше или просто усложняет использование класса?

Кстати:

Я не буду отмечать любой ответ как принятый, потому что я не думаю, что есть правильный ответ, только предпочтения и вкус.

Edit:

Просто чтобы немного пояснить мой пример. Я не только заинтересован, если каталоги отличаются, меня также интересует, как они различаются (какие файлы). Так что простого возвращаемого значения int будет недостаточно. Ответ от cdragon76.myopenid.com на самом деле очень близок к тому, что я хочу (+1 к вам).

Ответы [ 14 ]

12 голосов
/ 06 ноября 2008

Я бы подумал, что комбинация этих двух является "правильным" выбором, так как я ожидаю, что метод Compare будет возвращать результат сравнения, а не сам сравнитель.

DirectoryComparer c = new DirectoryComparer();

int equality = c.Compare("C:\\Dir1", "C:\\Dir2");

... и, как упоминает Дана, в .Net есть интерфейс IComparer , который отражает этот шаблон.

Метод IComparer.Compare возвращает значение типа int, поскольку использование классов IComparer в основном осуществляется с сортировкой. Общая схема, тем не менее, соответствует проблеме вопроса в следующем:

  1. Конструктор инициализирует экземпляр с (необязательно) «настройкой» параметров
  2. Метод сравнения принимает два параметра «данных», сравнивает их и возвращает «результат»

Теперь результатом может быть int, bool, коллекция diff-файлов. Все, что соответствует потребности.

10 голосов
/ 06 ноября 2008

Я предпочитаю второй.

Я ожидаю, что конструктор создаст экземпляр класса. Метод сравнения делает то, для чего предназначен.

5 голосов
/ 06 ноября 2008

Я думаю, что интерфейс может быть тем, что вы ищете. Я хотел бы создать класс для представления каталога и реализовать интерфейс DirectoryComparer. Этот интерфейс будет включать метод сравнения. Если C # уже имеет интерфейс Comparable, вы также можете просто реализовать это.

В коде ваш звонок будет:

D1 = new Directory("C:\");
..
D1.compare(D2);
3 голосов
/ 06 ноября 2008

Я согласен с общим мнением о том, что нельзя выполнять длинные операции внутри конструкторов.

Кроме того, что касается темы дизайна, я бы подумал об изменении вашего второго примера, чтобы метод DirectoryComparer.Compare возвращал что-то отличное от DirectoryComparer объекта. (Возможно, новый класс с именем DirectoryDifferences или DirectoryComparisonResult.) Объект типа DirectoryComparer звучит как объект, который вы будете использовать для сравнения каталогов, а не как объект, который представляет различия между парой каталогов.

Тогда, если вы хотите определить различные способы сравнения каталогов (например, игнорирование меток времени, атрибутов только для чтения, пустых каталогов и т. Д.), Вы можете задать эти параметры, передаваемые в конструктор класса DirectoryComparer. Или, если вы всегда хотите, чтобы DirectoryComparer имел одинаковые правила сравнения каталогов, вы можете просто сделать DirectoryComparer статическим классом.

Например:

DirectoryComparer comparer = new DirectoryComparer(
    DirectoryComparerOptions.IgnoreDirectoryAttributes
);
DirectoryComparerResult result = comparer.Compare("C:\\Dir1", "C:\\Dir2");
3 голосов
/ 06 ноября 2008

Вы никогда не должны делать ничего, что могло бы потерпеть неудачу в конструкторе. Вы не хотите когда-либо создавать недопустимые объекты. Хотя вы могли бы реализовать состояние «зомби», когда объект мало что делает, гораздо лучше выполнять любую сложную логику отдельными методами.

2 голосов
/ 06 ноября 2008

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

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

1 голос
/ 07 ноября 2008

Я думаю, что для конструктора не только нормально занимать столько времени, сколько необходимо для создания допустимого объекта, но для этого требуется конструктор. Откладывать создание объекта очень плохо, так как в итоге вы получаете потенциально недопустимые объекты. Таким образом, вам придется проверять объект каждый раз, прежде чем дотронуться до него (именно так это делается в MFC, у вас есть bool IsValid() методы везде).

Я вижу лишь небольшую разницу в двух способах создания объекта. В любом случае новый оператор можно рассматривать как статическую функцию класса. Итак, все это сводится к синтаксическому сахару.

Что делает класс DirectoryComparer? Какова его ответственность? С моей точки зрения (это точка зрения программиста на C ++), похоже, что вам было бы лучше, если бы вы просто использовали свободную функцию, но я не думаю, что вы можете иметь свободные функции в C #, не так ли? Я предполагаю, что вы соберете файлы, которые отличаются в объекте DirectoryComparer. Если это так, вы могли бы лучше создать что-то вроде массива файлов или эквивалентного класса с соответствующим названием.

1 голос
/ 06 ноября 2008

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

  • Сравните еще раз - что, если каталоги изменились?
  • Измените файлы, которые вы сравниваете, обновив участников.

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

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

Поэтому я предпочитаю:

DirectoryComparer = new DirectoryComparer(&Dir1,&Dir2);</p> <p>DirectoryComparer->Compare();</p> <p>

Или

DirectoryComparer = new DirectoryComparer();</p> <p>DirectoryComparer->Compare(&Dir1,&Dir2);</p> <p>

1 голос
/ 06 ноября 2008

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

0 голосов
/ 07 ноября 2008

Я не думаю, что разговоры об абстрактных терминах, таких как "longy", имеют какое-то отношение к решению о том, что вы положили что-то в конструктор или нет.

Конструктор - это то, что должно использоваться для инициализации объекта, метод должен использоваться для того, чтобы «что-то делать», то есть иметь функцию.

...