Jump to content
  • Advertisement
Sign in to follow this  
okonomiyaki

Design question - similar to factory pattern, but not abstract

This topic is 4614 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I want a flexible way of adding components to the system I'm working on. Compoments such as renderer, sound, network, etc. I've refactored several different times until I found something I liked, but there's one problem. I defined an interface AXSystemComponent, and created a couple other specialized interfaces that inherit from it. You can either just inherit AXSystemComponent or one of the specialized ones to add a component to the system (well, you also have to register it with the base system). For each system component, I want the ability to quickly "get" it. I don't want to enforce the singleton pattern. Either way, through singletons or a method in the base system that gets the component, how can I return a pointer that is of the type of the component, NOT simply a pointer to the interface AXSystemComponent? For example, I want to say _Renderer = AXBaseSystem::GetComponent("renderer"); and not have to worry about casting it myself. This makes it easier to work with and also the ability to go AXBaseSystem::GetComponent("sound")->Stop() if I wanted to do something quickly. I kind of want something that looks like the singleton pattern, ie AXRenderer::Instance()->Render...(). What's the best way to make these things easily accessible? Again, I don't want to enforce the singleton pattern. I could use multiple inheritance, like class AXRenderer : AXSystemComponent, AXSingleton<AXRenderer> {} but I would like to access the components through the base system so I don't have to enforce singletons. And I don't like multiple inheritance, it's usually a sign of bad design. Should I just deal with casting it myself, since I will almost always store the pointer and use that internally anyway? What kind of design should I use?

Share this post


Link to post
Share on other sites
Advertisement
Guest Anonymous Poster
I work at a leading game company and have seen one game project try this approach (i.e., a generic framework for "major game components"). It doesn't work so well in practice. There's really very little in common between your renderer, physics, audio, controller, etc. components that's worth abstracting. You just end up making your code harder to use and debug.

The most effective approach to "components" that I've seen is to simply use global functions wrapped in component-specific namespaces that either handle major functionality, e.g., void audio::Update( const GameTime& ), or return pointers to global instances of (possibly pure virtual) subcomponents, e.g., renderer::IDisplay* renderer::GetDisplay(). Easy to use and the component interfaces can be free of all implementation details.

Share this post


Link to post
Share on other sites
Since your components' types are determined at runtime, dynamic_cast is a way to both cast a pointer and make sure that a component is the right type:
    AXAudio* pAudio = dynamic_cast< AXAudio* >( AXBaseSystem::GetComponent("sound") );
assert( pAudio );
pAudio->Stop();

Share this post


Link to post
Share on other sites
Quote:

how can I return a pointer that is of the type of the component, NOT simply a pointer to the interface AXSystemComponent?


[IMO!]

You don't.

If you don't want the base system to know about the component [which is rather assumed by the "flexible way of adding components" requirement], then the base system can't know about the component type; period.

Personally, I would expand the interface for the differing component types you want [renderer, sound, network, etc.], and supply those from the base.

Something akin to this. [sorry, been away from C++ for a few weeks]


struct Sound_Component_Interface{
void Stop()=0;
// ...
};

class Base_System{
static map<string, Sound_Component_Interface *> SoundMap;
public:
template <typename S>
static void Register_Sound(string name){
map[name]=new S();
}
static Sound_Component_Interface *Fetch_Sound(string name){ // find and return;}
};

// elsewhere
class Sound_Implimentation:public Sound_Component_Interface{
public:
void Stop(){
// stop sounds...
}
};

Base_System::Register_Sound_Component<Sound_Implimentation>("Sound");

// Use later:
Base_System::Fetch_Sound("Sound")->Stop();





Though in my experience, such registries are overkill for major components. Your base requires a set of components, just explicitly enumerate them...

Share this post


Link to post
Share on other sites
My framework provides both an engine and an editor. Moreover, as a real framework, it relies heavily on plug-ins. It needs to provide a way to let the user choose the one or other thing (mainly in but not restricted to the editor mode). So "extensions" as my most general abstraction provide some very general and human readable informations about just extensions. But each particular main kind of extension (say Graphics, Sound, FileFormat, Project::Type, ...) has its own registry and hence own database/finders/enumerations of instances.

To avoid to program too much, I make heavily use of templates, of course. Registry itself is a template class, and also a magic handle class (for more or less automatic registration/deregistration of extensions on creation/destruction of plug-ins) is a template.

Nevertheless, a problem like yours is left e.g. for FileFormat. I distinguish the different sub-types of FileFormat by MIME types, but also here I have an interface class for each main MIME type I support. Each of that classes provide another static enumeration routine, simply using dynamic_cast.

Share this post


Link to post
Share on other sites
Quote:
Original post by Anonymous Poster
There's really very little in common between your renderer, physics, audio, controller, etc. components that's worth abstracting. You just end up making your code harder to use and debug.


There isn't much in common, but there is some in common. I don't particularly like the idea of wrapping global functions in namespaces; what if I wanted two different renderers, or two different audio devices, and the appropriate one is determined at runtime? I like using classes (interfaces), and the common thing between all of these interfaces is that the instance needs to be accessed from anywhere. There's hardly anything defined in the AXSystemComponent interface, but how it's globally registered, etc., is nice to be done in this one interface, and I only have to change it once if I want to change how it works.

Quote:

Since your components' types are determined at runtime, dynamic_cast is a way to both cast a pointer and make sure that a component is the right type


True, I guess my point was how do I avoid doing this explicitly every time I get a component. I didn't know if there was a special template trick that could return different types, and use dynamic_cast internally itself.

Quote:

You don't.

...

Though in my experience, such registries are overkill for major components. Your base requires a set of components, just explicitly enumerate them...


Yeah, I guess I shouldn't worry about it too much, but I would like to make it as easy as possible to add/remove system components. And about the base system not knowing about the component type, you are right. Like I said in the paragraph above though, I didn't know if there was a template trick to return different types. But you are right, unless I do something like GetComponent<AXSound>("sound") there's absolutely no way for it to return different types.

Quote:

My framework provides both an engine and an editor. Moreover, as a real framework, it relies heavily on plug-ins. It needs to provide a way to let the user choose the one or other thing (mainly in but not restricted to the editor mode). So "extensions" as my most general abstraction provide some very general and human readable informations about just extensions. But each particular main kind of extension (say Graphics, Sound, FileFormat, Project::Type, ...) has its own registry and hence own database/finders/enumerations of instances.


interesting. I'm not worried about being too extensible, but we'll see what I come up with!

Thanks for all your remarks.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!