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