I've decided to create a 1 page "cheat sheet" of essential software design principles for my programmers. It doesn't explain the principles in any great depth, but is simply there as a reference and a reminder.
Here's what I've come up with - I would welcome your comments. What have I left out? What have I explained poorly? What is there that shouldn't be?
Basic Design Principles
The Principle of Least Surprise – your solution should be obvious, predictable and consistent.
Keep It Simple Stupid (KISS) - the simplest solution is usually the best one.
You Ain’t Gonna Need It (YAGNI) - create a solution for the current problem rather than what might happen in the future.
Don’t Repeat Yourself (DRY) - rigorously remove duplication from your design and code.
Advanced Design Principles
Program to an interface, not an implementation – Don’t declare variables to be of a particular concrete class. Rather, declare them to an interface, and instantiate them using a creational pattern.
Favour composition over inheritance – Don’t overuse inheritance. In most cases, rich behaviour is best added by instantiating objects, rather than inheriting from classes.
Strive for loosely coupled designs – Minimise the interdependencies between objects. They should be able to interact with minimal knowledge of each other via small, tightly defined interfaces.
Principle of Least Knowledge – Also called the “Law of Demeter”, and is colloquially summarised as “Only talk to your friends”. Specifically, a method in an object should only invoke methods on the object itself, objects passed as a parameter to the method, any object the method creates, any components of the object.
SOLID Design Principles
Single Responsibility Principle – Each class should have one well defined purpose, and only one reason to change. This reduces the fragility of your code, and makes it much more maintainable.
Open/Close Principle – A class should be open to extension, but closed to modification. In practice, this means extracting the code that is most likely to change to another class, and then injecting it as required via an appropriate pattern.
Liskov Substitution Principle – Subtypes must be substitutable for their base types. Essentially, get your inheritance right. In the classic example, type square should not inherit from type rectangle, as they have different properties (you can independently set the sides of a rectangle). Instead, both should inherit from type shape.
Interface Segregation Principle – Clients should not be forced to depend upon methods they do not use. Don’t have fat interfaces, rather split them up into smaller, behaviour centric interfaces.
Dependency Inversion Principle – There are two parts to this principle:
High-level modules should not depend on low-level modules. Both
should depend on abstractions.
Abstractions should not depend on details. Details should depend on abstractions. In modern development, this is often handled by an IoC (Inversion of Control) container.