How to refactor a Python “god class”?

Posted by Zearin on Programmers See other posts from Programmers or by Zearin
Published on 2012-03-28T16:45:44Z Indexed on 2012/03/28 17:43 UTC
Read the original article Hit count: 407

Filed under:
|
|

Problem

I’m working on a Python project whose main class is a bit “God Object”. There are so friggin’ many attributes and methods!

I want to refactor the class.

So Far…

For the first step, I want to do something relatively simple; but when I tried the most straightforward approach, it broke some tests and existing examples.

Basically, the class has a loooong list of attributes—but I can clearly look over them and think, “These 5 attributes are related…These 8 are also related…and then there’s the rest.”

getattr

I basically just wanted to group the related attributes into a dict-like helper class. I had a feeling __getattr__ would be ideal for the job. So I moved the attributes to a separate class, and, sure enough, __getattr__ worked its magic perfectly well…

At first.

But then I tried running one of the examples. The example subclass tries to set one of these attributes directly (at the class level). But since the attribute was no longer “physically located” in the parent class, I got an error saying that the attribute did not exist.

@property

I then read up about the @property decorator. But then I also read that it creates problems for subclasses that want to do self.x = blah when x is a property of the parent class.

Desired

  • Have all client code continue to work using self.whatever, even if the parent’s whatever property is not “physically located” in the class (or instance) itself.
  • Group related attributes into dict-like containers.
  • Reduce the extreme noisiness of the code in the main class.

For example, I don’t simply want to change this:

larry = 2
curly = 'abcd'
moe   = self.doh()

Into this:

larry = something_else('larry')
curly = something_else('curly')
moe   = yet_another_thing.moe()

…because that’s still noisy. Although that successfully makes a simply attribute into something that can manage the data, the original had 3 variables and the tweaked version still has 3 variables.

However, I would be fine with something like this:

stooges = Stooges()

And if a lookup for self.larry fails, something would check stooges and see if larry is there. (But it must also work if a subclass tries to do larry = 'blah' at the class level.)

Summary

  • Want to replace related groups of attributes in a parent class with a single attribute that stores all the data elsewhere
  • Want to work with existing client code that uses (e.g.) larry = 'blah' at the class level
  • Want to continue to allow subclasses to extend, override, and modify these refactored attributes without knowing anything has changed


Is this possible? Or am I barking up the wrong tree?

© Programmers or respective owner

Related posts about design

Related posts about python