Abstraction is one of the subjects in software development that’s really difficult to fully grasp without practice. It’s easy to read about it, talk about it, and profess doing it. To actually do it is another thing entirely: it requires practice, and patience, and experimentation.
I decided the best way to show abstraction in action is by showing the commits I made as I worked to abstract a model that contained the validation and database logic inside the same class. You can see my progress, as well as my notes about what I’m doing as I go along below.
This post contains full length code examples. I have used GitHub to provide them since GitHub’s diff viewer is second to none. Click on the smaller images to see the full code samples in new windows.
Abstraction And The Single Responsibility Principle
The biggest clue I find to indicate that further abstraction is necessary in an application is a violation of the single responsibility principle. The single responsibility principle states that every class should have one job and only one job.
When I come across a class that has more than one job, I know it can stand to be abstracted.
Creating Separate Classes
Breaking up classes can be challenging, especially in terms of knowing that we don’t want to break the existing functionality.
My solution for this was to leave the original model class intact as a gateway class, and offer two additional classes: one responsible for the validation, and one responsible for the storage of the data. This separates out the different jobs into separate classes, which meets the single responsibility principle test.
Even though the gateway class interacts with these two other classes, it too has only one job: message passing between different layers.
Taking a Second Look
Even though I had abstracted out the data access and the validation from the gateway object, there were still a few things I could do to further abstract the objects.
Data access is the big obvious one. In fact, data access that relies on a particular data storage engine is tight coupling in the worst way. It makes it near impossible to swap one data source for another.
However, we can make some basic assumptions about how we’re going to access data. Even a NoSQL database will have the basic four CRUD actions – create, retrieve, update and delete. So let’s implement a class that would be capable of implementing these four actions (I only implemented two, since that was all that was required for this demo).
Designing By Contract
Since it’s possible I may want to initiate a certain different type of data store in the future, I want to have some sort of interface I can use for these data storage classes. The creation and use of these interfaces is known as “designing by contract”, and it’s a well-understood principle that lends to reuse and abstraction.
I defined an interface that could be used to implement a similar data storage class, one that uses Postgres, or MongoDB or even Redis.
Doing Some Typehinting and Dependency Injection
Of course, this app is hardly testable. And even with interfaces and our design by contract strategy, we have no way of injecting the objects we want or need into the system. So let’s fix that.
We should inject all the objects we need into the ClientModel class. The ClientModel will continue to take its previously expected arguments, and will typehint against the class types it knows about. The other classes instantiate objects in their constructor methods too. Instead, we will accept those objects as arguments, and typehint on the class or interface we expect.
Improvements Still To Be Made
There are still improvements we could make, but that I did not make for the sake of this demo. The class naming strategy is not in compliance with the PSR standards. The names I selected were not descriptive enough in some instances. There’s a file system organization challenge and I’m using require statements.
The validation logic and the database logic could be upgraded to be more robust and catch different types of exceptions, and handle them differently. The validation function and SQL construction could be broken into their own, testable functions. There’s always more to do, but there’s also plenty here to demonstrate the process of abstracting a model into separate classes.
Learn how to do this hands-on
Starting on April 14th, I’ll be offering a class called The Object Oriented PHP Masterclass. One of the many topics we’ll be covering is abstraction. If you struggle with abstraction or other object oriented programming principles, this class is for you.
The Object Oriented PHP Masterclass is designed to teach you the principles of object oriented programming in a way you’ve never experienced before. The class is entirely online in the comfort of your home or office. And you’ll get a chance to get your hands dirty with actual code, and receive reviews and tips from yours truly on how to make your code better. Plus, with two live sessions, you’ll have plenty of chances to ask questions and get answers to your most pressing object oriented programming challenges. Are you ready to take your career to the next level?
The good news is there are a few seats left. You can still get a seat to The Object Oriented PHP Masterclass!
The bad news is that these seats will go fast. Plus, early bird pricing ends April 1st That’s only a week away! The class is only $499 for two weeks of personal, online hands-on instruction, but the price goes up to $599 next Monday!
Frustrated with your company’s development practices?
You don't have to be!
No matter what the issues are, they can be fixed. You can begin to shed light on these issues with my handy checklist.
Plus, I'll help you with strategies to approach the issues at the organization level and "punch above your weight."