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
InnerClasswith the same__init__asMyClass - Define
InnerClass.dangerous(self)with the same logic ofMyClass._dangerous - Make
MyClassinto 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.