Inheritance is when one object inherits behavior from another object, and can change that behavior if needed; it is a mechanism for code reuse.
Interfaces are one way of implementing inheritance. Subobjects inherit from a base object. Anytime you’re writing code that interacts with these objects, you can write code that interacts with the interface or write code that interacts with the subobjects. You should always favor writing code that interacts with the interface, not the implementation. Doing so means your code will work with all of the subobjects instead of just one.
A single interface to entities of different types. For example, when one object inherits from another, polymorphism allows the subclass to stand in for the superclass. See http://en.wikipedia.org/wiki/Polymorphism_(computer_science).
Factor out details from a common pattern so that you can work close to the level of human thought, leaving out details which matter in practice, but are exigent to the problem being solved. In practice, take code that is common between two objects and put it in a single place.
Encapsulation is used to hide the implementation of an object in such a way that it is easy to use and easy to change. The object acts as a black box that provides a service to its users, but does not open up the code so someone can change it or use it the wrong way. Any time you have behavior in an application that you think is likely to change, move that behavior away from parts of your application that won’t change very frequently. In other words, encapsulate what varies.
Object-Oriented Design Principles
- Encapsulate what varies
- Code to an interface rather than to an implementation
- Each object in your application should have only one reason to change. Minimize the chances that your objects are going to have to change by reducing the number of things in the object that can cause it to change.
- The Open-Closed Principle (OCP) – Allow change but don’t allow modification. Inheritance is an example of this. Extending a class allows someone to change behavior without modifying the base class. Composition is another example.
- DRY (Don’t Repeat Yourself) – Avoid duplicate code by abstracting out things that are common and placing them in a single location.
- The Single Responsibility Principle (SRP) – Each object should have only one responsibility, and all the object’s services should be focused on that single responsibility. You can spot classes that aren’t using the SRP with a simple test:
- On a sheet of paper, write down a bunch of lines like this: The ______ _______ itself. You should have a line like this for every method in the class you’re testing for the SRP.
- In the first blank of each line, write down the class name; in the second blank, write down one of the methods in the class. Do this for each method in the class.
- Read each line out loud (you may have to add a letter or word to get it to read normally). Does what you just said make any sense? Does your class really have the responsibility that the method indicates it does? Take all of the methods that don’t make sense on a class and move them to classes that do make sense for that particular responsibility.
- The Liskov Substitution Principle (LSP) – Subtypes should be substitutable for their base types. When you find code that violates the LSP (subtypes that aren’t substitutable for their base types) consider using delegation, composition or aggregation to use behavior from other classes without resorting to inheritance.
- Delegation means invoking a method on a different object to accomplish some behavior you are interested in. Let the other class do the work.
- Composition lets you choose a behavior from a family of behaviors, often via several implementations of an interface. The composing object ‘owns’ the behaviors it uses, and they stop existing as soon as the composing object does. In other words, the containing object instantiates a member object and assigns it to a member variable…when the containing object stops existing so does all of its member variables.
- Aggregation allows you to use behaviors from another class without limiting the lifetime of the other class. The containing class has a reference to an object that has been instantiated on its own outside of the containing class. When the containing class is destroyed, only the reference is destroyed. For example, passing a reference to an existing object into the containing object’s constructor and assigning the reference to a member variable.