Object-oriented concepts and Design principles
Core object-oriented concepts
- Inheritance
- Encapsulation
- Polymorphism
- Abstraction
Core design principles
A design principle sits underneath a design pattern.
- Encapsulate what varies
- Favor composition over inheritance
- Program to interfaces not implementation
- Strive for loose coupling
SOLID principles
- Single responsibility
- Open/closed
- Liskov substitution
- Interface segregation
- Dependency inversion (a high level module should not depend on a low level module)
Difference between design patterns and design principles
Design principles are general guidelines that can guide your class structure and relationship
Design patterns are tried-and-true solutions aimed at commonly occurring problems.
Encapsulate what varies (principle 1)
Look for code that is always changing and encapsulate it in a separate class.
For example:
// Varying if (...) obj = new objType1(); else if (...) obj = new objType2(); else obj = new objType3(); // Not varying obj.method1(); obj.method2(); obj.method3();
Move the varying code into a factory class.
Favor composition over inheritance (principle 2)
“Has a” is better than “Is a”.
e.g. Dog has an owner, taxi has a passenger.
e.g. CoffeeWithAButter is-a Coffee OR Coffee has-a Condiment
Using has-a design, you can add any number of condiments easily at runtime. This avoids class duplication.
The inheritance relationship is built at compile time but the composition relationship can be dynamically adjusted at runtime.
Strive for Loose coupling (principle 3)
Loose coupling: Components have little knowledge about other components. Naturally reduces dependency between objects.
Try to avoid making your dependency on a concrete class. Program to an interface, not an implementation.
e.g. a class depending on LCD screen and its method for printing. Instead, it can use an abstract object, which at run time can be LCD screen or Widget.
Program to an interface not implementation (principle 4)
Program to a super-type.
Eg.
obj1.elem = new concreteClass(); // Too rigid
Note that we always have to create a new instance of a concrete class. But, starting with an abstract interface pointer allows for the flexibility to choose an appropriate concrete implementation at run time and exploit polymorphism.