Является ли этот дизайн чрезмерным? - PullRequest
7 голосов
/ 23 марта 2011

Рассматриваете ли вы использование интерфейса и полиморфизма для расширения этого дизайна, чтобы быть чрезмерно инженерным?

Плюсы

  • Extensible
  • Герметичная
  • Auto-волшебно

Против

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

Мой инстинкт заключается в том, что в данном конкретном случае один if оператор и логический флаг - лучший вариант, но не все согласились со мной.

Что вы думаете?


Оригинал

// Connects to a local pipe, and naturally
// owns that connection
struct CommandWriter
{
   CommandWriter() {
       fd = open("/path/to/fifo", O_WRONLY);
       if (fd == -1)
           throw std::runtime_error("Could not establish connection to FIFO");
   };

   ~CommandWriter() {
       close(fd);
   };

   // (Has useful member functions here)

   private:
      CommandWriter(CommandWriter const&); // Not relevant to question

      int fd;
};

Расширенный с логическим флагом

// Adds a constructor where an FD can be specified
// from the call site, and no ownership is taken
struct CommandWriter
{
   CommandWriter() : owns_fd(true) {
       fd = open("/path/to/fifo", O_WRONLY);
       if (fd == -1)
           throw std::runtime_error("Could not establish connection to FIFO");
   };

   CommandWriter(int fd) : fd(fd), owns_fd(false) {};

   ~CommandWriter() {
       if (owns_fd)
          close(fd);
   };

   // (Has useful member functions here)

   private:
      CommandWriter(CommandWriter const&); // Not relevant to question

      int  fd;
      bool owns_fd;
};

Расширено полиморфизмом

// Sorry for the poor type names!
struct ICommandWriter
{
   virtual ICommandWriter() {}

   // (Has useful member functions here)

   private:
      ICommandWriter(ICommandWriter const&); // Not relevant to question
};

struct CommandWriter_Connects : ICommandWriter
{
   CommandWriter_Connects() {
       fd = open("/path/to/fifo", O_WRONLY);
       if (fd == -1)
           throw std::runtime_error("Could not establish connection to FIFO");
   };

   ~CommandWriter_Connects() {
       close(fd);
   };

   // (Has useful member functions here)

   private:
      int fd;
};

struct CommandWriter_Uses : ICommandWriter
{
   CommandWriter_Uses(int fd) : fd(fd) {};

   ~CommandWriter_Uses() {};

   // (Has useful member functions here)

   private:
      int fd;
};

Ответы [ 6 ]

9 голосов
/ 23 марта 2011

Это зависит от того, для чего вы будете его использовать. Если у вас большой проект и вы будете использовать варианты класса много раз, то, безусловно, имеет смысл сделать его гибким.

Эмпирическое правило:

  1. Используйте это один раз - просто будьте проще.
  2. Используйте его дважды - сделайте его простым, сделайте копию и при необходимости измените
  3. Три или более - обобщите это и сделайте так, чтобы оно работало для всех случаев.

Конечно, есть много исключений, но это отправная точка.

7 голосов
/ 23 марта 2011

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

CommandWriter::CommandWriter (int _fd) : fd (dup (_fd)) {};

Добавление логического флага для этого - создание колеса.И используя полиморфизм строит вертолет.

1 голос
/ 23 марта 2011

В коде полиморфизма:

// (Has useful member functions here)

Правда ли? Если во всех трех местах есть много функций-членов (базовый класс и два производных класса), то владелец и не владеющий автором действительно разные звери. Вероятно, тогда лучше разделить их на разные классы, чем иметь класс, который ведет себя очень по-разному в соответствии с установленным в нем логическим флагом, в соответствии с которым был вызван конструктор.

Я подозреваю, что все полезные функции-члены находятся в базовом классе, и все производные классы делают это изменение конструкции и разрушение. В этом случае я бы хотел класс smart_fd, который содержит fd и знает, как его утилизировать (вам нужно два случая - вызвать close или ничего не делать. shared_ptr разрешает произвольную деструктивную функцию, но вы, вероятно, этого не делаете нужно здесь).

Затем добавьте один из них в CommandWriter и инициализируйте его по-разному в зависимости от того, какой конструктор CommandWriter вызывается.

Практическое правило: классы, управляющие ресурсами, больше ничего не должны делать.

1 голос
/ 23 марта 2011

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

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

Но, как уже упоминалось выше, если вынамерены использовать его только один раз, просто возьмите версию с логическим флагом.

1 голос
/ 23 марта 2011

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

Сохраняйте это простым и глупым.

(Я бы предпочел полиморфное решение, если уверен, что в будущем необходимо добавить дополнительные пути к коду).

1 голос
/ 23 марта 2011

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

Альтернативным решением будет использование Шаблон стратегии .Это похоже на пользовательские средства удаления для умных указателей Boost.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...