Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Self-containment in OO

13 views
Skip to first unread message

Vladimir Jovic

unread,
Mar 22, 2010, 7:40:56 AM3/22/10
to
Hello,

In the article in Lahman's blog here:
http://pathfinderpeople.blogs.com/hslahman/what_is_oo_development_all_about/
the explanation about self-containment in OO tells this:

***
As a practical matter that means that any object responsibility can be
exhaustively unit tested without implementing any other object behaviors
***

What if the system is complex, and this class handles other classes?
What are strategies for handling this case?

Since I am working in C++, I can imaging that the testing class is
implemented as a template, where the template parameters are the classes
that the tested class handles.
Or, is there other way?
This seams like a complex solution just to simplify testing.

Thanks in advance.


PS In case I wasn't clear enough, here is a simple ASCI diagram:
a -> b
\-> c -> d
\-> e
\-> f

The question : How to test class c, using stubs for classes d,e,f?
An example in c++ would be perfect, but java or other language is ok as well

Daniel T.

unread,
Mar 22, 2010, 10:47:03 AM3/22/10
to
Vladimir Jovic <vlada...@gmail.com> wrote:

Looking at your diagram, if 'd', 'e', and 'f' were all ints would you be
concerned with testing 'c' in isolation to them? I think not. You can be
sure that 'int' is fully tested and functions, and you rely on that fact
when testing to see that 'c' works.

Things aren't much different when 'd', 'e' or 'f' are classes that you
write, except that you have more work to do in order to make sure they
are fully tested before you can test 'c' properly.

The key to "self-containment" is that 'c' does expose its 'd', 'e' or
'f' and rely on others changing the state of them outside of itself. In
other words, your aim should be a containment relationship (as defined
by Riel, not the UML version of containment) wherever possible.

The point of containment in this sense, is that 'a' doesn't care that
'c' uses one or more 'd's to do its job, and 'c' could replace its 'd'
with something completely different without affecting 'a' in any way.
(This is one of the motivations for making all member-variables private.)

Hope this helps.

Vladimir Jovic

unread,
Mar 22, 2010, 12:47:25 PM3/22/10
to

Yes, off course. Doing that makes not sense.

> Things aren't much different when 'd', 'e' or 'f' are classes that you
> write, except that you have more work to do in order to make sure they
> are fully tested before you can test 'c' properly.
>

The problem I am facing is the unit tests are becoming more and more
complex as the we go more levels down.
But is there an alternative?

> The key to "self-containment" is that 'c' does expose its 'd', 'e' or
> 'f' and rely on others changing the state of them outside of itself. In
> other words, your aim should be a containment relationship (as defined
> by Riel, not the UML version of containment) wherever possible.
>

Great. Do you recommend Riel's book to improve OO design?
I found this on the net:
http://lcm.csa.iisc.ernet.in/soft_arch/OO_Design_Heuristic.htm
(summary of OO design from Riel)

I do not understand what do you mean by "c expose d,e or f".
My colegue suggested to add a method to create instances of the
contained classes, but in my opinion that would simplify the testing,
but the class (and all contained classes) would become much more complex.
Do you mean something like this:

class E : public SomeBase1
{
//...
};
class F : public SomeBase2
{
//...
};

class C
{
//...
void UpdateClass1( SomeBase1 &obj1 )
{
// update instance of object e
}
void UpdateClass2( SomeBase2 &obj2 )
{
// update instance of object f
}

private:
SomeBase1 *e;
SomeBase2 *f;
};

> The point of containment in this sense, is that 'a' doesn't care that
> 'c' uses one or more 'd's to do its job, and 'c' could replace its 'd'
> with something completely different without affecting 'a' in any way.
> (This is one of the motivations for making all member-variables private.)
>

Ok, that means I missunderstood the self-containment. I thought it
favour the use of mock classes for d,e,f classes when testing class c.

> Hope this helps.

It does :)

Daniel T.

unread,
Mar 22, 2010, 7:18:43 PM3/22/10
to
Vladimir Jovic <vlada...@gmail.com> wrote:
> Daniel T. wrote:
> > Vladimir Jovic <vlada...@gmail.com> wrote:
> >
> > > PS In case I wasn't clear enough, here is a simple ASCI diagram:
> > > a -> b
> > > \-> c -> d
> > > \-> e
> > > \-> f
> > >
> > > The question : How to test class c, using stubs for classes d,e,f?
> > > An example in c++ would be perfect, but java or other language is
> > > ok as well
> >
> > Looking at your diagram, if 'd', 'e', and 'f' were all ints would
> > you be concerned with testing 'c' in isolation to them? I think not.
> > You can be sure that 'int' is fully tested and functions, and you
> > rely on that fact when testing to see that 'c' works.
>
> Yes, off course. Doing that makes not sense.
>
> > Things aren't much different when 'd', 'e' or 'f' are classes that
> > you write, except that you have more work to do in order to make
> > sure they are fully tested before you can test 'c' properly.
>
> The problem I am facing is the unit tests are becoming more and more
> complex as the we go more levels down. But is there an alternative?

I'm not sure what you mean by "levels down" do you mean d, e, and f are
harder to test than c, or that c is harder to test than d, e, and f?

Either way, if you distributed your functionality well, no class should
be harder to test than any other.

> > The key to "self-containment" is that 'c' does expose its 'd', 'e'
> > or 'f' and rely on others changing the state of them outside of
> > itself. In other words, your aim should be a containment
> > relationship (as defined by Riel, not the UML version of
> > containment) wherever possible.
>
> Great. Do you recommend Riel's book to improve OO design? I found this
> on the net:
> http://lcm.csa.iisc.ernet.in/soft_arch/OO_Design_Heuristic.htm
> (summary of OO design from Riel)

Yes, I recommend it highly.

> I do not understand what do you mean by "c expose d,e or f".

The point of a containment relationship is that it guarantees that we
can ignore the contained objects at some high level of design. In your
example above, if 'a' (and 'c's test harness) can use 'c' without
knowing that it is using a 'd', 'e' and 'f' internally, then 'c'
contains them.

As a practical matter in C++, this means that 'c' does not expose a
pointer or non-const reference to the 'd', 'e', or 'f' that it contains.

> My colegue suggested to add a method to create instances of the
> contained classes, but in my opinion that would simplify the testing,
> but the class (and all contained classes) would become much more
> complex. Do you mean something like this:
>
> class E : public SomeBase1
> {
> //...
> };
> class F : public SomeBase2
> {
> //...
> };
>
> class C
> {
> //...
> void UpdateClass1( SomeBase1 &obj1 )
> {
> // update instance of object e
> }
> void UpdateClass2( SomeBase2 &obj2 )
> {
> // update instance of object f
> }
>
> private:
> SomeBase1 *e;
> SomeBase2 *f;
> };

Again, what if they were ints? Would you feel the need to put two
"updateInt" functions in the class in order to test it? Sometimes that
is appropriate, but not often.

If anything, maybe something like this:

class C {
E e;
F f;
public:
const E& get_e() const { return e; }
const F& get_f() const { return f; }
// functions that modify e and f
};

Note that the above does *not* break containment because the functions
can easily be replace by ones that return by value, for example:

E C::get_e() const {
E result;
// set E's state appropriately using other information.
return result;
}

In languages other than C++, things get a little more complicated and it
is harder to efficiently maintain containment.

Vladimir Jovic

unread,
Mar 23, 2010, 4:46:17 AM3/23/10
to
Daniel T. wrote:
> Vladimir Jovic <vlada...@gmail.com> wrote:
>> Daniel T. wrote:
>>> Vladimir Jovic <vlada...@gmail.com> wrote:
>>>
>>>> PS In case I wasn't clear enough, here is a simple ASCI diagram:
>>>> a -> b
>>>> \-> c -> d
>>>> \-> e
>>>> \-> f
>>>>
>>>> The question : How to test class c, using stubs for classes d,e,f?
>>>> An example in c++ would be perfect, but java or other language is
>>>> ok as well
>>> Looking at your diagram, if 'd', 'e', and 'f' were all ints would
>>> you be concerned with testing 'c' in isolation to them? I think not.
>>> You can be sure that 'int' is fully tested and functions, and you
>>> rely on that fact when testing to see that 'c' works.
>> Yes, off course. Doing that makes not sense.
>>
>>> Things aren't much different when 'd', 'e' or 'f' are classes that
>>> you write, except that you have more work to do in order to make
>>> sure they are fully tested before you can test 'c' properly.
>> The problem I am facing is the unit tests are becoming more and more
>> complex as the we go more levels down. But is there an alternative?
>
> I'm not sure what you mean by "levels down" do you mean d, e, and f are
> harder to test than c, or that c is harder to test than d, e, and f?
>

If the system looks like this:

a -> b -> c -> d -> .... -> x -> y
-> ...
-> ...
-> ...

where all these are classes, then testing a is basically the
functionality test. But if you want to write unit tests for b or c,
things are more complicated then for example writing for x or y. Unless
you use mock classes.

> Either way, if you distributed your functionality well, no class should
> be harder to test than any other.
>

Might be a stupid question, but is there a metric or method to measure
functionality distribution?

>>> The key to "self-containment" is that 'c' does expose its 'd', 'e'
>>> or 'f' and rely on others changing the state of them outside of
>>> itself. In other words, your aim should be a containment
>>> relationship (as defined by Riel, not the UML version of
>>> containment) wherever possible.
>> Great. Do you recommend Riel's book to improve OO design? I found this
>> on the net:
>> http://lcm.csa.iisc.ernet.in/soft_arch/OO_Design_Heuristic.htm
>> (summary of OO design from Riel)
>
> Yes, I recommend it highly.
>
>> I do not understand what do you mean by "c expose d,e or f".
>
> The point of a containment relationship is that it guarantees that we
> can ignore the contained objects at some high level of design. In your
> example above, if 'a' (and 'c's test harness) can use 'c' without
> knowing that it is using a 'd', 'e' and 'f' internally, then 'c'
> contains them.
>
> As a practical matter in C++, this means that 'c' does not expose a
> pointer or non-const reference to the 'd', 'e', or 'f' that it contains.
>

Ok, this makes it clear. This basically means that class 'a' knows what
class 'b' (that is contains) is going to do, but now how.

Thank you.

Daniel T.

unread,
Mar 23, 2010, 8:44:26 AM3/23/10
to

Not at all. Your job when testing b or c is the same as when testing x
or y... make sure that each function meets its postconditions when its
preconditions hold true. The cyclomatic complexity of 'b' should be
about the same as the cyclomatic complexity of 'x'.

> > Either way, if you distributed your functionality well, no class
> > should be harder to test than any other.
>
> Might be a stupid question, but is there a metric or method to measure
> functionality distribution?

I use the number of tests necessary for thorough path test coverage.
Figure out the cyclomatic complexity of each module (class) in your
system, and try to bring them on par. Don't be dogmatic about it, but if
you have some modules that are much higher than average, those are god
classes and need to be re-distributed. Classes that are much lower are
data buckets and should probably take on more responsibility.

Here is the original paper on cyclomatic complexity:

http://classes.cecs.ucf.edu/eel6883/berrios/notes/Paper%204%20(Complexity
%20Measure).pdf

As a practical matter, you determine the cyclomatic complexity of a
class by adding the following:

+1 for each non-const member function.
+1 for each loop ('while' and 'for') in the member functions of the class
+1 for each decision ('if' and 'case') in the member functions of the
class
-1 for each exception ('throw') in the member functions of the class

Yes, you are literally counting keywords here, which makes it an easy
thing to do. Maybe you can even write a tool that does it for you.

BTW, the above assumes two things, (1) that you never put mutating state
inside a conditional and (2) that you only throw an exception when there
is a contract violation.

Garrett Smith

unread,
Apr 16, 2010, 1:53:56 AM4/16/10
to

I see:

| 2. Users of a class must be dependent on its public interface, but a
| class should not be dependent on its users.

What about IoC? What is wrong with depending on abstractions.


| 18. Eliminate irrelevant classes from your design.

Only classes? What about methods or even statements?

Dead code makes the code harder to read and hides bugs.

| 37. Derived classes must have knowledge of their base class by
| definition, but base classes should not know anything about their
| derived classes

NO abstract classes? If an abstract class has two methods, m1 and m2, m1
calls m2, and m2 is abstract then it must be implemented by the
subclass. That seems to violate this principle.

I agree with a lot of what is written, though it seems a bit rough.

[...]
--
Garrett
comp.lang.javascript FAQ: http://jibbering.com/faq/

Daniel T.

unread,
Apr 16, 2010, 9:37:20 AM4/16/10
to
Garrett Smith <dhtmlk...@gmail.com> wrote:
> Daniel T. wrote:
> > Vladimir Jovic <vlada...@gmail.com> wrote:

> > > Do you recommend Riel's book to improve OO design? I found this on
> > > the net:
> > > http://lcm.csa.iisc.ernet.in/soft_arch/OO_Design_Heuristic.htm
> > > (summary of OO design from Riel)
> >
> > Yes, I recommend it highly.
>
> I see:
>
> | 2. Users of a class must be dependent on its public interface, but a
> | class should not be dependent on its users.
>
> What about IoC? What is wrong with depending on abstractions.

From the book:

If a contained object is dependent on the class that contains it,
then it is not reusable. If the card reader of an automatic teller
machine has knowledge that it is contained in an ATM, then it cannot
be taken out of the ATM and used to build a security door...

When a contained object must send a message to its containing class,
then the containing class should be made more general through
judicious use of inheritance. In this way, the contained object does
not depend on the containing class, only on its base class. It is
easier to require a reuser to inherit from some abstract class than
to use a particular class (effectively ruining any chance of reuse
outside of the original domain).

In other words, there is nothing wrong with depending on abstractions,
there is something wrong with depending on concrete types.

> | 18. Eliminate irrelevant classes from your design.
>
> Only classes? What about methods or even statements?
>
> Dead code makes the code harder to read and hides bugs.

The heuristic that covers methods is: "Do not clutter the public
interface of a class with things that users of that class are not able
to use or are not interested in using." As for statements, the book is
aimed at Object-Oriented heuristics, not general programming heuristics.

Now you might wonder why two different heuristics? The answer is due to
the difference in granularity and reusability. In order for a class to
be reusable, it often must provide methods that not all users use. (For
example, there are many methods on your typical container class that may
never be used in any one particular context, or even in an entire
program, but they are used by some of their clients.) However, an
"irrelevant class" is defined in the book as a class that has only the
standard complement of methods that all classes are expected to
implement (as per heuristic 4: "Implement a minimal public interface
that all classes understand.") Such classes have (almost literally) no
functionality within any system.

> | 37. Derived classes must have knowledge of their base class by
> | definition, but base classes should not know anything about their
> | derived classes
>
> NO abstract classes? If an abstract class has two methods, m1 and m2,
> m1 calls m2, and m2 is abstract then it must be implemented by the
> subclass. That seems to violate this principle.

Here there is some confusion about what the "knowing something about"
means:

If base classes have knowledge of their derived classes, then it is
implied that if a new derived class is added to a base class, the
code of the base class will need modification.

Riel goes on to specifically address the use of polymorphism to avoid
these sorts of issues. In other words, your example does not violate
this heuristic, rather it is a method for avoiding the violation of this
heuristic.

More concretely:

class Base {
virtual void foo() = 0;
public:
void bar() {
foo();
}
};

The class above depends only on its own interface, it does not depend on
the interface, or implementation of any derived class.

> I agree with a lot of what is written, though it seems a bit rough.

The heuristics alone as a bald list do seem a bit rough, but you have to
remember there is an almost 400 page book that goes along with them.
Also, keep in mind, the book was written in 1996 when people were new to
OO. Now, some of the heuristics are things that are an ingrained part of
the community.

Garrett Smith

unread,
Apr 16, 2010, 9:00:18 PM4/16/10
to
Daniel T. wrote:
> Garrett Smith <dhtmlk...@gmail.com> wrote:
>> Daniel T. wrote:
>>> Vladimir Jovic <vlada...@gmail.com> wrote:
>
>>>> Do you recommend Riel's book to improve OO design? I found this on
>>>> the net:
>>>> http://lcm.csa.iisc.ernet.in/soft_arch/OO_Design_Heuristic.htm
>>>> (summary of OO design from Riel)
>>> Yes, I recommend it highly.
>> I see:
>>
>> | 2. Users of a class must be dependent on its public interface, but a
>> | class should not be dependent on its users.
>>
>> What about IoC? What is wrong with depending on abstractions.
>
> From the book:
>
> If a contained object is dependent on the class that contains it,
> then it is not reusable. If the card reader of an automatic teller
> machine has knowledge that it is contained in an ATM, then it cannot
> be taken out of the ATM and used to build a security door...
>


Thanks you for the clarification and elaboration.

Is the conclusion in the first sentence is strongly supported by the
rest of the paragraph?

The rest of the paragraph misses a more obvious point: Why paint
yourself into a corner with tangled design as tight coupling?

Arguments about the security door are going to seem like far fetched
theoretical paranoia to a project manager who is commissioned to a
project regarding ATM cards and won't likely have much influence.

The Card's implementation details depending will result in confusing
code in the Card. Such coupling might cause problems when a new ATM
vendor comes on the market (different host environment). Addressing
those problems is going to cost money and having everybody in a mad
scramble trying to make things work in the new version or make of the
ATM when it is released.

This sort of thing is unfortunately too common with javascript for the
web. Programs are often tied to proprietary (mis)features of a
particular host environment (browser).

Then when "it doesn't work", you end up seeing code like:

| <meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7" >

The sad thing is that most of the "cross browser" javascript libraries
that you'll see suffer from these avoidable problems.

Implementation code is also often tied with details of a javascript
library. The jQuery javascript library, for exmaple, provides methods
that are very complicated and loosely defined. When they change, it
tends to "not work" for the user going through an upgrade.

> When a contained object must send a message to its containing class,
> then the containing class should be made more general through
> judicious use of inheritance. In this way, the contained object does
> not depend on the containing class, only on its base class. It is
> easier to require a reuser to inherit from some abstract class than
> to use a particular class (effectively ruining any chance of reuse
> outside of the original domain).
>

OK.

> In other words, there is nothing wrong with depending on abstractions,
> there is something wrong with depending on concrete types.
>

Right.

>> | 18. Eliminate irrelevant classes from your design.
>>
>> Only classes? What about methods or even statements?
>>
>> Dead code makes the code harder to read and hides bugs.
>
> The heuristic that covers methods is: "Do not clutter the public
> interface of a class with things that users of that class are not able
> to use or are not interested in using."


Right.

As for statements, the book is
> aimed at Object-Oriented heuristics, not general programming heuristics.
>

OK.

> Now you might wonder why two different heuristics? The answer is due to
> the difference in granularity and reusability. In order for a class to
> be reusable, it often must provide methods that not all users use. (For
> example, there are many methods on your typical container class that may
> never be used in any one particular context, or even in an entire
> program, but they are used by some of their clients.)

Sounds cluttered.

I try to get maximum reuse out of each module. Who wants to use a module
that is largely irrelevant? Regarding code that must be downloaded
before being interpreted, certainly the user does not!

However, an
> "irrelevant class" is defined in the book as a class that has only the
> standard complement of methods that all classes are expected to
> implement (as per heuristic 4: "Implement a minimal public interface
> that all classes understand.") Such classes have (almost literally) no
> functionality within any system.
>

I see. But what if someone *is* genuinely interested in using that
interface? What if, for example, an interface is defined and then an
implementation implements all of those methods in a specific way such
that other implementations would find irrelevant? What about State or
Strategy, for that matter?

For example, an interface with a "blendTo" method that acts as a
strategy. Color transition is implemented in a way that would make no
sense to the transition for length, yet ColorTransition and
LengthTransition have identical interfaces, making them both redundant.

>> | 37. Derived classes must have knowledge of their base class by
>> | definition, but base classes should not know anything about their
>> | derived classes
>>
>> NO abstract classes? If an abstract class has two methods, m1 and m2,
>> m1 calls m2, and m2 is abstract then it must be implemented by the
>> subclass. That seems to violate this principle.
>
> Here there is some confusion about what the "knowing something about"
> means:
>
> If base classes have knowledge of their derived classes, then it is
> implied that if a new derived class is added to a base class, the
> code of the base class will need modification.
>
> Riel goes on to specifically address the use of polymorphism to avoid
> these sorts of issues. In other words, your example does not violate
> this heuristic, rather it is a method for avoiding the violation of this
> heuristic.
>

I see. I think the problem is his terminology "no knowledge". Obviously,
a superclass that is designed to be such knows that it has a subclass (a
subclass has knowledge of its superclass should go without saying).

Reil is not capable of describing exactly what it is that is a problem
from which he has derived his badly written principle.

A more suitable principle, IMHO:

Do not rely on implementation details or other side effects of an object
- code to the interface (as above).

> More concretely:
>
> class Base {
> virtual void foo() = 0;
> public:
> void bar() {
> foo();
> }
> };
>
> The class above depends only on its own interface, it does not depend on
> the interface, or implementation of any derived class.
>

Right. *That* makes sense.

>> I agree with a lot of what is written, though it seems a bit rough.
>
> The heuristics alone as a bald list do seem a bit rough, but you have to
> remember there is an almost 400 page book that goes along with them.
> Also, keep in mind, the book was written in 1996 when people were new to
> OO. Now, some of the heuristics are things that are an ingrained part of
> the community.

Ah, if only it were true with javascript community. Take a look at, for
example, Dojo or MooTools.

johnza...@gmail.com

unread,
Apr 28, 2010, 3:32:38 PM4/28/10
to
On Mar 22, 7:40 am, Vladimir Jovic <vladasp...@gmail.com> wrote:
> Hello,
>
> In the article in Lahman's blog here:http://pathfinderpeople.blogs.com/hslahman/what_is_oo_development_all...

> the explanation about self-containment in OO tells this:
>
> ***
> As a practical matter that means that any object responsibility can be
> exhaustively unit tested without implementing any other object behaviors
> ***
>
> What if the system is complex, and this class handles other classes?
> What are strategies for handling this case?
>
> Since I am working in C++, I can imaging that the testing class is
> implemented as a template, where the template parameters are the classes
> that the tested class handles.
> Or, is there other way?
> This seams like a complex solution just to simplify testing.
>
> Thanks in advance.
>
> PS In case I wasn't clear enough, here is a simple ASCI diagram:
> a -> b
>    \-> c -> d
>          \-> e
>          \-> f
>
> The question : How to test class c, using stubs for classes d,e,f?
> An example in c++ would be perfect, but java or other language is ok as well

The problem here is methodological, and has nothing to do with code.

You are fundamentally abusing the static production rules of your
third generation language.

You are using an inheritance hierarchy to create a "controller tree".
In Executable UML, Balcer and Mellor emphatically state:

"The first rule of control partitioning is that you do not make
controller objects. The second rule of control partitioning is that
*you do not make controller objects.", Executable UML, page 236,
http://books.google.com/books?id=zBS0aWNjBqcC&pg=PA236&lpg=PA236&dq=The+first+rule+of+control+partitioning+is+that+you+do+not+create+controller+objects.&source=bl

It is fundamentally obvious to anyone looking at your example that you
are using object superclasses as a way to control leaf classes. What
you've effectively done is build a lattice, or house of cards, for a
software system. The reason unit testing is getting progressively
harder is that you don't actually have any real world analagous units
to test, so as you make changes to your software, you have to edit the
lattice appropriately. This requires preserving an entailment
relation through all leaf classes that ensures value paths are
steady. Technically speaking, this is fundamentally impossible to do
and will guarantee client code will break if it is using value paths
now absorbed by the new class.

Bottom line: Your software, as implemented, sucks, and is not testable
in the way OO programmers discuss testability. Kent Beck pioneered
Test-Driven Development as a way to help prevent programmers from
making such design mistakes, because testing first generally causes
programmers to steer clear of such awful designs.

johnza...@gmail.com

unread,
Apr 28, 2010, 3:33:38 PM4/28/10
to
On Apr 16, 9:00 pm, Garrett Smith <dhtmlkitc...@gmail.com> wrote:

> Daniel T. wrote:
> > The heuristics alone as a bald list do seem a bit rough, but you have to
> > remember there is an almost 400 page book that goes along with them.
> > Also, keep in mind, the book was written in 1996 when people were new to
> > OO. Now, some of the heuristics are things that are an ingrained part of
> > the community.
>
> Ah, if only it were true with javascript community. Take a look at, for
> example, Dojo or MooTools.
> --
> Garrett
> comp.lang.javascript FAQ:http://jibbering.com/faq/

Interesting. I would love examples of shitty Javascript code. Where
should I start looking?

I don't think the problem is especially unique to prototype-based
languages like Javascript, though...

S Perryman

unread,
Apr 29, 2010, 4:21:13 AM4/29/10
to
johnza...@gmail.com wrote:

Ironic, as all the examples I've ever seen presented by the test-driven
"gurus" have appalling testability (by "testability" I use the long-
time qualitative/quantitative definitions used by the hardware/software
engineering communities) .


Regards,
Steven Perryman

Vladimir Jovic

unread,
Apr 29, 2010, 4:50:23 AM4/29/10
to
johnza...@gmail.com wrote:
>
> It is fundamentally obvious to anyone looking at your example that you
> are using object superclasses as a way to control leaf classes. What
> you've effectively done is build a lattice, or house of cards, for a
> software system. The reason unit testing is getting progressively
> harder is that you don't actually have any real world analagous units
> to test, so as you make changes to your software, you have to edit the
> lattice appropriately. This requires preserving an entailment
> relation through all leaf classes that ensures value paths are
> steady. Technically speaking, this is fundamentally impossible to do
> and will guarantee client code will break if it is using value paths
> now absorbed by the new class.
>

You got that more or less right.

The good thing is that I have better confidence in unit tests, because
they are doing more then just unit testing. Not all things are
"emulated" (as in TDD), but the very bad side is the maintenance is
getting harder.


> Bottom line: Your software, as implemented, sucks, and is not testable
> in the way OO programmers discuss testability. Kent Beck pioneered
> Test-Driven Development as a way to help prevent programmers from
> making such design mistakes, because testing first generally causes
> programmers to steer clear of such awful designs.

I know, but I am trying to improve :)

What are alternatives? How to fix such architecture?

I am looking into dependency injection, but if you know of more
techniques and design patterns that are used in TDD, would be great if
you share.

S Perryman

unread,
Apr 29, 2010, 6:02:25 AM4/29/10
to
Vladimir Jovic wrote:

> What are alternatives? How to fix such architecture?
>
> I am looking into dependency injection, but if you know of more
> techniques and design patterns that are used in TDD, would be great if
> you share.

1. Separation of concerns

A universal means of reducing dependency.


2. If you have a system with a dependency graph thus :

A -> B -> C1 -> D -> E -> F
-> C2

There is nothing wrong with that.

From the construction/testability viewpoint, we are asking whether you
can construct/test thus :

E -> F
D -> E
C1 -> D
B -> C1 + C2
A -> B

where the entities on the right-hand side of the "->" are the real
entities, or test stubs.

The problem you appear to have is that the dependency chain cannot be
divided as above, for your system.

On that basis, the "Demeter" principles are probably most appropriate.
These attempt to minimise the dependencies that entities have on eachother.

You will also find that the Demeter concepts have good synergy with things
like dependency injection.


Regards,
Steven Perryman

Vladimir Jovic

unread,
Apr 29, 2010, 7:40:56 AM4/29/10
to
S Perryman wrote:
> Vladimir Jovic wrote:
>
>> What are alternatives? How to fix such architecture?
>>
>> I am looking into dependency injection, but if you know of more
>> techniques and design patterns that are used in TDD, would be great if
>> you share.
>
> 1. Separation of concerns
>
> A universal means of reducing dependency.
>
>
> 2. If you have a system with a dependency graph thus :
>
> A -> B -> C1 -> D -> E -> F
> -> C2
>
> There is nothing wrong with that.
>
> From the construction/testability viewpoint, we are asking whether you
> can construct/test thus :
>
> E -> F
> D -> E
> C1 -> D
> B -> C1 + C2
> A -> B
>
> where the entities on the right-hand side of the "->" are the real
> entities, or test stubs.
>

I can, but the problem (I think) is that I am using real objects for
unit testing.


> The problem you appear to have is that the dependency chain cannot be
> divided as above, for your system.
>
> On that basis, the "Demeter" principles are probably most appropriate.
> These attempt to minimise the dependencies that entities have on eachother.
>
> You will also find that the Demeter concepts have good synergy with things
> like dependency injection.
>

That means I am on good tracks, because that is how I designed the
system (based on the Demeter concepts).


Thanks

S Perryman

unread,
Apr 29, 2010, 2:36:28 PM4/29/10
to
Vladimir Jovic wrote:

> S Perryman wrote:

>> From the construction/testability viewpoint, we are asking whether you
>> can construct/test thus :

>> E -> F
>> D -> E
>> C1 -> D
>> B -> C1 + C2
>> A -> B

>> where the entities on the right-hand side of the "->" are the real
>> entities, or test stubs.

> I can, but the problem (I think) is that I am using real objects for
> unit testing.

For the example I have given, are you stating that in order to do
verification of E, you are using a "real" F etc ??

If so, do you believe that to be an issue ??
Because it actually isn't.

Using E -> F as the example, the reasons that one would provide a test
stub variant of F, in order to test an implementation of E are :

1. No implementation of F is available at the time that verification of
E is to be done

2. The effort required to operate an actual implementation of F (which
could be a complex environment - hardware etc, have poor testability
etc) , has a very low ROI (time, resources etc) in terms of the actual
verification being done on E.


If none of the above apply, a real implementation of F (on the precondition
that F itself has passed its verification process) is perfectly acceptable
for use in verification of E.


>> On that basis, the "Demeter" principles are probably most appropriate.
>> These attempt to minimise the dependencies that entities have on
>> eachother.

>> You will also find that the Demeter concepts have good synergy with
>> things like dependency injection.

> That means I am on good tracks, because that is how I designed the
> system (based on the Demeter concepts).

Good.


Regards,
Steven Perryman

Vladimir Jovic

unread,
Apr 30, 2010, 2:00:30 AM4/30/10
to
S Perryman wrote:
>
> On that basis, the "Demeter" principles are probably most appropriate.
> These attempt to minimise the dependencies that entities have on eachother.
>
> You will also find that the Demeter concepts have good synergy with things
> like dependency injection.
>

Forgot to ask : what are other OO architecture design approaches?
Do you have a link with summarises all? Or at least all major?

johnza...@gmail.com

unread,
Apr 30, 2010, 5:11:25 PM4/30/10
to
On Apr 29, 4:21 am, S Perryman <a...@a.net> wrote:

I am not sure who your list of gurus are, but I've certainly seen at
least one "guru" admit in a public presentation that how they test
code changed over time, especially once they learned to "mock roles,
not objects."

TDD also is much harder way to do unit testing than the methodology
laid out by H.S. Lahman on his blog, but most practitioners do not
understand the simplicity of the actors model and its simplified
version for real-time embedded systems.

johnza...@gmail.com

unread,
Apr 30, 2010, 5:22:02 PM4/30/10
to
On Apr 29, 4:50 am, Vladimir Jovic <vladasp...@gmail.com> wrote:

> johnzabro...@gmail.com wrote:
>
> > It is fundamentally obvious to anyone looking at your example that you
> > are using object superclasses as a way to control leaf classes.  What
> > you've effectively done is build a lattice, or house of cards, for a
> > software system.  The reason unit testing is getting progressively
> > harder is that you don't actually have any real world analagous units
> > to test, so as you make changes to your software, you have to edit the
> > lattice appropriately.  This requires preserving an entailment
> > relation through all leaf classes that ensures value paths are
> > steady.  Technically speaking, this is fundamentally impossible to do
> > and will guarantee client code will break if it is using value paths
> > now absorbed by the new class.
>
> You got that more or less right.
>
> The good thing is that I have better confidence in unit tests, because
> they are doing more then just unit testing. Not all things are
> "emulated" (as in TDD), but the very bad side is the maintenance is
> getting harder.

I disagree, completely. You have less confidence *in your current
methodology*, and therefore abuse unit tests to do more than just test
units.

Maintenance is not simply getting harder. It is not simply that it is
harder to write correct code for new specifications, or write correct
code to amend an existing specification. It is that you have no
structural integrity whatsoever to your solution. The fact that
superclasses are controlling leaf classes means that you have no
static relationships that show the structure of your problem domain.
You have no structure chart. When the spec changes, that indicates
something has changed in one or more collaborators. The spec may
invalidate the role of a collaborator, and suggest that you need to
change the static structure of your application. By doing this
*analysis*, without writing any code, you can predict how hard
maintenance is going to be.

On the other hand, by using your current approach, you have no way to
tell your client, who is revising the spec, "Whoa, this is way out of
scope, and will cause cost-overruns if we don't negotiate; I have to
pay for Vlad Juniors babysitter."


> > Bottom line: Your software, as implemented, sucks, and is not testable
> > in the way OO programmers discuss testability.  Kent Beck pioneered
> > Test-Driven Development as a way to help prevent programmers from
> > making such design mistakes, because testing first generally causes
> > programmers to steer clear of such awful designs.
>

> I know, but I am trying to improve :)
>

> What are alternatives? How to fix such architecture?

Model the problem -- I can't really give better advice without knowing
something about the problem. Chances are, if you are writing code in
a way such that superclasses are controlling leaf classes, they you
have a specification where the problem can be broken down into stages
(what you are currently using classes with arbitrary names for)

johnza...@gmail.com

unread,
Apr 30, 2010, 5:35:12 PM4/30/10
to
On Apr 30, 5:22 pm, "johnzabro...@gmail.com" <johnzabro...@gmail.com>
wrote:

Oops. I hit send too soon. Please re-read carefully, as I've
inserted sentences below from what I've written above in the last


paragraph. I should've said:

> > What are alternatives? How to fix such architecture?

Model the problem -- I can't really give better advice without knowing

something about the problem. Based on experience with this code
smell, where superclasses are pervasively controlling leaf classes, I
can *guess* what the general solution would look like, but I don't
want the general solution to be your *actual* solution. So what
follows is my analysis of the meta-problem, not the problem domain
itself.

If you are writing code in a way such that superclasses are
controlling leaf classes, then you have a Specification where the
problem can be broken down into Stages (what you are currently using
classes with arbitrary names for). If at each Stage you cannot
predict what the sequences of actions will be, then you also need a
Sequence abstraction so that each Stage is composed of Sequences. But
this analysis is not complete; you need to recursively specify what
the specification of each stage and sequence is. In other words, a
Sequence must collaborate with a SequenceSpecification and a Stage
must collaborate with a StageSpecification. A StageSpecification is
composed of SequenceSpecifications, so that when you want to reuse the
Stage, you instead focus on reusing its Specification, giving you a
degree of freedom in how to implement the Stage by taking advantage of
what the Specification *doesn't* say. This analysis will allow you to
*easily* apply parametric polymorphism on-demand at each Stage and
Sequence, as well as allow for implementation hiding.

Your current solution doesn't really allow parametric polymorphism in
a safe way, and it doesn't allow for implementation hiding since the
parent superclasses must know about their child subclasses in order to
properly control them.

Vladimir Jovic

unread,
May 3, 2010, 8:06:52 AM5/3/10
to

It looks like you are forming a state chart.

>
> Your current solution doesn't really allow parametric polymorphism in
> a safe way, and it doesn't allow for implementation hiding since the
> parent superclasses must know about their child subclasses in order to
> properly control them.
>

In other words, use "tell, don't ask" principle to remove the super
classes. Correct?

btw what tools are you using to model the problem?
I modelled few things, but only on paper. Today I thought of trying
kivio to model a problem I am facing.

johnza...@gmail.com

unread,
May 4, 2010, 11:35:36 AM5/4/10
to
On May 3, 8:06 am, Vladimir Jovic <vladasp...@gmail.com> wrote:

It's not a state chart, it is a collaboration diagram (technically,
not a diagram, since I described the solution in text).

State charts explicitly define execution semantics. What I am instead
doing is saying, you are using "class" to represent a Stage class, and
methods to represent a Sequence class, and inheritance to represent
sequential coupling. I am then saying that what you are doing is
mathematically not versionable, because you have no way to predict
paths through the system used by existing clients, especially in an
open system (e.g., not a "small in-house system" or "embedded
system").

Also, the collaboration diagram represents modeling process
refinement, whereas state charts can model reactive systems. I can
discuss the details as to why this is different some other time (or
forget about it).

> > Your current solution doesn't really allow parametric polymorphism in
> > a safe way, and it doesn't allow for implementation hiding since the
> > parent superclasses must know about their child subclasses in order to
> > properly control them.
>
> In other words, use "tell, don't ask" principle to remove the super
> classes. Correct?
>
> btw what tools are you using to model the problem?
> I modelled few things, but only on paper. Today I thought of trying
> kivio to model a problem I am facing.

I honestly don't use tools. A text editor, some auto-complete, a web
browser, a 30" monitor, and two 20" vertical monitors is all you need.

Garrett Smith

unread,
May 14, 2010, 5:38:03 PM5/14/10
to
johnza...@gmail.com wrote:
> On Apr 16, 9:00 pm, Garrett Smith <dhtmlkitc...@gmail.com> wrote:
>> Daniel T. wrote:
>>> The heuristics alone as a bald list do seem a bit rough, but you have to
>>> remember there is an almost 400 page book that goes along with them.
>>> Also, keep in mind, the book was written in 1996 when people were new to
>>> OO. Now, some of the heuristics are things that are an ingrained part of
>>> the community.
>> Ah, if only it were true with javascript community. Take a look at, for
>> example, Dojo or MooTools.

[snip signature]

>
> Interesting. I would love examples of shitty Javascript code. Where
> should I start looking?
>

OK, but to make to make a good assessment of a piece of code, the
language, and problem context needs to be understood. A fundamental
difference with web apps is variant deployment environment. Could be
IE6, Chrome Frame, various JScript builds, Opera masking as IE, iPhone,
Blacberry 9000.

> I don't think the problem is especially unique to prototype-based
> languages like Javascript, though...

No, but there are significant problems that contribute to really bad
javascript.

There are many many ways to cause problems in code, both with strategies
and overall design.

ECMAScript is a simple but flexible and powerful dynamic language yet
taken for something that is not real programming.

RIA development is relatively new and there is much bad advice and
misinformation.

Interviews for front end engineering are hard on both ends (interviewer
and interviewee). With so much misinformation what should the
interviewer ask? What should he trust? With such a great amount of
misinformation and harmful advice, the majority of web developers hired
are really quite bad at what they do.

Variations exist in implementations of the language itself and
especially in various browser DOMs which were historically awful.

Historically, browser detection has been used to address these and other
issues. A consequential effect of that is that it obfuscates the problem.

With little information on how to solve such problems, developers turned
to javascript libraries. These things are now considered to be "de
facto" industry standard.

So with all that, lets take a look at a popular javascript library:
jQuery. No problem and no context is presented here, only the
constructor, which is oddly placed in the prototype as
`jQuery.prototype.init`.

It is not necessary to know what it is doing to see that the number of
loops and `if` and `else` statements is quite high. For those who know
the HTML DOM, it is still an eyesore to look at.

| init: function( selector, context ) {
| var match, elem, ret, doc;
|
| // Handle $(""), $(null), or $(undefined)
| if ( !selector ) {
| return this;
| }
|
| // Handle $(DOMElement)
| if ( selector.nodeType ) {
| this.context = this[0] = selector;
| this.length = 1;
| return this;
| }
|
| // The body element only exists once, optimize finding it
| if ( selector === "body" && !context ) {
| this.context = document;
| this[0] = document.body;
| this.selector = "body";
| this.length = 1;
| return this;
| }
|
| // Handle HTML strings
| if ( typeof selector === "string" ) {
| // Are we dealing with HTML string or an ID?
| match = quickExpr.exec( selector );
|
| // Verify a match, and that no context was specified for #id
| if ( match && (match[1] || !context) ) {
|
| // HANDLE: $(html) -> $(array)
| if ( match[1] ) {
| doc = (context ? context.ownerDocument || context :
| document);
|
| // If a single string is passed in and it's a single tag
| // just do a createElement and skip the rest
| ret = rsingleTag.exec( selector );
|
| if ( ret ) {
| if ( jQuery.isPlainObject( context ) ) {
| selector = [ document.createElement( ret[1] ) ];
| jQuery.fn.attr.call( selector, context, true );
|
| } else {
| selector = [ doc.createElement( ret[1] ) ];
| }
|
| } else {
| ret = buildFragment( [ match[1] ], [ doc ] );
| selector = (ret.cacheable ? ret.fragment.cloneNode(true) :
| ret.fragment).childNodes;
| }
|
| return jQuery.merge( this, selector );
|
| // HANDLE: $("#id")
| } else {
| elem = document.getElementById( match[2] );
|
| if ( elem ) {
| // Handle the case where IE and Opera return items
| // by name instead of ID
| if ( elem.id !== match[2] ) {
| return rootjQuery.find( selector );
| }|
|
| // Otherwise, we inject the element directly into the jQuery object
| this.length = 1;
| this[0] = elem;
| }
|
| this.context = document;
| this.selector = selector;
| return this;
| }
|
| // HANDLE: $("TAG")
| } else if ( !context && /^\w+$/.test( selector ) ) {
| this.selector = selector;
| this.context = document;
| selector = document.getElementsByTagName( selector );
| return jQuery.merge( this, selector );
|
| // HANDLE: $(expr, $(...))
| } else if ( !context || context.jquery ) {
| return (context || rootjQuery).find( selector );
|
| // HANDLE: $(expr, context)
| // (which is just equivalent to: $(context).find(expr)
| } else {
| return jQuery( context ).find( selector );
| }
|
| // HANDLE: $(function)
| // Shortcut for document ready
| } else if ( jQuery.isFunction( selector ) ) {
| return rootjQuery.ready( selector );
| }
|
| if (selector.selector !== undefined) {
| this.selector = selector.selector;
| this.context = selector.context;
| }
|
| return jQuery.makeArray( selector, this );
| },

No for loops in that method, but a combined total of 21 if and else
statements over 104 SLOC. That is the entry point for most jQuery
programs and it is one of the simpler and more clearly defined methods.

johnza...@gmail.com

unread,
May 21, 2010, 12:19:21 PM5/21/10
to
On May 14, 5:38 pm, Garrett Smith <dhtmlkitc...@gmail.com> wrote:

Garrett.

Thanks for taking the time to reply.

I tend to agree that many GUIs are not as purely object-oriented as
their marketing teams say they are.

WPF is a good example. I've written several blog posts about design
flaws in WPF[1], and how it allows permiscuous access to effectively
global objects. What is most interesting is that WPF's dependency
change propagation subsystem actually doesn't like random ranges to
global objects, and doing so brings the system to its knees. The
Visual Studio 2010 product team quickly "discovered" this [2]. I also
discuss RIA models on my blog, at least once (that I can remember),
disecting a particular "architecture" I did not like because it had no
way to handle partial failure, or fail gracefully [3]. I also wrote a
blog post about how to design data presentation controls in a modular
fashion, but never really got around to explaining the OO solution of
converting a dynamic behavior problem into a dynamic relationship
participation problem [4]

[1] http://z-bo.tumblr.com/post/113891179/wpf-design-flaws-part-1
[2] http://blogs.msdn.com/visualstudio/archive/2010/03/02/wpf-in-visual-studio-2010-part-2-performance-tuning.aspx
[3] http://z-bo.tumblr.com/post/246854981/what-is-sofea-what-is-soui
[4] http://z-bo.tumblr.com/post/378279345/re-gridview-with-variable-number-of-columns

0 new messages