469,589 Members | 2,251 Online
Bytes | Developer Community
New Post

Home Posts Topics Members FAQ

Post your question to a community of 469,589 developers. It's quick & easy.

Making all members of pimpl classes public

Hi,

I'm creating a library where several classes are intertwined rather tightly.
I'm thinking of making them all use pimpls, so that these circular
dependancies can be avoided easily, and I'm thinking of making all these pimpl
class declarations public. Reasoning is that since only the code within the
..cc file will need to ever access them, why protect them in ways that would
make access to them more difficult and obfuscated?

What are your thoughts on this?

--
To reply, take of all ZIGs !!

Alternative email address: em******@asfandyarZIG.cjbZIG.netZIG
Mar 1 '06 #1
34 3352
Asfand Yar Qazi wrote:
I'm creating a library where several classes are intertwined rather
tightly. I'm thinking of making them all use pimpls, so that these
circular dependancies can be avoided easily
Use Pimpls in an emergency, as a retrofit to a bad design.

If you are planning a design, write lots of unit tests, and read the
Refactoring books to help keep the design clean as it grows.

Then research the Dependency Inversion Principle, and use that to put in
just a few abstract interfaces. The necessities of your simple test cases
will force many of these interfaces to exist.
and I'm thinking of making all these pimpl class declarations public.
Reasoning is that since only the code within the .cc file will need to
ever access them, why protect them in ways that would make access to them
more difficult and obfuscated?


The rule is not "make all data members private". The rule is "don't make
primitive things globally public".

You should prefer to make everything as private as possible. Things inside
implementation files (".cc") are already private, so making them private: is
irrelevant.

A .cc file that mostly deals with its own contents, and has very few
dependencies out to other .cc files, is "coherent". The more coherent your
..cc file, the more useful and mobile it is to other .cc files. You can
change it easily without affecting other .cc files.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 1 '06 #2
Phlip wrote:
Asfand Yar Qazi wrote:

I'm creating a library where several classes are intertwined rather
tightly. I'm thinking of making them all use pimpls, so that these
circular dependancies can be avoided easily

Use Pimpls in an emergency, as a retrofit to a bad design.

If you are planning a design, write lots of unit tests, and read the
Refactoring books to help keep the design clean as it grows.

Then research the Dependency Inversion Principle, and use that to put in
just a few abstract interfaces. The necessities of your simple test cases
will force many of these interfaces to exist.


I don't think I agree with that - the pimpl idiom easily separates interface
from implementation. It allows me to place just the interface in the .hh
file, without reference to a single data member. It easily allows
copy-on-write behaviour, and allows the interface file to remain unchanged
even if things underneath change radically.

Or is that what you meant?

--
To reply, take of all ZIGs !!

Alternative email address: em******@asfandyarZIG.cjbZIG.netZIG
Mar 2 '06 #3
Asfand Yar Qazi wrote:

I don't think I agree with that - the pimpl idiom easily separates
interface from implementation.


I guess you don't need pimpl to separate interface from implementation,
of course, if you're not making template classes.

Mar 2 '06 #4
Asfand Yar Qazi wrote:
Phlip wrote:
Asfand Yar Qazi wrote:

I'm creating a library where several classes are intertwined rather
tightly. I'm thinking of making them all use pimpls, so that these
circular dependancies can be avoided easily

Use Pimpls in an emergency, as a retrofit to a bad design.

If you are planning a design, write lots of unit tests, and read the
Refactoring books to help keep the design clean as it grows.

Then research the Dependency Inversion Principle, and use that to put
in just a few abstract interfaces. The necessities of your simple test
cases will force many of these interfaces to exist.

Pimpl is one of the standard patterns in GoF, although they call it the
"Bridge" pattern.

Mar 2 '06 #5

Phlip wrote:
Asfand Yar Qazi wrote: Use Pimpls in an emergency, as a retrofit to a bad design.
This seems naive to me. Pimpls used in conjunction with the Bridge
pattern are often required for good "dynamic" design. It is certainly
not only for emergency, or as result of refactoring.
If you are planning a design, write lots of unit tests, and read the
Refactoring books to help keep the design clean as it grows.
Specify your interface...
Then research the Dependency Inversion Principle, and use that to put in
just a few abstract interfaces. The necessities of your simple test cases
will force many of these interfaces to exist.
Hmmm, you are still required to construct your abstract classes
somewhere. I prefer making my clients dependent on non-abstract
classes. My pimpls are the abstract classes. Therefore:

Interface <non-abstract> has a pimpl <abstract>. The Pimpl (or bridge)
is then provided by a factory. The client has not need to provide, or
specify the concrete class.
You should prefer to make everything as private as possible. Things inside
implementation files (".cc") are already private, so making them private: is
irrelevant.


Yes, half true. What about the library implementor. Sometimes common
behaviour (not data) can be captured in the base class (template method
pattern). private methods are then called from the public method. On
the other hand, if this is not the case, and pimpl is implemented
correctly (i.e. it is abstract), then it only has methods callable from
the interface, which means there is not need to make anything private.

The other reason for using pimpl (compiler firewall) often has the
entire Impl class <then non-abstract> in the cpp file. In this case it
is fine to make everything public, as the Impl is often a privately
nested class, which makes it inaccessible to other classes anyway.

Regards,

Werner

Mar 2 '06 #6
In message <Ao******************************@comcast.com>, Aleksander
Beluga <ch*****@tigris.org> writes
Asfand Yar Qazi wrote:
I don't think I agree with that - the pimpl idiom easily separates
interface from implementation.


I guess you don't need pimpl to separate interface from implementation,
of course, if you're not making template classes.

Sometimes you do. Private declarations are conceptually part of the
implementation, not the interface, but the language syntax requires them
to appear in the interface definition. This introduces a coupling that's
not present in the ideal conceptual model which completely separates
interface and implementation, and this coupling causes recompilation of
everything that uses the interface whenever the private parts change. A
major practical reason for using the pimpl idiom is to reduce the
private part of the public interface to a single pointer, removing that
syntax-imposed coupling and reducing build times.

--
Richard Herring
Mar 2 '06 #7

Richard Herring wrote:
In message <Ao******************************@comcast.com>, Aleksander
Beluga <ch*****@tigris.org> writes
Asfand Yar Qazi wrote:
I don't think I agree with that - the pimpl idiom easily separates
interface from implementation.


I guess you don't need pimpl to separate interface from implementation,
of course, if you're not making template classes.

Sometimes you do. Private declarations are conceptually part of the
implementation, not the interface, but the language syntax requires them
to appear in the interface definition. This introduces a coupling that's
not present in the ideal conceptual model which completely separates
interface and implementation, and this coupling causes recompilation of
everything that uses the interface whenever the private parts change. A
major practical reason for using the pimpl idiom is to reduce the
private part of the public interface to a single pointer, removing that
syntax-imposed coupling and reducing build times.


It's not only when the private part of the class changes that coupling
causes recompilation. If the private part of my_class needs something
defined in "another_header.h" then, without pimpl, "my_class.h" must
include "another_header.h" or it won't compile (unless a forward
declaration is sufficient, which isn't always the case).

Anyone using my_class will obviously include "my_class.h". But then
they will also be indirectly including "another_header.h" even though
that header was only of interest to the private part of my_class. If
"another_header.h" changes, *all* users of my_class will be forced to
recompile even though they don't care what "another_header.h" is. And
even if the change doesn't even affect the private part of my_class.

Use a pimpl in my_class and users of the class are no longer affected
by any change to the "another_header.h".

Gavin Deane

Mar 2 '06 #8
Asfand Yar Qazi wrote:
I don't think I agree with that - the pimpl idiom easily separates
interface
from implementation. It allows me to place just the interface in the .hh
file, without reference to a single data member. It easily allows
copy-on-write behaviour, and allows the interface file to remain unchanged
even if things underneath change radically.

Or is that what you meant?


Pimpl solves an emergency situation. If you have Foo.h, and it includes
many .h files, and it declares a huge class Foo, and if everyone uses Foo,
then each time you change any of those .h files, everything must recompile,
even things that do not directly use the changed things in those .h files.

The fastest and easiest fix replaces each .h file with a forward declare of
the class it defines, and replaces all of Foo's data members with a single,
bald pointer to FooImpl. Only define FooImpl in Foo.cpp, and forward every
method to it.

This is a ruthlessly effective fix because you needn't redesign Foo, or any
of its users, or any of its servants, to push in the fix. You only need a
few copies and pastes.

Now, in terms of the emergency, ask why Foo.h got so burdensome. Why do so
many things use it? Do they do too much? Why does it use so many servants?
Do they do too little?

If a class is well-designed, it will achieve many of Pimpl's benefits,
possibly including a bald pointer to an implementation object in a .cc
file. C++ works because the techniques that logically decouple modules
parallel the techniques that physically decouple them.

Ideally, if Foo is very important, its clients should access it through an
abstract interface; call this FooInf (but real code should think of a
better name). So now the clients cannot create a Foo because they are not
aware of it. If they want one, they must get it from a Foo Factory of some
kinds. This technique, "Construction Encapsulation", fully insulates Foo
clients from the concrete Foo, and its data members, just as rigorously as
the FooImpl would have. But the entire design is now more flexible.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 2 '06 #9
> So now the clients cannot create a Foo because they are not
aware of it. If they want one, they must get it from a Foo Factory of some
kinds. This technique, "Construction Encapsulation", fully insulates Foo
clients from the concrete Foo, and its data members, just as rigorously as
the FooImpl would have. But the entire design is now more flexible.
I mostly agree with you. I have mention this though:

I have seen applications where factories became the most used
(included) classes in an application. Every time new products came
along, the factory interface changed and everything needed to be
recompiled - especially because the client was made dependent on the
factory interface. Therefore, if you are implementing a library, hide
the factory interface from the client. This can be done very
conveniently by making the client dependent on a non-abstract
interfaces that makes use of the pimpl (being abstract), who's concrete
instance is provided by an opaque (not-client-visible) factory.

Regards,

Werner

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!


Mar 5 '06 #10
werasm wrote:
So now the clients cannot create a Foo because they are not
aware of it. If they want one, they must get it from a Foo Factory of some
kinds. This technique, "Construction Encapsulation", fully insulates Foo
clients from the concrete Foo, and its data members, just as rigorously as
the FooImpl would have. But the entire design is now more flexible.


I mostly agree with you. I have mention this though:

I have seen applications where factories became the most used
(included) classes in an application. Every time new products came
along, the factory interface changed and everything needed to be
recompiled - especially because the client was made dependent on the
factory interface. Therefore, if you are implementing a library, hide
the factory interface from the client. This can be done very
conveniently by making the client dependent on a non-abstract
interfaces that makes use of the pimpl (being abstract), who's concrete
instance is provided by an opaque (not-client-visible) factory.


The whole point of a factory is to insulate users from "new products".
Sounds like a bad implementation of a factory to me.

Why does the interface of a factory have to change for each new product?
Why does the implementation even need to change? The whole point is
to push the management of ids and creating functions into the new
products, and away from centralised management.

Ben Pope
--
I'm not just a number. To many, I'm known as a string...
Mar 5 '06 #11
werasm wrote:
I have seen applications where factories became the most used
(included) classes in an application.
For every pattern in the book /Design Patterns/, there is a big legacy app
out there written by those who didn't "get" some pattern, and abused it all
over the place.

There are probably more than three such abuse examples per pattern, these
days. ;-)
Every time new products came
along, the factory interface changed and everything needed to be
recompiled - especially because the client was made dependent on the
factory interface. Therefore, if you are implementing a library, hide
the factory interface from the client. This can be done very
conveniently by making the client dependent on a non-abstract
interfaces that makes use of the pimpl (being abstract), who's concrete
instance is provided by an opaque (not-client-visible) factory.


Right - you describe the emergency situation calling for a Pimpl fix.

The design violated "Construction Encapsulation". (Lots of unit tests would
have helped fix that, because test cases often construct fake objects and
pass them to the tested objects, who should not create their own objects.)

In a typical good design, data enters from the outside world as numbers, and
then factory-like things convert these to objects. Inside this "boundary
layer", objects just mix it up, constructing very few new objects, and
passing references around to each others' interfaces.

Ordinarily, too many switch statements are a design smell. In such
well-factored code, the joke is "too many 'if' statements are a design
smell".

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 5 '06 #12
> The whole point of a factory is to insulate users from "new products".
Sounds like a bad implementation of a factory to me.


Ok, this may be a naive implementation, but more or less iaw. GOF. I
haven't got the book here, so bare with me (I'll be extra brief):

struct Factory
{
X* makeX() const;
Y* makeY() const;
Z* makeZ() const;
};

Ok, X, Y and Z is of course ABC's, right? therefore adding a new
implementation is fine - this won't cause the client to recompile. But
what about adding a new ABC?

struct Factory
{
W* makeW const;
X* makeX() const;
Y* makeY() const;
Z* makeZ() const;
};

Now interface of this factory has changed. All clients of Factory
requires recompilation, despite them not making use of W. How do you
overcome this? I'd love to see - always nice to be less naive
eventually...

Regards,

Werner

Mar 5 '06 #13
The design violated "Construction Encapsulation".
Not exactly sure what you mean here. Give me an example of a factory
not violating "Construction Encapsulation".
(Lots of unit tests would
have helped fix that, because test cases often construct fake objects and
pass them to the tested objects, who should not create their own objects.)
I may have a vague idea, but I'm not sure I see your point here too.
Small example please.
In a typical good design, data enters from the outside world as numbers, and
then factory-like things convert these to objects.
Factory like things? Does this mean the inner objects use (are exposed
to the interface) of these factory like things. How do these factory
like things look. Are they very concise - only responsible for creating
one type - ever, so that their interface can never change? If yes,
good. If no, problem.
Ordinarily, too many switch statements are a design smell. In such
well-factored code, the joke is "too many 'if' statements are a design
smell".


No ifs and no switches sound good, yes. Thats what I prefer.

Werner

Mar 5 '06 #14
werasm wrote:
The design violated "Construction Encapsulation".
Not exactly sure what you mean here. Give me an example of a factory
not violating "Construction Encapsulation".


This is the answer to your other post:
struct Factory
{
X* makeX() const;
Y* makeY() const;
Z* makeZ() const;
};
That's not a factory, because calling aFactory.makeX() is the moral
equivalent of calling new X(). It's just as coupled.

This is a factory:

struct Factory
{
ABC* make(int type) const;
};

It essentially converts a type to a base class pointer. The calling code
never needs to know the class, only the type code.

Now this is unencapsulated construction:

void Foo()
{
ABC*p = new X;
...
}

This is partly encapsulated construction:

void Foo(int what)
{
ABC*p = someFactory.make(what);
...
}

And this is fully encapsulated construction:

void Foo(ABC & thing)
{
...
}

That's what it means - the clients of objects are not responsible for
creating their servant objects. They are only responsible for using them,
and Something Else creates them.
(Lots of unit tests would
have helped fix that, because test cases often construct fake objects and
pass them to the tested objects, who should not create their own
objects.)


I may have a vague idea, but I'm not sure I see your point here too.
Small example please.


Suppose the code has three derived classes of ABC. Your X, Y, and Z.

Now suppose I write a unit test that creates a fourth derivative - MockABC.
It overrides ABC methods and puts test-ready things in them.

Which of my Foos is easiest to test? Which one is the easiest to pass a
MockABC into?

Now suppose I design my program by writing such tests. The easier classes
are to test, the more decoupled they become.

A test case:

TEST_(TestSuite, doFooThing)
{
MockABC aMock;
Foo(aMock);
CHECK_EQUAL(42, aMock.fooCalledMeWith());
}
Factory like things? Does this mean the inner objects use (are exposed
to the interface) of these factory like things. How do these factory
like things look. Are they very concise - only responsible for creating
one type - ever, so that their interface can never change? If yes,
good. If no, problem.


They look like simple factories. But you don't happen to call them that, or
"implement the Class Factory Pattern from the book Design Patterns". They
might look like the prototype pattern.

Consider this method:

void Foo(ABC & thing);

It has no Factory inside it. Something created the right Thing for it. It
doesn't care what.

Factory is a nice pattern, but you could conceivably write a program that
never couples its constructors to its calling code, yet has no recognizable
Factory in it.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 5 '06 #15
werasm wrote:
The whole point of a factory is to insulate users from "new products".
Sounds like a bad implementation of a factory to me.


Ok, this may be a naive implementation, but more or less iaw. GOF. I
haven't got the book here, so bare with me (I'll be extra brief):

struct Factory
{
X* makeX() const;
Y* makeY() const;
Z* makeZ() const;
};

Ok, X, Y and Z is of course ABC's, right? therefore adding a new
implementation is fine - this won't cause the client to recompile. But
what about adding a new ABC?

struct Factory
{
W* makeW const;
X* makeX() const;
Y* makeY() const;
Z* makeZ() const;
};

Now interface of this factory has changed. All clients of Factory
requires recompilation, despite them not making use of W. How do you
overcome this? I'd love to see - always nice to be less naive
eventually...


Yep, I see your problem. You still need need to know your type at
runtime in order to instantiate it. You need to separate your type
information from your creation.

I don't have time to check this:
#include <string>
#include <map>

struct base{
virtual ~base() {}
};

struct derived : base {};

base* createBase() {
return new base;
}
base* createDerived() {
return new derived;
}

typedef base* (*creator)();

class Factory {
public:
void Register(std::string id, creator func) {
factory_[id] = func;
}
base* create(std::string id) {
return factory_[id]();
}
private:
std::map<std::string, creator> factory_;
};

int main() {
Factory factory;
factory.Register(std::string("base"), &createBase);
factory.Register(std::string("derived"), &createBase);

base* b = factory.create(std::string("base"));
return 0;
}
If you don't get it, I'm sure others can explain.

This is also covered in Modern C++ design (infinitely more elegant than
my example!)

Ben Pope
--
I'm not just a number. To many, I'm known as a string...
Mar 5 '06 #16
red floyd wrote:
Asfand Yar Qazi wrote:
Phlip wrote:
Then research the Dependency Inversion Principle, and use that to put
in just a few abstract interfaces. The necessities of your simple
test cases will force many of these interfaces to exist.

Pimpl is one of the standard patterns in GoF, although they call it the
"Bridge" pattern.


I believe that pImpl idiom is only C++ solution while DPs are usually
cross-language. Also, I believe that pImpl is a specific case of another
DP called Facade.
Mar 5 '06 #17
Aleksander Beluga wrote:
I believe that pImpl idiom is only C++ solution while DPs are usually
cross-language. Also, I believe that pImpl is a specific case of another
DP called Facade.


DPs are logical design. Pimpl is physical design. That means you could take
the pimpl out, and all the client classes would behave the same. Except
they'd take longer to compile.

A refactor to a better design, such as via the Dependency Inversion
Principle, _should_ simplify all the clients.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 6 '06 #18
* Phlip:
Aleksander Beluga wrote:
I believe that pImpl idiom is only C++ solution while DPs are usually
cross-language. Also, I believe that pImpl is a specific case of another
DP called Facade.
DPs are logical design. Pimpl is physical design. That means you could take
the pimpl out, and all the client classes would behave the same. Except
they'd take longer to compile.


Nope.

pimpl removes dependencies on header files, some of which are usually
ill-behaved, and anyway, which introduce definitions not present with pimpl.

A refactor to a better design, such as via the Dependency Inversion
Principle, _should_ simplify all the clients.


On the contrary. ;-)
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Mar 6 '06 #19
Alf P. Steinbach wrote:
DPs are logical design. Pimpl is physical design. That means you could
take
the pimpl out, and all the client classes would behave the same. Except
they'd take longer to compile.


Nope.

pimpl removes dependencies on header files, some of which are usually
ill-behaved, and anyway, which introduce definitions not present with
pimpl.


DPs are logical design. Pimpl is physical design. That means you could take
the pimpl out, and all the client classes would have exactly the same design
and contents. Except they'd take longer to compile, and would be exposed to
all the stray identifiers in the excess headers, that could change their own
identifier's meanings in subtle ways, if your C++ codebase was already
circling the drain anyway.
A refactor to a better design, such as via the Dependency Inversion
Principle, _should_ simplify all the clients.


On the contrary. ;-)


Else why put it in?

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 6 '06 #20
* Phlip:
Alf P. Steinbach wrote:
DPs are logical design. Pimpl is physical design. That means you could
take
the pimpl out, and all the client classes would behave the same. Except
they'd take longer to compile. Nope.

pimpl removes dependencies on header files, some of which are usually
ill-behaved, and anyway, which introduce definitions not present with
pimpl.


DPs are logical design. Pimpl is physical design. That means you could take
the pimpl out, and all the client classes would have exactly the same design
and contents.


Nope.

Except they'd take longer to compile, and would be exposed to
all the stray identifiers in the excess headers, that could change their own
identifier's meanings in subtle ways,
And not so subtle ways, like, not compiling at all.

if your C++ codebase was already circling the drain anyway.


Derogatory language isn't conducive to civilized discussion, Phlip.

Anyway, you've missed the point.

A refactor to a better design, such as via the Dependency Inversion
Principle, _should_ simplify all the clients.

On the contrary. ;-)


Else why put it in?


See above. Or google. ;-) Or, forget Ruby for a while, and try to
wrap some old C graphics library in C++, for example.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Mar 6 '06 #21
Alf P. Steinbach wrote:
Nope.


Please pick none or more:

[ ] Pimples are good and should be designed-for
[ ] the Introduce Pimple Refactor changes client classes
[ ] the Introduce Pimple Refactor introduces opportunities for
clients to simplify

Please back up your choice, and please assume the extra .h files that Pimpl
hides were themselves well-factored and insulated from the clients that
didn't use them directly and wants to become ignorant of them.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 6 '06 #22
* Phlip:
Alf P. Steinbach wrote:
Nope.


Please pick none or more:

[ ] Pimples are good and should be designed-for
[ ] the Introduce Pimple Refactor changes client classes
[ ] the Introduce Pimple Refactor introduces opportunities for
clients to simplify

Please back up your choice, and please assume the extra .h files that Pimpl
hides were themselves well-factored and insulated from the clients that
didn't use them directly and wants to become ignorant of them.


I don't see any point; this is rubbish.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Mar 6 '06 #23
Aleksander Beluga wrote:
...I believe that pImpl is a specific case of another DP called Facade.


"Intent: Provide a unified interface to a set of interfaces in a subsystem.
Facade defines a higher-level interface that makes the subsystem easier to
use."

They are indeed very close. But Pimpl provides only one interface for only
one class. Not a set of interfaces and not a subsystem. And Pimpl does not
make the subsystem easier to use, because all the clients call all the same
methods on the wrapper class; not reduced or simplified methods.

Yes yes yes it makes it easier to compile and easier to insulate. /DP/ uses
"easier" in the logical design sense, not the physical design sense.

If, however, you then procede to simplify the methods in your wrapper class,
or pull more implementation classes under its umbrella, then you indeed have
Facade.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 6 '06 #24
Alf P. Steinbach wrote:
I don't see any point; this is rubbish.


And "Derogatory language isn't conducive to civilized discussion". ;-)

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 6 '06 #25
* Phlip:
Alf P. Steinbach wrote:
I don't see any point; this is rubbish.


And "Derogatory language isn't conducive to civilized discussion". ;-)


Right.

There's a difference between (1) trying to associate something with
something bad, and (2) saying right out that rubbish is rubbish. The
first is derogatory (see the nearest dictionary definition); it appeals
to emotion, which is a well-known fallacy. The second is /opposite/.

--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
Mar 6 '06 #26

Yep, I see your problem. You still need need to know your type at
runtime in order to instantiate it. You need to separate your type
information from your creation.
Yes.

}
base* create(std::string id) {
return factory_[id]();
}
What about totally unrelated types. Types who do not derive from base?
Yes, I noticed Alexandrescu's solution addresses this with scattered
hierarchies. Yours don't. Derived's type is derived from base, which is
exactly what MakeX would give you in:

struct X{...};
struct XD : X{...};

struct Factory
{
X* makeX() const;
}
struct DFactory: Factory
{
XD* makeX() const;
}

Here XD represents your derived. I see no added advantange when
weighing your example against the original. The other thing you did not
address is constructors with many arguments. I know prototyping is one
way to address this. Unfortunately it is not good enough as not all
aggregates of the same type requires the same prototype.
int main() {
Factory factory;
factory.Register(std::string("base"), &createBase);
factory.Register(std::string("derived"), &createBase);

base* b = factory.create(std::string("base"));
return 0;
}


Less than ideal example...

How about:

Client::Client( const Factory& f)
{
//I'm still exposed to the interface of the entire factory... no
gains!
//What if I were required to make a type not derived from base?
}

What I'm getting at, is that each factories interface must be
ultra-concise (narrow). It should only represent one type. Andrei does
achieve this, yes - by means of the leaves of the scattered hierarchy.
The Clients can now state:

Client::Client( const AbstractFactoryUnit<MyServer>& f)
{
//Now I'm only exposed to the interface of the generic factory that
never changes,
// as well as that of MyServer, which is ok as I'll be using it. Of
course, if I'm only passing
// it along this is not necessary (Forward declare is sufficient).
}

This does not entirely solve the many arguments to a constructor
problem. I have scanned chapter 9 in Modern CPP briefly, but did not
see this being addressed (but I may have been to brief). I read the
part about using prototype. Yes, one only has one prototype per type,
and that prototype may not be the one that the client is requiring.
Sometimes the client may have data that may influence the prototype
(i.e. the prototype is constructed differently due to requirements by
client), right? This is rare though, and the fact that the interface of
the factory is narrowed down to one type helps allot. Agreed.

Now, my point is - making the client dependent on a non-abstract class
(and using an abstract pimpl as bridge), may solve some problems not
solved by Factories and Prototypes (like providing arguments to
constructors). Other solutions are what I call <delayed construction>
idiom. This is similar, actually, to the solution in MCPP design.
Simply:
template <class T>
struct CreationAbstractor
{
virtual T* create() const = 0;
}

Client::Client( const CreationAbstractor<MyType>& myTypeCreator )
: myType( myTypeCreator.create() )
{
}

Now the Client can dicate quite easily what type he requires. Whoever's
responsible for instantiating Client, provides the necessary
construction parameters:

struct MyTypeAbstractor : public CreationAbstractor<MyType>
{
MyTypeAbstractor( int x, int y, int z);
virtual MyType* create() const{ return new DerivedFromMyType( x,y,z
); }

private:
int x_;
int y_;
int z_;
};

This does assume that the one creating client, has enough info to
provide the construction parameters. Note that the one creating client
does not need to be exposed to the interface of MyType, though.

Kind regards,

Werner

Mar 6 '06 #27
werasm wrote:
What about totally unrelated types. Types who do not derive from base?


If they don't, then clients of the factory cannot use them interchangably,
so abstracting their construction has no point.

Look up "Liskov Substitution Principle".

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 6 '06 #28

Phlip wrote:
werasm wrote:
What about totally unrelated types. Types who do not derive from base?
If they don't, then clients of the factory cannot use them interchangably,
so abstracting their construction has no point.

Look up "Liskov Substitution Principle".


LSP - I have looked this up years ago (Robert Martin -
objectmentor.com). Why not just give me the URL :-).

And I've also read the other object mentor related things. You're
missing the point. AbstractFactory according to GOF is used for a whole
range of products. MCPP Design is iaw. this. The factory caters for
products with unrelated bases - read it. The range of products don't
require a common base - actually, I think it's detrimental to have a
common base.

My point is, that if one uses concrete interfaces, factories aren't
required (at least not by the client). The alternative has pros and
cons, but consider it as alternative to what your propose, and realise
that pimpl has more uses than you've advocated. OOP Library components
can be concrete in their entirety. All the application needs to do, is
to instantiate the correct instance of the lib (the one iaw. his
platform needs). This then in turn creates a library factory, who's
interface is hidden from clients, and that is only used by library
components (bridge pattern). The client then depends on consistent
concrete interfaces, and create these calling normal constructors. They
in turn use the factory to create underlying products, oblivious to the
client. Obviously, above mentioned method is not applicable to generic
libraries such as STL and boost.

My other point is, that if you use AbstractFactories - be sure that the
interface that the client requires to create her aggregate is very
narrow. Alexandrescu also emphasizes this in MCPP Design (scattered
hierarchies). This can also be achieved by the CreationAbstractor<T>
mentioned above.

Regards,

W

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!


Mar 6 '06 #29
werasm wrote:
LSP - I have looked this up years ago (Robert Martin -
objectmentor.com). Why not just give me the URL :-).
RCM didn't invented it. But I would have to google too, so you may as well
take the shot. You will find many, many different explanations and
tutorials about LSP. Some consider it the core topic of all of Object
Oriented Programming.
And I've also read the other object mentor related things. You're
missing the point. AbstractFactory according to GOF is used for a whole
range of products. MCPP Design is iaw. this. The factory caters for
products with unrelated bases - read it. The range of products don't
require a common base - actually, I think it's detrimental to have a
common base.
Until now I have avoided discussing the two /DP/ factories, because most
programs need only Prototype, or less.

Abstract factory returns common base types for each element it creates.
There's no common base of all elements; there is at least one for each
type. The example distinguishes Window with PMWindow and MotifWindow, then
ScrollBar with PMScrollBar and MotifScrollBar. Of course Window and
ScrollBar have no common base class. Clients of AbstractFactory are still
insulated from PM and Motif.

FactoryMethod encapsulates creation within a derived method. So (in C++) the
returned type must be a member of a type hierarchy.
My point is, that if one uses concrete interfaces, factories aren't
required (at least not by the client). The alternative has pros and
cons, but consider it as alternative to what your propose, and realise
that pimpl has more uses than you've advocated.
Can you please rename the thing you so eagerly defend to something other
than Pimpl? I fully support defending that thing!

And (per /DP/) there's no such thing as a "concrete interface", and designs
should program "to the interface".
OOP Library components
can be concrete in their entirety. All the application needs to do, is
to instantiate the correct instance of the lib (the one iaw. his
platform needs).
Right - polymorphism strikes again. The interface to the lib is still
abstract, still virtual. It just might not use the virtual keyword.
This then in turn creates a library factory, who's
interface is hidden from clients, and that is only used by library
components (bridge pattern). The client then depends on consistent
concrete interfaces, and create these calling normal constructors. They
in turn use the factory to create underlying products, oblivious to the
client.
Uh, sure, if you actually need all that...
Obviously, above mentioned method is not applicable to generic
libraries such as STL and boost.
Whyyyyyyyyy not?
My other point is, that if you use AbstractFactories - be sure that the
interface that the client requires to create her aggregate is very
narrow. Alexandrescu also emphasizes this in MCPP Design (scattered
hierarchies). This can also be achieved by the CreationAbstractor<T>
mentioned above.


All interfaces should be narrow. Per RCM, there should be one, different,
interface for each category of client to an object.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 6 '06 #30

Phlip wrote:
werasm wrote:

RCM didn't invented it.
No, Barbara Liskov did. I think references can be found in Herb Sutters
EC++.
Until now I have avoided discussing the two /DP/ factories, because most
programs need only Prototype, or less.
Yes, I realised we weren't talking about the same thing. You were
talking about Factory Method, I was talking about Abstract Factories.
The latter is more common for creating families of products.
Abstract factory returns common base types for each element it creates.
Thank you for that bit of insight :-).
There's no common base of all elements; there is at least one for each
type. The example distinguishes Window with PMWindow and MotifWindow, then
ScrollBar with PMScrollBar and MotifScrollBar. Of course Window and
ScrollBar have no common base class. Clients of AbstractFactory are still
insulated from PM and Motif.
Yes, I read GOF years ago. I see you use their example well.

FactoryMethod encapsulates creation within a derived method. So (in C++) the
returned type must be a member of a type hierarchy.
Yes, we weren't talking about the same thing. I was giving AFactory as
example from the start of my argument
Can you please rename the thing you so eagerly defend to something other
than Pimpl? I fully support defending that thing!
"The Bridge Pattern" - which makes use of the "Pimpl idiom". Yes, we
need to discern between Architectural Patterns, Design Patterns and
Idioms. Pimpl is the latter, that is used in a Design Pattern (bridge).
And (per /DP/) there's no such thing as a "concrete interface", and designs
should program "to the interface".
Ohhh, but you do get something with a consistent interface. Does that
require to be abstract as well? Actually, most things have consistent
interfaces. Its the implementation that varies, mostly. Read about
"Bridge Patten" - RCM tells you all about that too. I got most of my
OOP ideas from him.
Right - polymorphism strikes again. The interface to the lib is still
abstract, still virtual. It just might not use the virtual keyword.
Not true. Certainly not for STL and BOOST (but they not applicable in
this discussion as they are not pure OOP libs. The paradigm "generic
programming" comes into play).
Uh, sure, if you actually need all that...
Oh, makes it alot easier for the client.
Whyyyyyyyyy not?
Because of the paradigm shift, see.
All interfaces should be narrow. Per RCM, there should be one, different,
interface for each category of client to an object.


Yes, I've emphasized that already. This is a good rule of thumb.
Exceptions are always a consideration, though. As long as one knows the
pros and cons.

W

Mar 7 '06 #31
Phlip wrote:
werasm wrote: Right - polymorphism strikes again. The interface to the lib is still
abstract, still virtual. It just might not use the virtual keyword.


Not true. Certainly not for STL and BOOST (but they not applicable in
this discussion as they are not pure OOP libs. The paradigm "generic
programming" comes into play).


Sorry, I misread what you were saying. You right - this is yet another
form of polymorphism - without the virtual keyword (at least from
clients perspective). The interface to the library is abstract in the
sense that the true implementation is only decided at runtime by the
Abstract Factory, however, as far as physical structure is concerned
(in coding terms), it is non-abstract i.e. the client instantiates a
non-abstract class. If you reason that a class is abstract just because
it aggregates abstract classes, well then yes - in that case all
classes are always abstract, because most class interface to abstract
classes.

Regards,

W

Mar 7 '06 #32
werasm wrote:
Can you please rename the thing you so eagerly defend to something other
than Pimpl? I fully support defending that thing!


"The Bridge Pattern" - which makes use of the "Pimpl idiom". Yes, we
need to discern between Architectural Patterns, Design Patterns and
Idioms. Pimpl is the latter, that is used in a Design Pattern (bridge).


Suppose we write a UML diagram on a whiteboard. Now you point to one box,
and say "oh, make that one a Pimpl".

I can do that by drawing a jagged line across the box and writing <<pimpl>>
on it. Expressing the trivial extra design in UML is possible, but it's
still implementation-level, below the level of the abstract design.

I can't simplify any other class's design. (Gee, of course I can simplify
their headers, and that might take pressure off them. I can't simplify them
in terms of what methods they call on our Pimpl-ed class.)

Now suppose you point to one box and say, "Escallate that into interfaces,
one for each category of clients". I shouldn't write that as a <<tag>>. I
should write some base classes, and should point the clients at them. This
provides an opportunity to simplify those classes. And, because C++ is
wonderful^W pragmatic^W vaguely utilitarian, a client that depends on an
abstract base class doesn't need to compile all the headers that its
derived class implementation needs.

You guys have gotten hung up on Pimpl as if it were a high-level design
pattern, or a good goal. It's just a technique. Clean code with a mature
design doesn't need it.

--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!
Mar 7 '06 #33

Phlip wrote:
werasm wrote:
"The Bridge Pattern" - which makes use of the "Pimpl idiom". Yes, we
need to discern between Architectural Patterns, Design Patterns and
Idioms. Pimpl is the latter, that is used in a Design Pattern (bridge).
You guys have gotten hung up on Pimpl as if it were a high-level design
pattern, or a good goal. It's just a technique. Clean code with a mature
design doesn't need it.


Well, seems you know UML too :->. Congats.
Clearly, You did not read what I wrote, else your response would have
been either naught, or different. I stated that the pimpl is and idiom
e.g. a implementation technique - a use to a means. The Design Pattern
is called "The Bridge Pattern". The "C++" idiom "Pimpl" is used in C++
to implement the Bridge Pattern. What you stating is in actual fact the
exact opposite. We only use pimpl to implement a high level design
pattern (like a list is used to implement the Subject-Observer Pattern
and the SO Pattern is used to implement the MVC Architectural Pattern.
I say again and again and again. It's only an idiom. Sometimes it is
used to, as you mentioned - fix the rotten smell. Other times, it has
roles to play in good design. If you can't see this, well - so sad,
your loss.

Kind regards,

Werner
--
Phlip
http://www.greencheese.org/ZeekLand <-- NOT a blog!!!


Mar 8 '06 #34
> Suppose we write a UML diagram on a whiteboard. Now you point to one box,
and say "oh, make that one a Pimpl".
No, I would not say that. I would expect you to use the best
implementation technique for the circumstance. I'm also, like you not
interested in the details. This is however not a design group and the
details are relevant.
I can do that by drawing a jagged line across the box and writing <<pimpl>>
on it. Expressing the trivial extra design in UML is possible, but it's
still implementation-level, below the level of the abstract design.
So? What has this got to do with my previous post.
I can't simplify any other class's design. (Gee, of course I can simplify
their headers, and that might take pressure off them. I can't simplify them
in terms of what methods they call on our Pimpl-ed class.)
We just realise the interfaces (or the requirement), whether
implemented as abstract or not is not relevant during conceptual
design.
Now suppose you point to one box and say, "Escallate that into interfaces,
one for each category of clients". I shouldn't write that as a <<tag>>. I
should write some base classes, and should point the clients at them. This
provides an opportunity to simplify those classes. And, because C++ is
wonderful^W pragmatic^W vaguely utilitarian, a client that depends on an
abstract base class doesn't need to compile all the headers that its
derived class implementation needs.
Yes, I agree with this. You mention the one case here. I will go into
my way of realising interfaces. Usually, I start with an entity (Bob).
Bob provides some services, and it requires some services. Services are
required from other entities. This realises the relationships that Bob
need to have. Some of these relationships are really vague. In some
cases I'm also not sure whether services required from particular
entities may vary at a later stage. Other entities (like STL and BOOST
lib entities) are crystal clear and I know that they wont change.
Therefore no abstraction is required when using these. A platform
peripheral like a mutex is an example of this. It's interface is known
to the extent where it has become mature enough to not change, but it
may have many implementations, which it gathers as new platforms (or
changes to platforms) comes along. In this case, the service provider
existed before the entity required it. If new implementation comes
along, Bob does not want to know about it (him wanting to be
cross-platform).

So, I have ask myself long ago - when shall I use the pimpl idiom, and
when not? I came to this conclusion.

1) When I write a class that provides a concise service but whose
implementation may vary, and this class is realised prior to his
clients requiring his realisation, to protect them from variance in
implementation, pimpl will do.

2) If I am a client (Bob) and I require services and I do not want to
be dependent on my service providers interfaces, as the likelyhood of
their interfaces varying at a later stage are great, then I will make
myself depended on interfaces defined in the same package (or
namespace, or logical organisational unit) that I'm in.

3) Another reason for pimpl - come to think of it! If I require to
execute within my own context (not within the context of the caller),
then I make use of a concrete interface whos member functions are
called synchronously. They in turn use some mechanism to call the pimpl
methods asynchronously.

.... and many more.

You guys have gotten hung up on Pimpl as if it were a high-level design
pattern, or a good goal. It's just a technique. Clean code with a mature
design doesn't need it.


Hmmm, who ever said anything about high level design pattern. We
actually said the exact opposite. Do you even read what we write, or
are you to hung up about what you know?

Regards, of the kind sort.

W

Mar 8 '06 #35

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

7 posts views Thread by Icosahedron | last post: by
4 posts views Thread by Oystein Haare | last post: by
5 posts views Thread by pmatos | last post: by
2 posts views Thread by Graham Reitz | last post: by
By using this site, you agree to our Privacy Policy and Terms of Use.