Coupling, Cohesion and Demeter

OOP · lesson 39 of 43 · 3 min read

Tell an object what you want instead of walking through its fields, and keep each class about one thing.

Open this lesson in the learning hub

Key points

  • Cohesion is how much a class is about one thing. Coupling is how much it leans on the shape of others.
  • The Law of Demeter: talk to neighbours, not strangers. One dot per call, not a.getB().getC().getD().
  • Every extra dot adds a class you depend on. Change any one of them and this line breaks, although it owns none of them.
  • Tell, do not ask: move the behaviour onto the object that holds the data, and call one method that means something.
  • Getter chains are the usual sign of low cohesion: the logic ended up outside the class that has the data.
  • Builders and streams break the letter of the rule and not the spirit, because each call returns the same kind of thing.

Example

public class Main {

    record Money(int cents) {
        Money minus(int amount) { return new Money(cents - amount); }
        boolean covers(int amount) { return cents >= amount; }
    }

    static final class Wallet {
        private Money money;

        Wallet(int cents) { this.money = new Money(cents); }

        // TELL, do not ask: the wallet decides, because the wallet owns the money.
        boolean pay(int amount) {
            if (!money.covers(amount)) return false;
            money = money.minus(amount);
            return true;
        }

        int balance() { return money.cents(); }
    }

    static final class Customer {
        private final Wallet wallet;

        Customer(int cents) { this.wallet = new Wallet(cents); }

        // One forwarding call. Nobody reaches through the customer to the wallet.
        boolean pay(int amount) { return wallet.pay(amount); }

        int balance() { return wallet.balance(); }
    }

    public static void main(String[] args) {
        Customer customer = new Customer(500);

        // The Demeter violation would read like this, and it needs a public
        // Wallet, a public Money and a setter before it even compiles:
        //     customer.getWallet().getMoney().setCents(...)

        System.out.println("paid 200? " + customer.pay(200));
        System.out.println("paid 500? " + customer.pay(500));
        System.out.println("balance   " + customer.balance());
    }
}

Ask the owner to do the job; a chain of getters is a smell, not a shortcut.

This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the OOP course, and every lesson in it is listed on the OOP contents page.