Coupling of objects

D

dover

It has be often mentioned that the coupling among objects should be avoided.

What're the major cases that objects are closed coupled?

How to avoid such cases?

Many thanks!
 
P

Phlip

dover said:
It has be often mentioned that the coupling among objects should be
avoided.

Define coupling. All authors seem to "know it when they see it".
What're the major cases that objects are closed coupled?

I (authoritatively) define coupling as "A must change only because B
changed."

Put another way, I associate my definition with a transition. Staring at a
static instance of A and B won't tell if a given change, C, will reveal
coupling. Attempting the change will reveal it.

The other kind of association is "coherency". That essentially means "good
coupling". I define it as "A and B share legitimate reasons to change."
How to avoid such cases?

Write lots of unit tests. Write them before writing the tested code, and
make them fail before making the tested code pass. Then refactor, testing
every few edits, until your design contains no duplicated definitions of
behavior.

Those forces overwhelmingly crush out all possible coupling from your
design. Under such a system you honestly needn't fret about coupling; it
becomes a non-issue, like bugs.

For example:

int main()
{

Source aSource("a b\nc, d");

string
token = aSource.pullNextToken(); assert("a" == token);
token = aSource.pullNextToken(); assert("b" == token);
token = aSource.pullNextToken(); assert("c" == token);
token = aSource.pullNextToken(); assert("d" == token);
token = aSource.pullNextToken(); assert("" == token);
// EOT!
}

That's just one puny test, lacking a test rig such as CppUnit. But it
already proves a very important aspect of the class Source's coupling.

You can construct a Source object using only main() and a string argument.
You are not required to construct or deploy or call or instantiate or
register or jump-thru-hoops any other objects, just to get a useful Source
going.

The complete application that code grew into appears here:

http://www.c2.com/cgi/wiki?MsWindowsResourceLint

I would be interested to learn if anyone thought any of my classes in there
were coupled. Or incoherent.
 
D

David Rubin

dover said:
It has be often mentioned that the coupling among objects should be avoided.

What're the major cases that objects are closed coupled?

1. inheritance
e.g., class A : public class B {};

2. friendship
e.g., class B; class A { friend class B; };

3. substantive use of class B in the interface of class A
e.g. A::foo(B b);

4. substantive use of class B in the implementation of class A
e.g., A::foo() { B b; }
How to avoid such cases?

See Lakos.
Many thanks!

/david
 
D

David Rubin

[snip]
I (authoritatively) define coupling as "A must change only because B
changed."
s/change/re-compile/

[snip]
How to avoid such cases?

Write lots of unit tests. Write them before writing the tested code, and
make them fail before making the tested code pass. Then refactor, testing
every few edits, until your design contains no duplicated definitions of
behavior.

To me, this approach encourages you to design to the unit test suite
rather than to the problem domain. I have never had a good experience
writing tests before I write the interface, but I guess it's good to
experiment.
Those forces overwhelmingly crush out all possible coupling from your
design. Under such a system you honestly needn't fret about coupling; it
becomes a non-issue, like bugs.

For example:

int main()
{
Source aSource("a b\nc, d");
string token;
token = aSource.pullNextToken();
assert("a" == token);

FWIW, I find it useful to define and use ASSERT (or redefine assert())
so that it doesn't stop the program execution at each failed
assertion. Otherwise, you may have to run the test driver several
times to solve one class of error.

/david
 
P

Phlip

David said:
Phlip wrote:

[snip]
I (authoritatively) define coupling as "A must change only because B
changed."

s/change/re-compile/

That is an artifact of how C++'s compilation model weds most kinds of
coupling to the recompile system. Put another way, you can build very large
systems in C++ without excess recompiles if you take care to follow good OO
design principles such as "program to the interface, not to the
implementation".

But you can still couple A and B unfairly, but A _doesn't_ recompile when B
changes. It just breaks.
[snip]
Write lots of unit tests. Write them before writing the tested code, and
make them fail before making the tested code pass. Then refactor, testing
every few edits, until your design contains no duplicated definitions of
behavior.

To me, this approach encourages you to design to the unit test suite
rather than to the problem domain. I have never had a good experience
writing tests before I write the interface, but I guess it's good to
experiment.

One designs them both, together, in tiny increments. One doesn't write many
test cases up front.
FWIW, I find it useful to define and use ASSERT (or redefine assert())
so that it doesn't stop the program execution at each failed
assertion.

Yes! ASSERT() should invoke a breakpoint expanded in the calling function,
not deep in the C++ Standard Library.
Otherwise, you may have to run the test driver several
times to solve one class of error.

No! If you run the tests every 1~10 edits, and predict the results of each
run, then any unexpected failure is cause to simply Undo the most recent
edits.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top