Introduction to Complex Software Design
- Goal: Build complex software that is easy to modify, extend, and maintain.
- Early approach: Structured programming.
• Influential paper: “gotos considered harmful”. - Modern approach: Object-Oriented Programming (OOP).
• Language support by itself is insufficient; success depends on how tools are used.
- Object-Oriented Design (OOD) supplies guidance for organising code and managing dependencies.
Robert C. Martin ("Uncle Bob")
- Key evangelist for OOD best-practices and agile craftsmanship.
- Major books (all repeatedly cited in industry):
- Clean Code: A Handbook of Agile Software Craftsmanship
- Agile Software Development, Principles, Patterns, and Practices
- Agile Principles, Patterns, and Practices in C#
- The Clean Coder: A Code of Conduct for Professional Programmers
- Blog article that popularised today’s topic: PrinciplesOfOod (butunclebob.com)
The SOLID Acronym (First Five OOD Principles)
- SRP Single Responsibility Principle
- OCP Open/Closed Principle
- LSP Liskov Substitution Principle
- ISP Interface Segregation Principle
- DIP Dependency Inversion Principle
- Motto: “Software development is not a Jenga game”; proper dependency management prevents systems from collapsing when touched.
Single Responsibility Principle (SRP)
- Definition: “A class should have only one reason to change.”
- Cohesion: All elements of a module should be functionally related; unrelated forces of change violate SRP.
- Motivational-poster slogan: “Just Because You Can, Doesn’t Mean You Should.”
Code Walk-Through
- Initial naïve class (Book)
public class Book {
private String name;
private String author;
private String text;
// constructor, getters, setters
}
- Mixing unrelated responsibilities (text processing and output formatting):
void printTextToConsole(String text) { /* formatting + printing */ }
void printTextToAnotherMedium(String text) { /* e.g., PDF */ }
- SRP-compliant refactor: split into
Book
(domain) vs. BookPrinter
(presentation).
Additional Domain Example
Person
and Account
separated rather than embedding account logic inside person.
public class Person { /* identity fields + List<Account> */ }
public class Account { /* account details */ }
Practical Significance
- High cohesion → easier unit testing, clearer mental model, lower merge conflicts.
- SRP violations often manifest as “God classes” or “utility blobs.”
Open/Closed Principle (OCP)
- Definition: “Software entities (classes, modules, functions) should be open for extension but closed for modification.”
- Heuristic side-rules:
• All instance variables \text{should be private}.
• Avoid global variables. - Aim: Ship code that rarely changes yet absorbs new requirements via polymorphism, inheritance, composition, or configuration.
- Poster: “Open-chest surgery is not needed when putting on a coat.”
Guitar Example
public class Guitar { String make; String model; int volume; }
- Extending behaviour without editing
Guitar
:
public class SuperCoolGuitarWithFlames extends Guitar {
String flameColor;
}
- Net effect: New features (flames) delivered while core class remains untouched.
Liskov Substitution Principle (LSP)
- Definition (Barbara Liskov, 1987): “Objects of a superclass shall be replaceable with objects of a subclass without altering correctness.”
- Formal phrasing (informal): If A is subtype of B then anywhere B is expected, an instance of A should behave equivalently.
- Poster: “If it looks like a duck, quacks like a duck, but needs batteries—wrong abstraction.”
Broken Car Example
public interface Car { void turnOnEngine(); void accelerate(); }
ElectricCar
cannot implement turnOnEngine
meaningfully → violates LSP by throwing AssertionError
.- Remedy: split into richer hierarchy, e.g.
Vehicle
, EnginePoweredVehicle
, or apply ISP to segregate “engine” capability.
Consequences
- Base class should be ignorant of its derivatives; subclasses honour invariants, pre-/post-conditions.
- Violations cause runtime surprises, type-checking workarounds, and brittle polymorphism.
Interface Segregation Principle (ISP)
- Definition: “Clients should not be forced to depend upon interfaces they do not use.”
- Avoid “fat” or “polluted” interfaces that couple unrelated clients.
- Poster: “You want me to plug this in, where?”
Bear Zoo Example
interface BearKeeper { washTheBear(); feedTheBear(); petTheBear(); }
interface BearCleaner { washTheBear(); }
interface BearFeeder { feedTheBear(); }
interface BearPetter { petTheBear(); }
- Implementations:
BearCarer
(clean + feed) vs. CrazyPerson
(pet only). Each class depends solely on what it needs.
Benefits & Implications
- Less recompilation, finer access control, easier mocking, safer access to dangerous operations.
Dependency Inversion Principle (DIP)
- Definition: “Depend on abstractions, not on concretions. High-level modules should not depend on low-level modules; both depend on abstractions.”
- Poster: “Would you solder a lamp directly to the wiring in a wall?”
Architectural Insight
- Conventional layering: higher-level (Policy layer) directly calls lower-level (Utility layer) classes → tight coupling.
- DIP refactors such that:
\text{High-level Module} \rightarrow \text{Interface}\leftarrow \text{Low-level Module}
• Abstraction in middle reverses compile-time dependency direction.
Coffee-Machine Example (Stackify article)
- Interfaces:
CoffeeMachine
, EspressoMachine
. - Implementations:
BasicCoffeeMachine
, PremiumCoffeeMachine
. - By coding to interfaces, UI or service layer can swap in different machine objects without change; grinders, brewing units & configuration maps live behind abstractions.
Practical Techniques
- Constructor injection, factory patterns, dependency-injection frameworks (Spring, Guice, Dagger).
- DIP increases reuse opportunities for higher-level policy code.
Cross-Cutting Observations
- SOLID principles are synergistic, not isolated rules. Violating one often leads to violations of others (e.g., SRP ↔ ISP, LSP ↔ OCP).
- These principles are language-agnostic—applicable even without OOP languages via disciplined design (e.g., using modules, pure functions, typeclasses).
- Ethical / Professional angle (Clean Coder): Engineers have responsibility to keep codebase maintainable for future colleagues.
External Resources & Further Reading
- Motivational posters collection: http://lostechies.com/derickbailey/2009/02/11/solid-development-principles-in-motivational-pictures/
- Uncle Bob’s article: http://butunclebob.com/ArticleS.UncleBob.PrinciplesOfOod
- Baeldung tutorial: https://www.baeldung.com/solid-principles
- Stackify DIP case study: https://stackify.com/dependency-inversion-principle/
Key Takeaways for the Exam
- Recognise each SOLID principle’s formal definition, purpose, and code symptoms when violated.
- Be ready to illustrate with concise class diagrams or code snippets (e.g., Book/BookPrinter, Car/ElectricCar).
- Understand interplay: e.g., applying ISP often helps restore LSP; enforcing DIP aids OCP.
- Remember poster metaphors—they’re handy mnemonic devices for quick recall.