我有一个有趣的问题。考虑这个课程层次结构:
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定义以及Builders的定义 在它注册。 Builder对象与名称相关联 并将分配Base的具体实现。实际上 使用shared_ptr处理内存时,实现有点复杂 回收,但它们对我的问题并不重要。
ConcreteFoo
具有特定于类的方法。但自具体实例以来
在动态工厂中创建具体类是未知的或
可访问的,它们只能在源文件中声明。我怎么能够
向foo_specific_method
的用户公开Base*
?
我正在添加我提出的解决方案作为答案。我已经命名了 它们可以让你轻松地在答案中引用它们。
我不只是在寻找对我的原始解决方案,新的解决方案的意见 将不胜感激。
答案 0 :(得分:2)
然而,演员阵容会比大多数其他解决方案更快:
基类中的添加:
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 :(得分:1)
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
将是
让这个变得复杂起来非常复杂。我能胜任这项任务,但我
不能指望未来的用户只需要添加c ++专家
一个具体的具体方法。
答案 2 :(得分:0)
向Base
添加特殊功能。
最简单,最不可接受的解决方案是添加
foo_specific_method到Base
。然后那些没有的课程
使用它可以将它定义为空。这不起作用,因为
允许用户注册自己的Builders
dynamic_factory。新课程也可能有具体的课程
具体方法。
在这个解决方案的精神,是一个稍微好一点。添加通用
函数Base
。
class Base
{
...
/// \return true if 'kind' supported
virtual bool concrete_specific( int kind, "foo specific parameters" );
};
这里的问题是可能存在相当多的重载 具体针对不同的参数集。
答案 3 :(得分:0)
投下它。
当需要特定于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");
}
这需要Base类中的另一个方法完全 可以接受,但它需要管理id。这很难 当用户可以在动态工厂注册他们自己的Builders时。
我不太喜欢任何铸造解决方案,因为它需要 要在使用专用方法的位置定义的用户类。 但也许我只是一个范围纳粹。
答案 4 :(得分:0)
cstdarg
解决方案。
Bjarn Stroustrup说:
一个定义良好的程序最多需要几个函数 参数类型未完全指定。重载函数和 使用默认参数的函数可用于处理类型 在大多数情况下检查否则会考虑离开 参数类型未指定。只有当参数的数量和 参数的类型变化是必要的省略号
class Base
{
...
/// \return true if 'kind' supported
virtual bool concrete_specific( int kind, ... ) =0;
};
这里的缺点是:
答案 5 :(得分:0)
你能创建Base的其他非具体子类,然后在DynamicFactory中使用多个工厂方法吗?
你的目标似乎是颠覆子类的重点。我真的很想知道你在做什么需要这种方法。
答案 6 :(得分:0)
如果具体对象具有特定于类的方法,那么它意味着您只是在处理该类的实例时才专门调用该方法,而不是在处理泛型基类时。这是关于b / c你正在运行一个检查对象类型的switch语句吗?
我将从不同的角度处理这个问题,使用“不可接受的”第一个解决方案,但没有参数,具体对象具有可存储其状态的成员变量。虽然我猜这会强制你有一个成员关联数组作为基类的一部分,以避免强制转换为首先设置状态。
您可能还想尝试装饰器模式。
答案 7 :(得分:0)
你可以做类似于CrazyMetaType
或cstdarg参数的事情,但是简单和C ++ - ish。 (也许这可能是SaneMetaType
。)只需为concrete_specific
的参数定义一个基类,并让人们从中派生出特定的参数类型。像
class ConcreteSpecificArgumentBase;
class Base
{
...
virtual void concrete_specific( ConcreteSpecificArgumentBase &argument ) =0;
};
当然,您需要RTTI在每个版本的concrete_specific
内对事物进行排序。但如果ConcreteSpecificArgumentBase
设计得很好,至少它会使调用concrete_specific
相当简单。
答案 8 :(得分:0)
奇怪的是,DynamicFactory的用户会收到Base类型,但是当它是ConcreteFoo时需要做特定的事情。
也许不应该使用工厂。
尝试查看其他依赖注入机制,例如自己创建ConcreteFoo,将ConcreteFoo类型指针传递给需要它的人,以及指向其他人的Base类型指针。
答案 9 :(得分:0)
上下文似乎假设用户将使用您的ConcreteType并知道它正在这样做。
在这种情况下,如果客户知道他们正在处理具体类型并且需要在该抽象级别工作,那么您的工厂中可能会有另一个返回ConcreteType *的方法。