When building a house, you want it to be well designed. It’s great if someone’s tested the solutions you intend to use, and if they’re universal. For example, a standard window is cheaper than windows in various odd shapes and sizes. You don’t have to use expensive, custom-designed hinges to be able to open them. They’re easily available, and fast to repair or replace. It’s very similar with building applications.
Solutions based on properly implemented patterns and principles are universal, easier to understand and develop. There is just one crucial thing. To really benefit from them, you need to know them. In this article, I will list and briefly describe some of the recommendations for object oriented design that are there to help you achieve your goal.
Programming patterns by Gang of Four (GoF)
The Gang of Four are the four authors of the book called “Design Patterns: Elements of Reusable Object-Oriented Software”. The book describes 32 design patterns that provide solutions to common software design problems. Although the authors developed their ideas 20 years ago, all modern frameworks and libraries use the structures described in this famous book. Knowing them gives you a number of advantages.
First, they help to understand the code you’re working with. This includes other developers’ work like frameworks, libraries and implementations. You can meet GoF patterns in all of these places. For example, it’s much easier to understand how some PHP frameworks work with events, if you know the Mediator pattern. Dealing with the Symfony framework forms is easier with knowledge of the Builder pattern.
Other benefits refer to the programming process. With knowledge of the patterns, the developer gets a selection of tried and tested solutions to work with. Solutions that are flexible, extensible, and predictable. How to deal with a category tree? Each element is a category (so it has common methods). However, some of them also collect subcategories. You want all elements to be atomically treated, just like a leaf? This may be a good time to consider using the Composite pattern.
If you want to know how to write code that is easy to maintain and extend over time, SOLID principles are good guidelines to follow. SOLID is a mnemonic acronym for five principles: the First Five Principles of Object-Oriented Programming.
single responsibility principle
In a few words, you shouldn’t design a class that has more than one reason to modify it. When modifying a badly designed class for one reason, there is a risk of breaking code that fulfills other responsibilities of that class. When you separate responsibilities into different classes, the danger of poisoning them is much lower. For example, if you have one class responsible for both preparing and printing data, changing how the print looks like can affect the parts responsible for data preparation.
The second principle says that classes should be open for extensions, but closed for modifications. When extending, well tested code is left unchanged and changes don’t infect other modules that may use the basic code. This makes extending a safer solution. However, it’s possible only when class is designed to allow it. So, while designing and implementing, think which methods and data should be changeable and make them open to changes.
Liskov substitution principle
What’s also important is that child classes should never break the parent class’s type definitions. Clients of the parent class should be able to work with children’s classes automatically, without any code changes. It’s a child class responsibility to adapt to every usage of the parent class. Sometimes, to make everything work, you need another level of abstraction.
interface segregation principle
It is very important to design these abstractions properly. The fourth principle says that interfaces should describe one responsibility. Otherwise, classes that implement this interface will break the single responsibility principle. What’s more, large interfaces may lead to implementing methods that the particular class doesn’t need, but the interface requires.
dependency inversion principle
The last principle refers to the relationships between objects. They should depend on abstractions, not on each other. A system component that is loosely coupled to other elements (by abstraction) is easier to replace. Also, adding new components is simpler.
General Responsibility Assignment Software Patterns (GRASP)
If we’re considering which class should take responsibility for a particular action, GRASP patterns can help us make this decision. Written in the form of 9 questions and answers, they lead us to the best solution. In a nutshell, we learn that each class should fulfill one clearly defined responsibility. The responsibility should be granted to the class that holds most information required to fulfill it, so that the resulting class has as few dependencies as possible. It is also important to see what connections it makes. It’s recommended to ‘talk’ only with neighbours and avoid connection chains. I strongly encourage to read all nine principles. It’s impossible to summarize them in just a few sentences.
Programming language Standards and Recommendations (eg. PSR in PHP)
These are the standards for particular programming languages that describe various solutions. They range from code-formatting rules to ready-made interfaces and libraries. Each language has its own standards and libraries. Sometimes they’re strongly related and come as a part of the language but sometimes they’re separate. In the latter case, using them isn’t required by the language, but it’s recommended by the community.
For example, the PHP Framework Interop Group (PHP-FIG) developed code formatting standards and abstractions that specify how to deal with cache, logs, request, response and other common structures. In short, elements that don’t have to (and often shouldn’t) be designed from scratch for every implementation.
Don’t Repeat Yourself (DRY). Keep your code Simple and Small (KISS). Don’t code what You Aren’t Gonna Need (YAGNI).
Following those three simple rules will make your code easier to understand, develop and test. If you repeat your code, then in a while it will lose consistency. Changes will then be harder to implement. Bugs will start popping up. If your code is difficult to understand – think again. Simple solutions are usually best. And never commit code that no one yet needs. Code that is not used is also not tested. It shouldn’t be a part of your application.
Object oriented design: All things considered…
Create clear code that’s easy to develop and test: these should be the most important goals for every developer. Knowledge of object oriented design patterns and principles mentioned above is an essential tool to achieve this objective. Universal, tested solutions save the time of the programmer who writes them, the programmers who use them and, finally, the customers. Thanks to these patterns, development becomes simpler and more predictable.