This week I needed to extract a little piece of computational geometry, imbedded in a single Python class of application specific logic. Therefore I proceeded to create a second class and migrate the computation geometry code over to it. On of the pleasures of Python is the ability to do this type of refactoring at low cost, and pretty much follow a recipe. Which I include here:
- Start with class A
- Create new class B, derive A from B
- Move part of A.__init__ to B.__init__
- Move initial subset of member functions A to B, implicitly creating a first API between A and B (API A-B)
- Iterate and apply one or more of:
- Split / merge methods within API A-B
- For A still accessed directly (through self) by B, extend API A-B to provide additional A content.
- Find flow/bundle/fiber/ data of A and B, refactor these to be within new classes C0, C1, .... Alternatively refactor them to be within additional Numpy axes (e.g. for ML applications).
- Move selected Cs to from A to B or B to A. If appropriate adapt API
- Move selected Cs into API A-B or take out of the API
- Find variants within the Cs. Bring these under new protocol classes P0, P1, ... Adapt API A-B to depend on Ps. Alternatively, find Numpy axes with shared "basis", and refactor towards abstract shape/axes properties, then adapt API.
- Stop when all traces of A have been removed from B, and the API A-B is generic enough for the desired future usage of B.
Notes:
- "Flow data" are typically found in arguments to functions.
- Writing generic axes logic in Numpy is super hard as Numpy gives little help for pure pointfree-style functional code. However it can be done, and you can for example write 1d, 2d, and 3d logic as just one common piece of Numpy code.
- I was going say always retest as you refactor. Yet in my usage above I did not, and regretted it when my first test failed after a few hours of work. Given that I had "changed much", I quickly reapplied my changes to the original code, but this time running tests each time.
All original content copyright James Litsios, 2021.