Avoiding Private Methods
Fri 01 June 2018 by Moshe ZadkaAssume MyClass._dangerous(self)
is a private method.
We could have implemented the same functionality without a private
method as follows:
- Define a class
InnerClass
with the same__init__
asMyClass
- Define
InnerClass.dangerous(self)
with the same logic ofMyClass._dangerous
- Make
MyClass
into a wrapper class overInnerClass
, where the wrapped attribute is private. - Proxy all necessary work into
InnerClass
.
This might seem onerous,
but consider that now, dangerous
is part of the public
interface of a class,
and would need to be explicitly documented as to assumptions and guarantees.
This documentation would have had to have been in comments around
_dangerous
anyway --
in order to clarify what its assumptions are,
and what invariants it is violating in MyClass
--
otherwise, maintaining the code that calls _dangerous
would be hard.
Of course,
this documentation is almost certain to be missing.
The existence of _dangerous
itself
implies this was an almost mechanical refactoring
of similar code into a method,
with the excuse of "its private"
used to avoid considering the invariants and interface.
Even if the documentation did exist,
now it is possible to unit-test that the documentation is correct.
Furthermore,
if we use best practices when we define MyClass
--
in other words,
avoid creating an InnerClass
object in the initializer,
and only creating it in an MyClass.from_parameters
,
we are also in a good position to unit test MyClass
.
This,
of course,
presented the worst case:
the code for _dangerous
touches absolutely every data member
of MyClass
.
In real life,
the worst case is not often encountered.
When we look at a private method as a code smell,
and contemplate the best way to refactor it away,
it turns out that we often can find a coherent set of attributes
that really does make sense as InnerClass
on their own merits.
Credit: This is based on an off-handed comment Glyph made in his blog post about attrs. I am also grateful to him for reviewing a draft copy of this post, and making many useful suggestions. All mistakes in interpretation or explanation are mine alone.