Gang-of-Four
Design Patterns
This is a reflection of my knowledge of popular Design Patterns used in the software development industry.
Design Patterns: Elements of Reusable Object-Oriented Software (1994) is a
software engineering book describing
software design patterns. The book was written by
Erich Gamma,
Richard Helm,
Ralph Johnson, and
John Vlissides, with a foreword by
Grady Booch. (
Available on Amazon)
To be knowledgable in Design Patterns makes all the difference when solving problems in your work experence as well as being able to be a guide to help others solve similar issues in your work environment. If I had a dollar for how many times I've been asked about different Design Patterns, I'd be rich. So, I have decided to add this to my blog for the benefit of anyone reading this that they might find GoF Design Patterns useful in their applications as well.
GIVEN that we need to design software without re-inventing the wheel
WHEN a new application requires design similar to something done before
THEN we inquire into the GoF Design Patterns and pick out a best fit.
WHEN a new application requires design similar to something done before
THEN we inquire into the GoF Design Patterns and pick out a best fit.
The formal Wikipedia entry for GoF: Design Patterns can be found here. But we are going to explore each and every given design pattern and express them in layman's terms for the benefit of the reader. Who may have only beginner or intermediate knowledge of Design Patterns as well as when and how to use them. Mind you, this is not intended to be an exhastive description of each design pattern, just my opinion their invdividual uses and best practices, (as the book tends to be too descriptive in it's explanation of each, in my humble opinion).
Now in the following coding examples you maybe saying to yourself that that does not look like generic C++. That's because it's not. It is borrowing some syntax sugar from Java, (for the benefit of the reader).
#define interface struct
#define pure = 0
#define implements : public
#define extends : public
Now GoF Design Patterns are grouped into three catagories, Creational, Structural and Behaviorial. Inside each of these three catagories there maybe between five to eleven individual design patterns each.
GoF: Design Patterns, Gamma, Helm, Johnson, Vlissides (1994)
In a situation like this we write a simple Test Case
Converting the Sprint Given-When-Then statement into a Catch2 style test case is usually a good approach. So we set up a series of theorectical interactions between a perspective customer and the person behind the counter trying to use the system to book a type of room.
SCENARIO("Verify FrontDesk", "[FrontDesk]")
{
GIVEN("that a hotel needs to reserve different types of rooms")
WHEN("a customer shows up at the hotel and asks for a room")
THEN("we inquire into the system the availability of that type of room.") {
WHEN("a customer shows up at the hotel and asks for a room")
THEN("we inquire into the system the availability of that type of room.") {
FrontDesk frontDesk;
frontDesk.listRooms();
auto economy = frontDesk.selectEconomyRoom();
auto standard = frontDesk.selectMediumRoom();
auto luxury = frontDesk.selectExpensiveRoom();
frontDesk.listRooms();
auto economy = frontDesk.selectEconomyRoom();
auto standard = frontDesk.selectMediumRoom();
auto luxury = frontDesk.selectExpensiveRoom();
}
}
The actual implementation of the test case is shown below:
Essentially, we have now expressed the top level methods for selecting three different types of rooms. But we haven't determined what a Room class is at this point, neither have we identified a Reservation class. This will come out in the implementation of the three methods identified above.
The first test case makes assumptions...
First and foremost, it assumes that the default constructor already has rooms available for rent. This is skipping ahead a little bit. Hence the first real test case is to add rooms to the FrontDesk object for it to select from. However, logically, a Front Desk in human terms is really a part of a Hotel class. As such it is logical to conclude that a Hotel has a FrontDesk but a FrontDesk may not have a Hotel. Such that, it could merely be a FrontDesk to some other type of building. So, to take into consideration the next programmer to have to maintain the code, we try to code in terms of human constructs, (that is, in ways that a non-programmer might think).
Right-click and "Save Image as ..." to get a better look at this image!
Hence the need for a Hotel class, (hence the advantage of writing our own interface to a Hotel allowing our code to be able to interface to currently existing systems).
Also we write the test case for this interface:
One of the advantages of writng new code with interfaces, (known as the Abstract Design Pattern in Gof), is that when it comes time to add this new code to an existing database all that is required is an implementation that can interact with the currently existing system. For now, we will implement our Hotel and Room classes with C++ STL constructs:
To further demonstrate the advantages of programming with interfaces, let's assume that we have completed our new functionality and wish to connect the FrontDesk package to a typical (albeit complex) real world database that probably has been in service for a few years. The following is addictional class that bridge both worlds without breaking code in either source base:
So this demonstrates one of the major advantages of using interfaces to link up with pre-exisitng systems and without too much fuss.
Organizing the Rooms...
It is advantageous to organize your C++ code in terms of everyday terminology. In this way, you can solite help from subject matter experts better, using the age old English language). Hence, the more human readable your code, the easier it is for both yourself and your colleagues to contribute to it's success.
In the case of the Room class for the Hotel class, we want to be able to identify rooms. At least by using their traditional room numbers. For example, while our first test case allowed us to add a room to a hotel, removing a particular room would logically require a human readable room number. Hence our next test case draws upon both the need for a room number as well as shed light on how a hotel might keep track of available rooms vs. reserved rooms.
Now upon piecing together this test case, it becomes obvious that changing the HotelInterface class to use the verbs 'reserve' is advantageous. But the logical inverse to reserving a room is the guest checkout process, hence if we use the 'checkin' verb instead of 'reserve' might make more sense. However, checking in and checking out a guest is typically a function of the clerk at the front desk. Not only that, but after a guest leaves the room the room itself is actually in need of cleaning. Moreover, what do you do if after booking a room but that the guest cancels at the last minute?
By and large the Hotel instance is not really concerned with the type of room, just which rooms are currently available for new guests.
All these considerations can be better named methods in the HotelInterface:. This is where Mocking frameworks come in really handy. As well, designing logical interfaces early in the software development process like this is more fun.
The second test case (below) shows a similar workflow to the natural reservation process except that in the latter case, the customer cancels the room.
The source code for Hotel.hpp can be found here:
Further, an added advantage to using interfaces in C++ is that the moment you change the interface itself, all C++ code related to changes are instantly detected by the C++ complier. Hence, you can make all the necessary changes in advance and safely from inside logical arranged test cases.
Encapsulating the RoomKey
The C++ STL makes life so much easier when it comes to simple applications such as described here. However, it doesn't takes an experienced mind to know that no two hotel chains are the same when it comes to room numbers. Hence, we are encouraged to setup a class to encapsulate this behavioir in our design.
Up to this point we have assumed that a RoomKey will always be an integer. But what do we do if it turns out that the legacy system of Paris Hilton's fame likes to classify different types of rooms using a single letter in front?
Hence we make use of C++'s bells and whistles to allow the RoomKey to accept either long values or text strings in which case it'll simply convert it to a tradtional crc32 value for organizing purposes.
The source code for RoomKey.hpp can be found here:
Now the above code can be enhanced to test for a particular syntax on the room number format. Another option would be to apply the GoF Decorator Design pattern whereby a derived class specializing in a particular room number format can be dedicated to test for particular syntax.
Where the actual syntax checking takes place in a dedicate exception class:
And we be sure to test it for basic functionality:
And in the event we want to add Delta hotel support:
And we be sure to test it for Delta Hotel RoomKey format:
The source code for unique RoomKey formats can be found here:
Re-evaluating FrontDeskInterface
Our first kick at the can had us propose an interface like this:
However, in light of the HotelInterface doing so much work for us, w e can leave the internal details of Room management elsewhere and just concentrate on the transaction at hand. Hence we play with a slightly different interface design inside the test case:
The above modification of the selectEconomyRoom method implies that a generica hotel would really not be the best place to determine if a given room is of a particular type. Given that common knowledge suggests that some hotel lines encode the type of room inside the room number key itself, it stands to reason then that we should supply different types of Hotel classes.
Naturally, smaller motels and hotels probably only use a basic numbering system but that being said, they might internally have certain number ranges to represent different types of rooms.
GIVEN the FrontDesk implementation will prefer the 'selectEconomyRoom' request be deferred to the Hotel instance WHEN we need the same method on both intefaces THEN we need to do is apply the Proxy Design Pattern.
Such that both the FrontDeskInterface and the HotelInterface will both share methods that are declared as part of the RoomSelectionInterface:
Now while this is a nice improvement, it stands to reason that the generic Hotel class is simply not in a good place to determine just what is a room type. For this, we are better off defining classes for each particular type of hotel, (even cheap hotels).
Hence our initial room selection using the gemeric base class is to simplyy take the first available room. In a derived class, this method can be refined to look for room type information based on the contents of the RoomKey, (aka DeltaRoomKey).
Now that we have a dedicated spot for Delta hotels, we can safely express this logic for Delta room types where it makes sense, (and can be easily found again later).
In the case of a cheap hotel, perhaps certain number ranges represent certain types of rooms, so we write the implementation for the CheapHotel class:
The test case shows the difference in RoomKey type:
Now that a hotel can determine the different types of rooms it has based on the type of hotel that it is, we can now proceed with the next logical step in the FrontDesk customer 'checkin' process.
Performing a checkin operation
Now that the HotelInterface is resolving Rooms instances and Room type selections, all that remains for the FrontDeskInterface to do is coordinate the checkin process.
Now the logical checkout method follows in kind:
So this is a basic introduction into how to form a simple application in C++ using interfaces, STL, Catch2 and a few GoF Design Patterns. Along with some syntax sugar borrowed from Java, we have a decent means by which to add new functionality to currently existing system.
FrontDeskInterface
While it is technically a work in progress, we are off to a good start with respect to the original objective of this application. The following interfaces are what have been developed so far.
During the first Amazon interview the interviewer wanted to add a last minute change. Rather than just have three types of rooms, what if we had no idea how many types of rooms a hotel might have. But rather than put that method inside this interface, giving it it's own interface is cleaner and more logical.
In this manner we will be able to add the RoomTypeInterface without upsetting the clean design of the FrontDeskInterface. When you use a good mocking framework such as EranPeer's FakeIt, we can test individual interfaces before combining them with other interfaces as well as integration into an implementation.
The HotelInterface ended up taking the brunt of the classification logic and along with the RoomKey base class, allowed for easy adoption of various room types recognition via the format of a given room number. Complete with lots of places for expansion in the future.
GIVEN that the HotelInterface had to handle these requests better than the original FrontDeskInterface, we added GoF Proxy Design Pattern here to keep the classes insync with each when different types of rooms were required.
The header files code for FrontDesk can be found here
The source files code for FrontDesk can be found here
The test case files code for FrontDesk can be found here
The ChessMind Open Source Project
If you are wondering why I chose to include the FrontDesk project inside my ChessMind project it was largely because I'd like very much to show off my ChessMind project. As it is part of my Post Graduate program in Data Science with Purdue University. Where we focus on Machine Learning and AI as I have taken an interest putting together a LinearRegression engine to try and predict chess moves. With the complete history of every championship chess game ever played in by chess mind's like Gary Kasparov and Gary Fischer, it'll be interesting to see if TensorFlow and Deep Learning methods can be used to predict chess moves in the tradiation of individual chess players using nVidia's GPU on your typical laptop.
If you are wondering why I chose to include the FrontDesk project inside my ChessMind project it was largely because I'd like very much to show off my ChessMind project. As it is part of my Post Graduate program in Data Science with Purdue University. Where we focus on Machine Learning and AI as I have taken an interest putting together a LinearRegression engine to try and predict chess moves. With the complete history of every championship chess game ever played in by chess mind's like Gary Kasparov and Gary Fischer, it'll be interesting to see if TensorFlow and Deep Learning methods can be used to predict chess moves in the tradiation of individual chess players using nVidia's GPU on your typical laptop.
The complete source code for the ChessMind Open Source Project can be found here
Final thoughts
Adding this section to my blog was a good investment of my time as it gave me an opportunity to demonstrate popular design and programming methods with respect to C++, STL, Catch2, FakeIt and Gof Design Patterns.
One other thing to keep in mind however is to look out for brain freeze before an important interview. About a 1/2 hour before this interview I was handed a Tim Horton's Maple Ice Capp and perhaps this was the cause of me having recollection issues with respect to the HashMap class.
Perhaps ...
Perhaps ...
Why you should avoid Linked Lists, Bjarne Stroustrup
Day 1 Keynote, Bjarne Stroustrup: C++11 Style
What you have seen here is a typical day in software development for me. You utilize the tools you have available combined with what you understand as current and correct feedback.
Day 1 Keynote, Bjarne Stroustrup: C++11 Style
What you have seen here is a typical day in software development for me. You utilize the tools you have available combined with what you understand as current and correct feedback.
PERRY ANDERSON: IT CONSULTANT PORTFOLIO