Welcome to Day Two of this “30 Days of TDD” series. If you’ve never done Test Driven Development or aren’t even sure what this "crazy TDD stuff” is all about than this is the series for you. Over the next 30 days this series of posts take you from “I can spell TDD” to being able to consider yourself a “functional” TDD developer. Of course TDD is a very deep topic and truly mastering it will take quite a bit of time, but the rewards are well worth it. Along the way I’ll be showing you how tools like JustCode and JustMock can help you in your practice of TDD.
In the first few posts of this series I’ll be reviewing a few basic software design and development concepts. These form a foundational basis for the application of TDD not only as a development methodology, but as a software design methodology. These posts will ensure that we are all starting off on the same basic page before leaping into more complicated and advanced topics.
Previous Posts in this series:
In today’s post I’ll be doing a quick review of principals of Object Oriented Programming (OOP). This post will help frame future posts in this series by ensuring that we are all on the same page in terms of our understanding of software development. Even if you are seasoned developer I recommend that you at least read the section on Polymorphism and Interfaces as these topics are widely misunderstood and are crucial to building good TDD practices.
What is OOP?
Object Oriented Programming (OOP) is a software development technique where a developer will create an abstraction (in code) of a real world object or concept. The belief is that if you can create a high enough fidelity model between the real world objects and the objects in your code, writing applications to automate the usage and automation of those objects will be easier. This approach has been mostly successful and is the most common software development paradigm that you will see in building Line of Business (LOB) applications.
When developing applications using OOP, you will define Classes that model real world objects. In your application these classes will be instantiated into Objects. In the course of the applications lifetime your code will call methods on these objects and potentially pass them to other methods as arguments. The interaction between classes is controlled be each classes public API, otherwise known as the collection of public methods and properties on the class. As we move into code samples in further posts you will see classes being created and methods on them being tested.
The terms “class” and “object” are NOT interchangeable. A Class is a definition of what an object should look like. It’s the blueprint you create by writing the C# or VB.NET that has the class’s member variable and methods. An Object is an instantiation of a Class. When you declare a variable of type class and either accept an instantiated object or you create an object using the “new” keyword, you get an Object based on the Class that was used to create the object.
OOP has three major tenets; Encapsulation, Inheritance and Polymorphism
Encapsulation is often referred to as the “Black Box” rule. It states that the internal state of an object should be protected and inaccessible from external entities. This means that external code cannot directly see or change the state of an object. Any access to the internals must occur through a public API. This enables the object to ensure that its internal state is always valid; meaning that the internal state can only be changed via a public method. Using the public API ensures that all workflow and validation rules are applied to said change.
When the internal state of an object is changed without checking these rules and running these workflows it’s possible for the object to become invalid. This means that the internal state has not been validated or that steps in a workflow may have been done in an incorrect order or skipped altogether. As an OOP developer it’s important to keep all of my internal state either private and only allow access through public methods for external classes and protected methods for classes that inherit from your class.
The corollary to the “Black Box” rule is that as a consumer of an object I should not have to concern myself with the internal workings of the object I’m consuming. I only need to understand how the public interface works and I can be assured that anything I try to make the object do will be vetted by the objects internal validation logic and protected from being used incorrectly. I am basing this on the concept that the developer of said class has verified the functionality of the class with tests. This is another good reason to use TDD; we can insure that the classes we are exposing for use are the best and most accurate that they can be.
So how does Encapsulation relate to TDD? Tests are consumers of objects just like any other code. As a result a test should only have access to the public API of an object and not its internals. This is an important concept for new practitioners of TDD; you are testing the public API, not the private or protected methods. As you’ll see in future posts, the private and protected methods should exist only to serve the public API and their existence is driven by the need of that public API. Therefore you do not need to write tests specifically for private or protected methods; they are tested via the test of the public API.
As you develop your application by building classes, you start to find that certain classes can seem to have relations to other classes. Sometimes two or more classes will seem very similar to each other. For example, in Figure 1 you can see that I have three classes; Car, Plane and Boat that appear to have several similarities:
Figure 1 – A class diagram showing the Plane, Car and Boat classes
While each of these represent a very different type of vehicle, we can see that they do have quite a bit in common. Instead of duplicating this functionality several times, it would make more sense to put it in one place where it can be used by all of these objects. This is where Inheritance comes in. With Inheritance I can define a base class (in this case called Vehicle) and have Plane, Car and Boat inherit from it. This creates a parent/child relationship as shown in Figure 2:
Figure 2 – Plane, Car and Boat now inherit from the Vehicle base class
Now the Plane, Car and Boat classes can inherit the functionality for the three properties (FuelCapacityInGallons, PassengerCapacity and RangeOnFullTankInMiles) from the base class Vehicle. If the functionality provided by Vehicle is sufficient for each particular derived class (the classes that inherit from the Vehicle base class) than the developer does not have to do any thing else. However if a derived class needed a different type of functionality for one of the properties defined in the base class, they can easily override the functionality from the parent and provider their own as shown in Figure 3:
Figure 3 – Plane now provides its own implementation of RangeOnFullTankInMiles
Now calling the RangeOnFullTankInMiles property on the Plane class uses its specific version of the property, while Car and Boat still rely on the implementation provided by the base class.
For most developers the concepts of Encapsulation and Inheritance are pretty easy to understand. Polymorphism is the concept that most developers struggle with and have difficulty understanding. This is unfortunate as a lot of the real power of OOP comes from its polymorphic abilities. Polymorphism is the concept that although two classes may be similar in that that share a similar set of behaviors, and based on those shared behaviors can be treated as identical, they can implement that behavior very different ways.
For example, in Figure 3 above, each of the vehicles inherit from Vehicle, but they move in different ways; cars drive on the road, boats float in the water and planes fly in the air. But they are all vehicles and if I was writing a method that required a vehicle, but not a specific type of vehicle, I could accept a type of Vehicle as a parameter and the caller could pass in a Car, Plane or a Boat. It doesn’t matter what the concrete type is, so long as it inherits from Vehicle it would be acceptable.
In fact my class wouldn’t even necessarily need to know what specific type of vehicle was passed. Since it inherits from Vehicle I already know everything about it I need, which in this case would be the public methods and properties (the API) of the Vehicle base class. I can call any of the public methods or properties defined on the Vehicle base class. If the concrete class I received does not have its own implementation of that method then the method from the base class would be used. If the concrete class does have an implementation then that implementation from the derived base class would be used instead, even though as far as my code knows I just have a generic Vehicle.
This is a very powerful concept as it adds to my code the ability to embrace the idea of generalization. I don’t need to create a method for each type derived from the Vehicle class, I just need to accept an instance of the Vehicle class and I can work with any class derived from it. What I cannot do is use methods that are declared solely on a specific base class. For example, if I had a method on Car called OpenTrunk that is not defined in Vehicle I would have to try to cast my object to type Car before I could call this method. Generally speaking if you find yourself doing a lot of this type of casting you may have a flaw in your design; either your base class is not general enough or it’s too general for the method you’re trying to write.
Polymorphism based on Inheritance isn’t always the best option. For example, I have the following class hierarchy Figure 4 (I have removed Car and Boat and the properties used in the previous example to keep the diagram easier to read):
Figure 4 – The Bird and FlyingSquirrel class are introduced
Looking at the three classes in the bottom row (Plane, Bird and FlyingSquirrel) it’s pretty obvious that these are all things that fly, but they do so in very different ways. These classes share one behavior, but not much else. Two of them are animals, one is not. Two of these move by self-powered flight, the other one glides. Trying to shoehorn these three object into the same class hierarchy would be a mistake. It would be VERY difficult to find an abstraction where that would make sense.
The good news is that we don’t have to try. We can use an Interface. An Interface is simply a contract that states that your code will implement and support a specific public API. Basically it’s a list of public methods and properties that you promise to have defined on your class. How this functionality is implemented is not part of the contract, just that the methods and properties are there and can be called.
To apply this to the current problem at hand, I can create an interface called IFly with a method called Fly. The next step is to have Plane, Bird and FlyingSquirrel implement the IFly interface as shown in Figure 5:
Figure 5 – The IFly interface has been implemented
As each of these implements the IFly interface, I can be assured that each class has a Fly method on it and that methods signature conforms to the method defined in the IFly interface. Like Inheritance based Polymorphism the use of Interfaces enables me to declare a method that takes a type of IFly (my interface) and accept any class that implements that interface into the method. The classes that implement IFly do not need to have a common ancestor beyond the IFly interface. In my method I can call any method of the object that is passed in that is defined on the IFly interface without being concerned what the concrete type is. Is it a plane? Is it a flying squirrel? Who cares, it supports the IFly interface, so I know I can call the Fly method on it.
Understanding interface based Polymorphism is almost a required skill for TDD. When we start examining mocking and dependencies in this series you’ll be relying on this knowledge on an almost daily basis. A good understanding is to crucial to success. This is because we’ll be heavily relying on the ability to substitute classes to help us decouple from application dependencies and test our code in isolation using Mocks, which will implement the same Interfaces that our dependency objects use.
While many seasoned develops are familiar with OOP it is sometimes good to review these principals and see another perspective on them. The information here, particularly Polymorphism with Interface is crucial to our practice of TDD in a .NET world. Now that we have a common frame of reference we are ready to dive right in to TDD in the next post by writing our first test!
Continue the TDD journey: