Less APIs or more encapsulation?

Bruno Desthuilliers bruno.42.desthuilliers at websiteburo.invalid
Thu Sep 10 03:49:32 EDT 2009


一首诗 a écrit :
> 2 class,  B contains C. 

Given your code snippet, I assume you meant that B instances have an 
instance of C as attribute.

>  When user want to use some service of C,

s/C/instances of C/


Python's classes are objects by themselves, so it's really important to 
make clear whether you're talking about the class object or instances of 
the class.

> there are two choice:
> 
> First, more encapsulation:
> 
> =============================
> class B:
>     def newMethod(self):
>         self.c.newMethod()

Don't expose 'c' as part of the API if you're after "more 
encapsulation". Reminder : by convention (and it's a really strong 
convention), attribute names starting with an underscore are 
implementation ('private' if you want to stick to mainstream jargon).

> class C:
>     def newMethod(self):
> 
>       #do something
>         pass
> 
> b.newMethod()
> 

yeps, composition/delegation. Classic scheme.

> Sencond : Call seice of f c directly:
(snip)
> b.c.newMethod()
> 
> 
> 
> Generally, what I learned from books told me that 1st choice is
> better.

I _usually_ is, unless it's B's responsability to provide access to a C 
instance. But then, it's usually written as:

b = B()
c = b.get_c()
c.some_method()

This scheme is usually found when there's a need to connect to an 
external resource. A canonical example can be found the DB API, where 
you first open a connection to the database, then ask the connection for 
  one (or more) cursor(s).

> But when C has many many methods to expose to outer user, 2nd choice
> seems to be more reasonable

Until you need to modify your implementation, or to have some code 
around the calls to methods of c, etc... The (potential) problem with 
the second scheme is that it may expose a bit too much of the 
implementation.

Hopefully, this may not be _that_ tragic with Python, since you can 
easily replace a direct attribute access with a computed one without 
impacting the client code - but not without paying some overhead (which 
FWIW you would have from the start using proper delegation).

> I In the first design, B.newMethod did
> nothing really useful.

Nope, or at least not directly. But then, why use the obj.method() 
syntax when C.method(c) would work too ?-)

More seriously: in lower-level, more static languages where you don't 
have the hand on attribute lookup, getting "proper encapsulation" right 
from the start is _really_ important. Python is more forgiving here 
since you have ways to customize attribute resolution. *But* it doesn't 
mean you should not pay attention to proper encapsulation.

> ctaully, , there are D/E/F, etc. in B we methodhod has to be exposed
> to user code,  which makes encapsulation more tedious.

In most languages, yeps. Good news, Python exposes enough of it's 
implementation to allow easy automation of the delegation. Read about 
the __getattr__ magic method.

Also and FWIW, writing dummy getters and setters for a dozen attributes 
is just as boring, and that's still the commonly accepted "best 
practice" in Java, C++ and most other mainstream "OO" languages.

> 
> In fact, these classes,  C/D/E/F all have different jobs, but they all
> belongs to a real world object "B", B is only a container of C/D/E/F.

What is a "real world" object ?

And if B is only here to provide (almost direct) access to instances of 
C/D/E/F, do you really need B ? (open question - you may actually need 
this level of indirection for pretty good and legitimate reasons... but 
one can't tell without knowing way more about your concrete use case)

> What do you think about it?

There are GoldenRules(tm) and BestPractices(tm). And then there's 
concrete application of all these things, and that's where the fun begin.

Blindly applying GoldenRules and BestPractices would border on cargo 
cult thinking. The real point behind GoldenRules etc is to draw your 
attention to known problems and the possible drawbacks of some 
conception/implementation choices, so you can make use of your own 
*informed* judgement wrt/ the concrete problem you're trying to solve.

IOW : there's no silver bullet, just a set of sound advices based on 
experience. Then it's up to you to decide which solution you think is 
the more appropriate here and now.



More information about the Python-list mailing list