Design Principles
High cohesion, low coupling, composition over inheritance, program to interfaces, immutability, and DI.
On this page
These design principles govern how classes relate to one another. Get them right and your system becomes flexible and testable; get them wrong and everything tangles together. They underpin the design patterns you'll meet next.
High cohesion
Cohesion measures how focused a class is. A highly cohesive class has elements that all work toward one clear purpose.
A drawer labeled "screwdrivers" containing only screwdrivers is cohesive - you know exactly what's inside and why. A junk drawer with batteries, receipts, and a spoon is not. Cohesive classes are easy to name, understand, and reuse.
Low coupling
Coupling measures how much classes depend on each other. Low coupling means changing one class rarely forces changes in others.
Loosely arranged cables are easy to rearrange; tangled ones mean tugging one yanks them all. Tightly coupled classes are tangled cables - a change in one drags the rest along. Aim for parts you can move independently.
The goal: high cohesion, low coupling. Focused parts, loosely connected.
Composition over inheritance
Inheritance ("is-a") is rigid and creates deep, brittle hierarchies. Composition ("has-a") - building objects from other objects - is more flexible.
// Inheritance: rigid - what if a duck-like robot needs to fly but not quack?
class Duck extends Bird { }
// Composition: mix in behaviors as parts
class Duck {
private final FlyBehavior fly; // HAS-A a fly behavior
private final QuackBehavior quack; // HAS-A a quack behavior
Duck(FlyBehavior fly, QuackBehavior quack) {
this.fly = fly;
this.quack = quack;
}
}Favor composition
Composition lets you assemble and swap behaviors at runtime, avoids fragile deep hierarchies, and sidesteps the limits of single inheritance. Reach for inheritance only for genuine, stable 'is-a' relationships; otherwise, compose.
Program to interfaces, not implementations
Depend on what something does (an interface), not how (a concrete class). You saw this with collections and Dependency Inversion.
List<String> items = new ArrayList<>(); // depend on List, not ArrayListNow you can switch ArrayList for LinkedList - or a mock in tests - without
touching the rest of your code.
Favor immutability where appropriate
Immutable objects (no setters, final fields) are simpler to reason about,
thread-safe, and safe to share. Prefer them for values - you met this in the OOP
stage.
Dependency Injection
Instead of a class creating its own dependencies, they're given to it from outside. This is Dependency Inversion made practical.
// Without DI: hard to test, tightly coupled
class OrderService {
private final PaymentGateway gateway = new StripeGateway(); // welded in
}
// With DI: dependencies passed in - flexible and testable
class OrderService {
private final PaymentGateway gateway;
OrderService(PaymentGateway gateway) { // injected
this.gateway = gateway;
}
}Rather than a chef growing their own wheat and milking cows (creating dependencies), ingredients are delivered to the kitchen (injected). The chef focuses on cooking. Swap suppliers freely - or hand over fake ingredients for a practice run (tests). Frameworks like Spring do this delivery automatically.
These principles enable the patterns
High cohesion, low coupling, composition, and programming to interfaces are the foundation the design patterns build on. Notice how each pattern in the next lessons is really these principles applied to a recurring problem.
Quick check
You want a class to be easy to test and swap its dependencies. Which approach fits best?
Key takeaways
- Aim for high cohesion (focused classes) and low coupling (few dependencies between them).
- Favor composition (has-a) over inheritance (is-a) for flexibility.
- Program to interfaces, not implementations, so parts stay swappable.
- Favor immutability for values - simpler, thread-safe, shareable.
- Dependency Injection supplies a class's dependencies from outside, improving testability and flexibility.
Now we reach one of software's greatest topics: design patterns. First up, the creational patterns.