OT: Programming Expansibility

Idan Arye via Digitalmars-d digitalmars-d at puremagic.com
Thu Oct 22 09:13:07 PDT 2015


On Wednesday, 21 October 2015 at 14:17:15 UTC, Jeffery wrote:
> *snip*

I think you are looking at it wrong. Object composition should 
either be public or private. If it's public, it should be 
perfectly fine for user code to be fully aware that a `Yours` has 
a `Mine` named `m`. Or, if we look at a more concrete example:

     class Name {
         public string first;
         public string last;
     }

     class Person {
         public Name name;
     }

I see no harm done in user code calling `person.name.first`, 
because a `Name` is just a type, just like `string` or `int` - 
the only difference is that `Name` is user defined.

In these cases, I find it ridiculous for a library wrap all the 
functionality of another library instead of just passing it on. A 
real life example: Java used to have a really crappy datetime 
module in it's standard library(Java 8 got an improved datetime 
module). Someone made a third party datetime library called "Joda 
Time"(http://www.joda.org/joda-time/), which is considerably 
better, and many libraries require it. If these libraries 
followed your rule, they would have to wrap the functions of 
library, so instead of `someEvent.getTime().getMonth()` they'll 
have to implement in the `Event` class a `getMonth` method so you 
could `someEvent.getMonth()`, and the same with all the methods 
in the rich interface provided by Joda Time. Does that seem 
reasonable to you?

Now, while  it's true that the fact that it comes from a third 
party library may make it more prune to bugs and breaking 
changes(which I assume are what you mean by "flaws"), these flaws 
don't really grow exponentially on the chain of indirection. The 
flaw *potential* might grow exponentially, since the number of 
possible chains can grow exponentially, but the number of chains 
actually used if far smaller!

By insisting on a single level of indirection, you are actually 
making things worse:

  - Since you need to wrap methods for the user code, you 
needlessly materialize many possible chains into existence, 
triggering many possible flaws that no one would have to deal 
with otherwise.

  - You make it impossible for users to deal with flaws in 
libraries you depend on. Even if your library should not be 
affected by that flaw, your users now depend on you to deal with 
it even if they should have been able to deal with it themselves.



So, a public composition is a public dependency and should not be 
hidden by the Law of Demeter. How about private composition?


If a composition is private, you should not be able to access it 
via `y.m.foo()` - but not because it's too long an indirection 
chain, but because it's a private member field! The outside world 
should not care that `Yours` has a `Mine` named `m` - this 
composition is supposed to be encapsulated.

The thing is - just like automatically defining getters and 
setters for all member fields breaks encapsulation, so does 
automatically defining proxy wrappers for all the methods of the 
member field. It might solve other problems(like lifetime and 
ownership problems), but it will not achieve the basic purposes 
of encapsulation, like allowing you to change the internal fields 
without affecting users of the outer object. If you change a 
method of the internal object, the methods of the outer object 
will also change.

So, this type of wrapping is not good here either. `Yours` 
shouldn't just have a method for invoking `m`'s `foo()`. If 
`Yours` has a functionality that requires invoking `m.foo()`, the 
implementation of that functionality can call `m.foo()` directly. 
Otherwise, there is no reason for any method of `Yours` to call 
`m.foo()` - certainly not as automatic, thoughtless means to 
allow users - that shouldn't even be aware of `m`'s existence - 
to have access to it.


More information about the Digitalmars-d mailing list