Conflicting design goals

Started by
4 comments, last by Andrew Russell 18 years, 9 months ago
...are causing me a bit of frustration. First, let me tell you microsoft really sucker punched me today. This morning, VSExpress politly informed me that the beta period had expired and to please stop using the software. Naturally, it doesn't give me a choice. Intalling beta2 was needlessly complicated by their dire warnings of doom if you failed to unistall everything else first. And then, I had to recompile boost. Finally, after a solid four hours, I was ready to tackle the problem I had meant to spent maybe half an hour on today. I have two goals which have nicely trumped each other. I want a clean, simple interface, and I want to hide the implementation completly. It's best illustrated by example;


header:

class A
{
public:
  void foo();

  void bar();
};

void dostuff(A* a);

cpp:

void somefunc(A* a)
{
  a->bar();
}

void dostuff(A* a)
{
  somefunc(a);
}

As you can see, only the function hidden in the cpp file uses A::bar. The problem is, anyone else can use A::bar too. Furthermore, somefunc must not appear in the header file, so I can not declare it as a friend to A. A::foo is the only public interface that should be visible out client code. Additionally, the usage must be as simple as A a; dostuff(a);. Currently, I just have A::bar dangling out there for anyone to call. The only way I see around it is to have somefunc exposed instead, which is an equally unappealing situtation. Or to change the way the class is used, which is absolutly out of the question.
Advertisement
I'm not to fond of your second design goal in the first place (first design goal is awesome). The second goal is really the source of your problem. The only real way to achieve it is to go all-out with a PIMPL (and here), or pure-virtual, or pure-C interface. It's not really something that works if you try and do it all within the one implementation class. C++ simply isn't designed to do 100% private implementations (although it can be made to with one of the above methods).

My suggestion: code by the first design goal - it is a good one, and then (by the laws of Yag Ni) add your total-implementation-hiding "stuff" only once you have a real need for it (and in that case, do it properly and seperatly).
Take a look at the Bridge pattern -- that's what you are looking for. (PIMPL is one implementation). Like Andrew Russell, I recommend that you use it only if absolutely necessary. It will make your code much more complicated and cumbersome.

Generally, PIMPL is done like this:

A.h
class A{    class Impl;   // Forward declaration of implementation class    Impl * pImpl; // Pointer to implementation class public:    A();    ~A();     void dostuff();}; 

A.cpp
class A::Impl{public:    void dostuff();private:    void foo();    void bar();}; void A::Impl::dostuff(){    bar();} A::A() : pImpl( new A::Impl ){} A::~A(){    delete pImpl;} void A::dostuff(){    pImpl->dostuff();} 
John BoltonLocomotive Games (THQ)Current Project: Destroy All Humans (Wii). IN STORES NOW!
I've seen this before and I like it:

In "Foo.h":
class Foo{public:    // Factory Constructor    static Foo* Create (/* create params */);    // Interface Methods    virtual void foo () =0;};


In "FooImp.h":
class FooImp : public Foo{public:    virtual void foo ();};void dostuff ();


In "Foo.cpp":
#include "FooImp.h"Foo* Foo::Create (){    return new FooImp ();}


In "FooImp.cpp":
#include "FooImp.h"void FooImp::foo (){    dostuff ();}


Doing it like this addes a few extra source files too the project but I would generally use this layout in a place where it made sense to put several of these base/interface classes into a single file.

[edit - forgot to add "dostuff ()"]
Quote:Generally, PIMPL is done like this:


I'm well aware of what the pimple pattern is; but thank you. It's nice when people put in effort.

Quote:100% private implementations


Visible is fine. Anyone can just look at the source anyway. Unacessible is the real goal. If it's hidden behind something as simple as a private specification, anyone who wants to screw with it will have to go out of their way to do so. And if they screw it up then, it's their own fault.

Nuget5555; that create function and dynamic allocation really kill it.
Quote:Original post by Deyja
Visible is fine. Anyone can just look at the source anyway. Unacessible is the real goal. If it's hidden behind something as simple as a private specification, anyone who wants to screw with it will have to go out of their way to do so. And if they screw it up then, it's their own fault.

So, uhmn, why don't you just make the functions private, possibly static, members (including dostuff and somefunc)?

This topic is closed to new replies.

Advertisement