template-method-pattern

1 posts

line

Code Quality Improvement Techniques Part 19: Child Lock (opens in new tab)

The "child lock" technique focuses on improving code robustness by restricting the scope of what child classes can override in an inheritance hierarchy. By moving away from broad, overridable functions that rely on manual `super` calls, developers can prevent common implementation errors and ensure that core logic remains intact across all subclasses. This approach shifts the responsibility of maintaining the execution flow to the parent class, making the codebase more predictable and easier to maintain. ## Problems with Open Functions and Manual Super Calls Providing an `open` function in a parent class that requires child classes to call `super` creates several risks: * **Missing `super` calls:** If a developer forgets to call `super.bind()`, the essential logic in the parent class (such as updating headers or footers) is skipped, often leading to silent bugs that are difficult to track. * **Implicit requirements:** Relying on inline comments to tell developers they must override a function is brittle. If the method isn't `abstract`, the compiler cannot enforce that the child class implements necessary logic. * **Mismatched responsibilities:** When a single function handles both shared logic and specific implementations, the responsibility of the code becomes blurred, making it easier for child classes to introduce side effects or incorrect behavior. ## Implementing the "Child Lock" with Template Methods To resolve these issues, the post recommends a pattern often referred to as the Template Method pattern: * **Seal the execution flow:** Remove the `open` modifier from the primary entry point (e.g., the `bind` method). This prevents child classes from changing the overall sequence of operations. * **Separate concerns:** Move the customizable portion of the logic into a new `protected abstract` function. * **Enforced implementation:** Because the new function is `abstract`, the compiler forces every child class to provide an implementation, ensuring that specific logic is never accidentally omitted. * **Guaranteed execution:** The parent class calls the abstract method from within its non-overridable method, ensuring that shared logic (like UI updates) always runs regardless of how the child is implemented. ## Refining Overridability and Language Considerations Designing for inheritance requires careful control over how child classes interact with parent logic: * **Avoid "super" dependency:** Generally, if a child class must explicitly call a parent function to work correctly, the inheritance structure is too loose. Exceptions are usually limited to lifecycle methods like `onCreate` in Android or constructors/destructors. * **C++ Private Virtuals:** In C++, developers can use `private virtual` functions. These allow a parent class to define a rigid flow in a public method while still allowing subclasses to provide specific implementations for the private virtual components, even though the child cannot call those functions directly. To ensure long-term code quality, the range of overridability should be limited as much as possible. By narrowing the interface between parent and child classes, you create a more rigid "contract" that prevents accidental bugs and clarifies the intent of the code.