Programming by interfaces - sticking points (C++)

Started by
3 comments, last by MGB 12 years, 6 months ago
Something that still periodically bugs me about programming by interfaces:

You have a nicely generalised interface for most of the funtionality, but a concrete implementation may need to consume objects specific to that implementation. You could maybe provide an object factory but it may still require different data in its construction.

One solution to remove the dependency would be a 'blobject' that just contains a number of key-value pairs, but this seems rather over-engineered.

Also if the interface takes a callback that provides data that's specific to the implementation, the client again has a dependency on the specific type involved.

It still seems appropriate to extract out a common interface, so I don't think I'm forcing a square peg into a round hole...

Does anyone have more insight into a better design?
Advertisement

You have a nicely generalised interface for most of the funtionality, but a concrete implementation may need to consume objects specific to that implementation.


Even if concrete objects are the end result of what's being used, they too need to be abstracted away to some common concept that they implement. Then your glue code deals with the nastiness of translating from interface to library.

Granted, that's a lot of headache which is where the whole tradeoff between time & flexibility arises.
Requiring a specific derived type indicates a design flaw. If I have Foo, then I have Foo, no matter how it's implemented. If for some reason I need FooBar, then Foo abstraction is wrong. Point of static typing is just that, to prevent and alert at incompatibilities.

Coding to interfaces in C++ is also not the perfect way to go about it. C++ already provides interface/implementation separation in form of header/implementation files, although such separation is compile-time. In addition, C++ lacks interfaces as first class members, they can be merely emulated. And to take most advantage of the language, runtime polymorphism should not be favored over overloading and compile-time resolution.

You could maybe provide an object factory but it may still require different data in its construction.[/quote]

Well, yes. If you need bricks to make one IBuilding and steel to make different kind of IBuilding, what would the common ground be? In Java, seeing FactoryAdapterProxySingletonFactoryFactory is not uncommon for precisely this reason.

A KV map is one solution, but it merely bypasses the type system. It would be same as if you provided a file in which parameters are stored. Type safety and other considerations go out the window, so does compile-time resolution.

'new' operator by itself is a factory. Built into the language. It takes a set of parameters and returns a new instance of an object and abstracts away actual allocation mechanism.

Also if the interface takes a callback that provides data that's specific to the implementation, the client again has a dependency on the specific type involved.[/quote]

A whole set of patterns was invented to deal with such scenario, but it again indicates incorrect level of abstraction. Assuming you're abstracting away Win32 and POSIX audio callback. Two completely different APIs, types, callbacks, .... The solution is not to mangle this into form. Instead, abstract it away entirely. Have a single subsystem, such as Audio, which accepts your application-specific callbacks. Implement the rest entirely inside the subsystem. In such particular case, there is even no need for run-time polymorphism, since there is one interface (.h) linked with platform-specific implementation (.cpp) during compilation.


If design at any time requires knowing concrete type, the abstraction is unsound.

Something that still periodically bugs me about programming by interfaces:

You have a nicely generalised interface for most of the funtionality, but a concrete implementation may need to consume objects specific to that implementation. You could maybe provide an object factory but it may still require different data in its construction.



Interface should talk to interface, not implementation.
Your this example is that your interface is talking to the implementation.

An implementation is implementing an interface, so it must conform the interface.
That's to say, the implementation may need different input data, but since itself conforms the interface, the input data should be required by the interface, not the implementation.
So even all kind of data implementations are different, the data interface should be the same for a certain interface that using it.

Thinking about Windows COM, or similar interface-based design, may help you a lot.

And I think it's better if we think, or even design, interface and implementation as totally different things. Implementation may have totally different physical (class hierarchy) or logical features. But that's OK as long as it conforms the interface.

However, over abstract interface may (or may not) impact the performance, it depends on your project.

https://www.kbasm.com -- My personal website

https://github.com/wqking/eventpp  eventpp -- C++ library for event dispatcher and callback list

https://github.com/cpgf/cpgf  cpgf library -- free C++ open source library for reflection, serialization, script binding, callbacks, and meta data for OpenGL Box2D, SFML and Irrlicht.

Hmm. Thanks for the answers people. Think I have more work to do on my OOP skills, but seems I've been mixing up interfaces and abstraction layers.


To quote Wikipedia, "[font=sans-serif][size=2]A good abstraction will generalize that which can be made abstract; while allowing specificity where the abstraction breaks down and its successful application requires customization to each unique requirement or problem."[/font]
[font=sans-serif][size=2]
[/font]
[font=sans-serif][size=2]Antheus: if I needed something called '[/font][color=#1C2837][size=2]FactoryAdapterProxySingletonFactoryFactory' I'd know I was doing something wrong ;)

This topic is closed to new replies.

Advertisement