views:

77

answers:

3

A big part of my C++ application uses classes to describe the data model, e.g. something like ClassType (which actually emulates reflection in plain C++).

I want to add a new module to my application and it needs to make use of these ClassType's, but I prefer not to introduce dependencies from my new module on ClassType.

So far I have the following alternatives:

  • Not making it independent and introduce a dependency on ClassType, with the risk of creating more 'spaghetti'-dependencies in my application (this is my least-preferred solution)
  • Introduce a new class, e.g. IType, and letting my module only depend on IType. ClassType should then inherit from IType.
  • Use strings as identification method, and forcing the users of the new module to convert the ClassType to a string or vice versa where needed.
  • Use GUID's (or even simple integers) as identification, also requiring conversions between GUID's and ClassType's

How far should you try to go when decoupling modules in an application?

  • just introduce an interface and let all the other modules rely on the interface? (like in IType describe above)
  • even decouple it further by using other identifications like strings or GUID's?

I afraid that by decoupling it too far, the code becomes more unstable and more difficult to debug. I've seen one such example in Qt: signals and slots are linked using strings and if you make a typing mistake, the functionality doesn't work, but it still compiles.

How far should you keep your modules decoupled?

+2  A: 

99% of the time, if your design is based on reflection, then you have major issues with the design.

Generally speaking, something like

if (x is myclass)
elseif (x is anotherclass)
else

is a poor design because it neglects polymorphism. If you're doing this, then the item x is in violation of the Liskov Substitution Principle.

Also, given that C++ already has RTTI, I don't see why you'd reinvent the wheel. That's what typeof and dynamic_cast are for.

Billy ONeal
Wholly agreed. Reinventing RTTI nearly always betrays a fundamental lack of understanding of C++ OOP.
Reinderien
I have good reasons to use a reflection-based design because my application is somewhat special. Using a configuration file, the users may extend the datamodel with additional types and properties. This is impossible to achieve without a reflection-based design, and also C++'s RTTI doesn't help here. If it wouldn't be for this flexibility, I would completely agree with you. Unfortunately in this case, I can't.
Patrick
@Patrick: If the types you are working with are dynamic, you won't be able to implement your type system in terms of C++ types anyway. Youd be better off with a type `UserType` which can contain sub `UserType`s, which the user can then extend.
Billy ONeal
+1  A: 

I'll steer away from thinkng about your reflection, and just look at the dependency ideas.

Decouple what it's reasonable to decouple. Coupling implies that if one thing changes so must another. So your NewCode is using ClassType, if some aspects of it change then yuou surely must change NewCode - it can't be completely decoupled. Which of the following do you want to decouple from?

  1. Semantics, what ClassType does.
  2. Interface, how you call it.
  3. Implementation, how it's implemented.

To my eyes the first two are reasonable coupling. But surely an implementation change should not require NewCode to change. So code to Interfaces. We try to keep Interfaces fixed, we tend to extend them rather than change them, keeping them back-compatible if at all possible. Sometimes we use name/value pairs to try to make the interface extensible, and then hit the typo kind of errors you allude to. It's a trade-off between flexibility and "type-safety".

djna
A: 

It's a philosophical question; it depends on the type of module, and the trade-offs. I think I have personally done all of them at various times, except for the GUID to type mapping, which doesn't have any advantages over the string to type mapping in my opinion, and at least strings are readable.

I would say you need to look at what level of decoupling is required for the particular module, given the expected external usage and code organization, and go from there. You've hit all the conceptual methods as far as I know, and they are each useful in particular situations.

That's my opinion, anyway.

Nick