1. Separating out the things that change from those that stay the same.
Obviously, we do it to avoid the tight coupled code, get it easy to make changes with less cost. At first time when we put down your fingers to write methods, we should recognise what change from what stay the same. For example:
class Animal def make_sound end end
As you can see, the make_sound method here will be changed regularly. It relies on what kind of animal. The animal could be dog or cat or bird. They have different sounds. So, at first we notice that make_sound is changeable. Hence, we pull out this method to isolate it from the certain code.
2. Program to an interface, not an implementation
If we have the tree of inheritances like this A -> B -> C -> D -> E -> F -> ... Z. If A can do the same things, we shouldn't involve methods of B ... Z (A is superclass).
- Carefully abstract out all the important functionality into many separate interfaces.
- Program to the most general type you can
- Reason ruby doesn't have interfaces -> encourage to program to interfaces. If B inherits A, if A can do things for B so involve methods of A instead of B.
- Reduce the amount of coupling in our code.
# challence car = Car.new car.drive(100) plane = Plane.new plane.fly(1000) # solution vehicle1 = Vehicle.new vehicle1.traval(100) vehicle2 = Vehicle.new vehicle2.traval(100)
3. Prefer composition over inheritance
- Inheritance tends to marry the subclass to the superclass, leading to bound the common implementation core (x)
- Superclass is shown in subclass, leading to tight coupled (x)
- Is kind of (x) => has (o)
- Abstracting common methods at base class and refer to #2 if we couldn't avoid to use inheritance
# Challenge class Vehicle # All sorts of vehicle-related code... def start_engine # Start the engine end def stop_engine # Stop the engine end end class Car < Vehicle def sunday_drive start_engine # Cruise out into the country and return stop_engine end end # Solution class Engine # All sorts of engine-related code... def start # Start the engine end def stop # Stop the engine end end class Car def initialize @engine = Engine.new end def sunday_drive @engine.start # Cruise out into the country and return... @engine.stop end end
Handing problems off to other objects. Take a look on code at #3, we can see that Car delegates object @engine to do things.