Это недостаток дизайна? - PullRequest
0 голосов
/ 11 ноября 2009

Рассмотрим два класса

class A{
     public:
       A(){
       }
       ~A(){
       }
};


class AImpl : public A{
      public:
         AImpl(){
             a = new AInternal();
         }
         AImpl(AInternal *a){
             this->_a = a;
         }
         ~AImpl(){
             if(a){
                delete a;
                a = null;
             }
         }
       private:
             AInternal *a;
};

Я пытаюсь скрыть реализацию AInternal и выставить только интерфейс A. Две вещи, которые я вижу здесь

  1. класс А полностью пуст.
  2. Сокрытие достигается в основном через наследование. Я должен фактически использовать downcasting и upcasting от A до AImpl и наоборот.

Это хороший дизайн. Будучи очень неопытным в проектировании, я не вижу его подводных камней и почему это плохо?

Ответы [ 6 ]

18 голосов
/ 11 ноября 2009

Вы слишком усложняете вещи, используя 3 класса. Я думаю, что вы ищете это идиома pimpl .

1 голос
/ 11 ноября 2009

Я пытаюсь скрыть реализацию AInternal и выставить только интерфейс A.

Я думаю, вы пытаетесь сделать что-то вроде factory .

Вот пример:

 class IA {                                               
 public:
         IA() {}
         virtual ~IA() {}
         virtual void dosth() =0;
 };

 class Factory {
 private:
         class A : public IA {
         public:
                 A () {}
                 virtual ~A() {}

                 void dosth() { cout << "Hello World"; }
         };

 public:
         Factory () {}
         virtual ~Factory() {}

         IA*newA() { return new A; }
 };

И использование класса Фабрика:

Factory f;
IA*a = f.newA();
a->dosth();
return 0;
0 голосов
/ 11 ноября 2009

Вы, кажется, объединяете две общие конструктивные особенности:

1) AInternal - это "прыщ". Это обеспечивает лучшую инкапсуляцию, например, если вам нужно добавить новое поле в AInternal, тогда размер AImpl не изменится. Это нормально.

2) A - это базовый класс, используемый для обозначения интерфейса. Поскольку вы говорите о повышении и понижении, я предполагаю, что вам нужен динамический полиморфизм, что означает, что у вас будут функции, которые передают указатели или ссылки на A, и во время выполнения ссылки будут фактически иметь тип AImpl. Это также хорошо, за исключением того, что деструктор А должен быть либо виртуальным и общедоступным, либо не виртуальным и защищенным.

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

Чего я не вижу, так это того, почему вам когда-нибудь пришлось бы опускаться (то есть, бросить A * на AImpl *) Обычно это плохие новости. Таким образом, в вашем дизайне могут быть некоторые проблемы, которые можно выявить, только показав нам больше определений классов и клиентский код, который фактически использует A и AImpl.

0 голосов
/ 11 ноября 2009

Если вы собираетесь сделать это таким образом, то деструктор ~A должен быть virtual.

0 голосов
/ 11 ноября 2009

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

0 голосов
/ 11 ноября 2009

IMO AInternal не имеет смысла. Все, что вы там делаете, должно быть сделано в AImpl. В противном случае это нормально делать в C ++.

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