Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Applying the Open-Closed Principle and Law of Demeter in Object-Oriented Design

Tech 1

Open-Closed Principle

Core Concept

  1. The Open-Closed Principle (OCP) is a foundational tenet of maintainable object-oriented design.
  2. Software components—classes, modules, or functions—should be open for extension but closed for modification. Bheavior changes should occur by adding new code, not altering existing logic.
  3. Abstractions define stable contracts; concrete implementations supply variability. This decouples clients from volatile details.
  4. Most design patterns and SOLID-aligned practices aim to uphold OCP by anabling evolution without destabilizing existing functionality.

Problematic Implementation

public class ShapeRenderer {
    public static void main(String[] args) {
        Renderer engine = new Renderer();
        engine.render(new Box());
        engine.render(new Oval());
        engine.render(new Polygon());
    }
}

class Renderer {
    void render(Shape shape) {
        if (shape.kind == Shape.KIND_BOX) {
            drawBox(shape);
        } else if (shape.kind == Shape.KIND_OVAL) {
            drawOval(shape);
        } else if (shape.kind == Shape.KIND_POLYGON) {
            drawPolygon(shape);
        }
    }

    void drawBox(Shape s) { System.out.println("Rendering box"); }
    void drawOval(Shape s) { System.out.println("Rendering oval"); }
    void drawPolygon(Shape s) { System.out.println("Rendering polygon"); }
}

class Shape {
    static final int KIND_BOX = 1;
    static final int KIND_OVAL = 2;
    static final int KIND_POLYGON = 3;
    int kind;
}

class Box extends Shape {
    Box() { this.kind = KIND_BOX; }
}

class Oval extends Shape {
    Oval() { this.kind = KIND_OVAL; }
}

class Polygon extends Shape {
    Polygon() { this.kind = KIND_POLYGON; }
}

This version violates OCP: adding a new shape (e.g., Star) forces edits to Renderer.render()—a fragile, error-prone change.

Refactored with OCP Compliance

public class ShapeRenderer {
    public static void main(String[] args) {
        Renderer engine = new Renderer();
        engine.render(new Box());
        engine.render(new Oval());
        engine.render(new Polygon());
        engine.render(new Star()); // No changes to Renderer needed
    }
}

class Renderer {
    void render(Shape shape) {
        shape.display(); // Delegated behavior
    }
}

abstract class Shape {
    abstract void display();
}

class Box extends Shape {
    @Override
    void display() { System.out.println("Rendering box"); }
}

class Oval extends Shape {
    @Override
    void display() { System.out.println("Rendering oval"); }
}

class Polygon extends Shape {
    @Override
    void display() { System.out.println("Rendering polygon"); }
}

class Star extends Shape {
    @Override
    void display() { System.out.println("Rendering star"); }
}

Now Renderer depends only on the Shape abstraction. New shapes integrate seamlessly via inheritance and method override—no modifications required.

Law of Demeter

Core Concept

  1. A unit (e.g., class or method) should interact only with its immediate collaborators.
  2. Overly tight coupling increases ripple effects during change and reduces testability.
  3. Also known as the Principle of Least Knowledge, it mandates that an object must not reach into the internals of another object. Encapsulate complexity behind clean interfaces.
  4. A concise phrasing: "Only talk to your direct friends."
  5. Direct friends include classes appearing as:
    • Fields (instance variables),
    • Method parameters,
    • Return types. Classes instantiated or referenced only within method bodies (local variables) are not direct friends—and accessing their internals violates the law.

Practical Implication

The Law of Demeter prioritizes loose coupling—not zero coupling. It discourages chained navigation, such as a.getB().getC().doSomething(), which exposes internal structure and creates brittle dependencies. Instead, delegate responsibility: let a expose a method like a.doSomethingOnC() that internally orchestrates the call chain.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.