Join us for the fourth post in the Design Patterns series. After looking at the command pattern in the last article, I thought it was time to take a closer look at the bridge pattern. While not the most common pattern, it can prove to be a lifesaver - especially in situations where you need a larger number of classes dealing with the same type of domain problem solving.
The code presented in this post can be found here. Please note that the projects in the solution correspond to the names of the patterns.
The goal
We want to create an application. I know, this is a shocker. However, the idea behind the application is quite simple. It needs to be able to create licences for software or just about anything. It should also be able to apply any kind of discount to an offer.
The old way
Sounds simple? It is, but the devil is in the details. More specifically, it lies in the number of classes we need to create to achieve our goal. We will start simple by creating two classes that will be responsible for the licence's lifetime. We want to have one monthly licence and one lifelong licence. This case is a perfect example of how class inheritance can be very useful. The base class will look like this:
It has two properties, a constructor and two methods. The methods give us a price and an expiration date respectively. Next we need to create the actual licence classes - we will have two of them, at least for now. The classes represent a monthly and a lifelong licence.
The classes do not seem complicated. They aren’t. Now it's time to add an option for a discount. We would like to have two options here - a small discount that lowers the price by 25%, and a large one that lowers the price by half. You can find the solution for this in a code below.
If you invoke the OldWay method, you can see that the solution works. The method creates licence objects and outputs them to the console.
Basically, invoking this method should give the specified results. The results are fine. The code works. So where is the devil hiding? Or maybe there is none and we can let it go?
The problem aka. the devil
Let us come to the point. We have two licenses and two types of discounts. That gives us a total of four classes. Sounds reasonable, does not it? Well, yes and no. The problem is that we are doing multiplication. Two times two is not scary, but as the number of options grows, multiplication becomes a problem
We want to replace multiplication with add. I know that 2 + 2 equals 4, but when there are more options, it makes a big difference. 3+3+3 equals 9. 3*3*3 equals 27. In such situations, the bridge pattern helps us rearrange our code. Let us see how it works.
The new solution
Using a bridge pattern is the new solution. The idea behind it is to couple classes in a looser way. This makes it more flexible to create combinations of classes. The first step is to create the core license class.
As we can see, this version of the license class is a bit more complicated. There are two major differences. First, the class takes a discount object as a parameter. Second, there are two methods responsible for a price - the protected abstract one, which takes into account the license price, which is not affected by discounts, and the public one, which considers the discount.
Now let us see what this independent discount class looks like.
The construction is quite straightforward. We have a base class that defines the method for the percentage discount value. All classes that derive from this class simply implement the value of this discount. What is particularly interesting in this simple code is the NoDiscount class. It was created based on the Null Pattern, which will be the main topic of one of the upcoming posts. So just a quick heads up. This pattern encourages us to stop using nulls and replace them with objects with neutral action results.
The last missing parts are the License classes.
And here are the last two elements, which do not contain a lot of code. What they really do is provide license-specific data - expiration time and initial price. The mechanism for calculating the final value is in the base class.
The code appears to be ready to go. Let us see if it works then, shall we? To do this, we need to call the NewWay method.
After running the code, the result should look something like this. Hopefully exactly like this.
Summary
As has been shown, the bridge pattern gives us a way to keep the number of classes within reasonable limits. It accomplishes this by making the classes more independent. Like any single pattern, it should be used in cases where value can be gained. Have you used it before? Let me know at karol.rogowski@softwarehut.com.