A PIE
What does it stand for?
[ A ] bstraction
[ P ] olymporphism
[ I ] nheritance
[ E ] ncapsulation
Abstraction
We create abstractions to simplify our program domain. They allow us to focus on the problem we are solving rather than getting lost in minute details. You don't need to know how every subsystem in your car works in order to drive, do you?
Think about writing code as the process of creating a language to describe a problem we're solving. Abstractions are the building blocks of this language.
In our code
We are creating abstractions in the small whenever we:
- name a variable
- create (and name) a method
- create (and name) a class
- simplify an attribute by representing it as a number (hunger) or a label ("purple")
This is why naming is so important: we need to accurately convey the abstraction we're describing to those that follow (as well as our later selves).
We apply the other OO principles (PIE) to create meaningful and useful abstractions of more complex concepts.
Polymorphism
the quality or state of existing in or assuming different forms
Polymorphism allows us to represent intent, but allow the implementation to vary as needed based on context.
In our code
Method overloading
A method with the same name that accepts different argument types.
Examples:
Console
'sWriteLine
methods.- XUnit's numerous
Assert.Equal
methods
Both of these methods accept string
s, int
s, decimal
s.
In our code
Method overriding
A method that redefines a method from a base class is said to override that method. A great example of this are the ToString
, Equals
, and HashCode
methods from System.Object
that you may have seen. (Remember, Object
is the base class of all classes.)
Base classes and interfaces
We also implement polymorphism by:
- extending a base class (a
String
isAObject
) - implementing an interface (a
List<T>
isAIList<T>
isAIEnumerable<T>
)
Inheritance
Inheritance is the mechanism whereby a class inherits behavior from a base class. We say the class is extending the base class. Recall that all classes, whether we tell them to or not, implicitly extend Object
.
Sometimes we create types (classes) that only exist so that they may be extended. We use the keyword abstract
to create these abstract classes, which may also declare abstract
methods.
The System.Collections
namespace has several great examples of using inheritance and polymorphism: the List
s, Enumerable
s and Map
s that you know and love.
In our code
Inheritance is what allows String
concatenation to work without us doing anything extra (though perhaps it ain't pretty). Like when we do this:
VirtualPet pet = new VirtualPet();
Console.WriteLine("My pet is " + pet);
What happens behind the scenes is that the ToString()
method of Object
(VirtualPet
's base class) is being called. VirtualPet
has inherited this method from its base.
Encapsulation
Encapsulation at its simplest is hiding away information that isn't necessary to share. The more knowledge exposed to the outside world about an object, the more opportunity for coupling there is.
Coupling makes software difficult to change.
In our code
public class Circle
{
private double radius;
public double Radius
{
get { return radius; }
}
}
The radius
instance variable above is private and exposed via a property's getter (Radius
). Not only does this avoid radius
being manipulated externally and possibly resulting in an invalid state, but also gives us the flexibility to implement Radius
in a different way if necessary. The code that asks our Circle
object for the radius doesn't need to know how the radius is being determined.
This is encapsulation. A more complex example of encapsulation would be an object responsible for calculating sales tax for a transaction. If the formula changes, yet it is encapsulated within the object, other objects requesting the tax calculation need not change. This would also likely involve polymorphism, since sales tax calculations vary by a number of factors, including location and product type.