Конкретные класс-специфические методы - PullRequest
4 голосов
/ 14 февраля 2009

У меня интересная проблема. Рассмотрим эту иерархию классов:

class Base
{
public:
   virtual float GetMember( void ) const =0;
   virtual void SetMember( float p ) =0;
};

class ConcreteFoo : public Base
{
public:
   ConcreteFoo( "foo specific stuff here" );

   virtual float GetMember( void ) const;
   virtual void SetMember( float p );

   // the problem
   void foo_specific_method( "arbitrary parameters" );
};

Base* DynamicFactory::NewBase( std::string drawable_name );

// it would be used like this
Base* foo = dynamic_factory.NewBase("foo");

Я опустил определение DynamicFactory и то, как строители зарегистрирован с этим. Объекты Builder связаны с именем и выделит конкретную реализацию Базы. Настоящий реализация немного сложнее с shared_ptr для обработки памяти рекламации, но они не важны для моей проблемы.

ConcreteFoo имеет метод, специфичный для класса. Но так как конкретные случаи создать в динамической фабрике конкретные классы не известны или доступны, они могут быть объявлены только в исходном файле. Как я могу выставить foo_specific_method пользователям Base*?

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

Я не просто ищу мнения о моих оригинальных решениях, новых был бы оценен.

Ответы [ 10 ]

2 голосов
/ 14 февраля 2009

Приведение будет быстрее, чем в большинстве других решений, однако:

в базовом классе добавить:

void passthru( const string &concreteClassName, const string &functionname, vector<string*> args )
{
    if( concreteClassName == className )
         runPassThru( functionname, args );
}

private:
    string className;
    map<string, int> funcmap;
    virtual void runPassThru( const string &functionname, vector<string*> args ) {}

в каждом производном классе:

void runPassThru( const string &functionname, vector<string*> args )
{
   switch( funcmap.get( functionname ))
   {
      case 1:
          //verify args
          // call function
        break;
      // etc..
   }
}

// call in constructor
void registerFunctions()
{
      funcmap.put( "functionName", id );
      //etc.
}
1 голос
/ 14 февраля 2009

Решение CrazyMetaType.

Это решение не очень хорошо продумано. Я надеялся, что кто-то может имел опыт работы с чем-то похожим. Я видел это относится к проблема неизвестного числа известного типа. Это было довольно гладко. я думал применить его к неизвестному номеру неизвестного типа *** S ***

Основная идея - CrazyMetaType собирает параметры типа безопасный способ, то выполнение конкретного конкретного метода.

class Base
{
   ...
   virtual CrazyMetaType concrete_specific( int kind ) =0;
};

// used like this
foo->concrete_specific(foo_method_id) << "foo specific" << foo_specific;

Мое единственное беспокойство в связи с этим решением состоит в том, что CrazyMetaType будет безумно сложно заставить это работать. Я до этой задачи, но я не может рассчитывать на то, что будущие пользователи будут экспертами по С ++, просто чтобы добавить один конкретный конкретный метод.

0 голосов
/ 14 февраля 2009

Кажется, что контекст предполагает, что пользователь будет работать с вашим ConcreteType и знает, что он делает это.

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

0 голосов
/ 14 февраля 2009

Странная часть заключается в том, что пользователи вашего DynamicFactory получают базовый тип, но ему нужно делать определенные вещи, когда это ConcreteFoo.

Может быть, фабрика не должна использоваться.

Попробуйте взглянуть на другие механизмы внедрения зависимостей, такие как создание ConcreteFoo самостоятельно, передать указатель типа ConcreteFoo тем, кто в нем нуждается, и указатель типа Base на другие.

0 голосов
/ 14 февраля 2009

Вы можете сделать что-то похожее на аргумент CrazyMetaType или cstdarg, но простой и C ++ - иш. (Возможно, это может быть SaneMetaType.) Просто определите базовый класс для аргументов concrete_specific и заставьте людей выводить из этого конкретные типы аргументов. Что-то вроде

class ConcreteSpecificArgumentBase;

class Base
{
   ...  
   virtual void concrete_specific( ConcreteSpecificArgumentBase &argument ) =0;
};

Конечно, вам понадобится RTTI, чтобы разобраться в каждой версии concrete_specific. Но если ConcreteSpecificArgumentBase хорошо спроектирован, то по крайней мере это сделает вызов concrete_specific довольно простым.

0 голосов
/ 14 февраля 2009

Если конкретный объект имеет метод, специфичный для класса, это означает, что вы будете вызывать этот метод только тогда, когда имеете дело с экземпляром этого класса, а не когда вы имеете дело с базовым базовым классом. Это происходит из-за того, что вы выполняете оператор switch, который проверяет тип объекта?

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

Вы также можете попробовать шаблон Decorator.

0 голосов
/ 14 февраля 2009

Не могли бы вы создать другие неконкретные подклассы Base и затем использовать несколько фабричных методов в DynamicFactory?

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

0 голосов
/ 14 февраля 2009

Решение cstdarg.

Бьярн Страуструп сказал:

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

class Base
{
   ...
   /// \return true if 'kind' supported
   virtual bool concrete_specific( int kind, ... ) =0;
};

Недостатки здесь:

  • почти никто не знает, как правильно использовать cstdarg
  • это не очень с ++ - у
  • это небезопасно.
0 голосов
/ 14 февраля 2009

Просто разыграй.

Когда нужен специальный метод foo, обычно вы знаете, что Base* на самом деле ConcreteFoo. Так что просто убедитесь, что определение класса ConcreteFoo доступно и:

ConcreteFoo* foo2 = dynamic_cast<ConcreteFoo*>(foo);

Одна из причин, по которой мне не нравится это решение, это то, что dynamic_casts работают медленно и требуется RTTI.

Следующим шагом будет избегать dynamic_cast.

ConcreteFoo* foo_cast( Base* d )
{
   if( d->id() == the_foo_id )
   {
      return static_cast<ConcreteFoo*>(d);
   }

   throw std::runtime_error("you're screwed");
}

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

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

0 голосов
/ 14 февраля 2009

Добавление специальных функций к Base.

Самое простое и неприемлемое решение - добавить foo_specific_method до Base. Тогда классы, которые не использовать его можно просто определить как пустое. Это не работает, потому что пользователи могут регистрировать своих собственных строителей с dynamic_factory. Новые классы также могут иметь конкретный класс конкретные методы.

В духе этого решения один чуть лучше. Добавить общий функционирует до Base.

class Base
{
   ...
   /// \return true if 'kind' supported
   virtual bool concrete_specific( int kind, "foo specific parameters" );
};

Проблема здесь в том, что может быть довольно много перегрузок конкретные для разных наборов параметров.

...