Ever wondered what design patterns are? In this article, I'll explain why design patterns are important, and will provide some examples, in PHP, of when and why they should be used.
What are Design Patterns?
Design patterns are optimized, reusable solutions to the programming problems that we encounter every day. A design pattern is not a class or a library that we can simply plug into our system; it's much more than that. It is a template that has to be implemented in the correct situation. It's not language-specific either. A good design pattern should be implementable in most—if not all—languages, depending on the capabilities of the language. Most importantly, any design pattern can be a double-edged sword— if implemented in the wrong place, it can be disastrous and create many problems for you. However, implemented in the right place, at the right time, it can be your savior.
There are three basic kinds of design patterns:
structural
creational
behavioral
Structural patterns generally deal with relationships between entities, making it easier for these entities to work together.
Creational patterns provide instantiation mechanisms, making it easier to create objects in a way that suits the situation.
Behavioral patterns are used in communications between entities and make it easier and more flexible for these entities to communicate.
Why should we use them?
Design patterns are, by principle, well-thought out solutions to programming problems. Many programmers have encountered these problems before, and have used these 'solutions' to remedy them. If you encounter these problems, why recreate a solution when you can use an already proven answer?
Example
Let's imagine that you've been given the responsibility of creating a way to merge two classes which do two different things based on the situation. These two classes are heavily used by the existing system in different places, making it difficult to remove these two classes and change the existing code. To add to this, changing the existing code requires that you'll also need to test any changed code, since these sorts of edits, in a system which relies on different components, almost always introduce new bugs. Instead of doing this, you can implement a variation of the strategy pattern and adapter pattern, which can easily handle these types of scenarios.
class StrategyAndAdapterExampleClass {
private $_class_one;
private $_class_two;
private $_context;
public function __construct( $context ) {
$this->_context = $context;
}
public function operation1() {
if( $this->_context == "context_for_class_one" ) {
$this->_class_one->operation1_in_class_one_context();
} else ( $this->_context == "context_for_class_two" ) {
$this->_class_two->operation1_in_class_two_context();
}
}
}
Pretty simple, right? Now, let's take a closer look at the strategy pattern.
Strategy Pattern
The strategy pattern is a behavioral design pattern that allows you to decide which course of action a program should take, based on a specific context during runtime. You encapsulate two different algorithms inside two classes, and decide at runtime which strategy you want to go with.
In our example above, the strategy is based on whatever the $context variable was at the time the class was instantiated. If you give it the context for class_one, it will use class_one, and vice versa.
Cute, but where can I use this?
Imagine that you're currently developing a class which can either update or create a new user record. It still needs the same inputs (name, address, mobile number, etc.), but, depending on a given situation, it has to use different functions when updating and creating. Now, you could probably just use an if-else to accomplish this, however, what if you need to use this class in a different place? In that case, you'll have to rewrite the same if-else statement all over again. Wouldn't it be easier to just specify your context?
class User {
public function CreateOrUpdate($name, $address, $mobile, $userid = null)
{
if( is_null($userid) ) {
// it means the user doesn't exist yet, create a new record
} else {
// it means the user already exists, just update based on the given userid
}
}
}
Now, the "usual" strategy pattern involves encapsulating your algorithms inside another class, but in this case, another class would be wasteful. Remember that you don't have to follow the template exactly. Variations work as long as the concept remains the same, and it solves the problem.
Adapter Pattern
The adapter pattern is a structural design pattern that allows you to repurpose a class with a different interface, allowing it to be used by a system which uses different calling methods.
This also lets you alter some of the inputs being received from the client class, making it into something compatible with the adaptee's functions.
How can I use this?
Another term to reference an adapter class is a wrapper, which basically lets you "wrap" actions into a class and reuse these actions in the correct situations. A classic example might be when you're creating a domain class for table classes. Instead of calling the different table classes and calling up their functions one by one, you could encapsulate all of these methods into one method using an adapter class. This would not only allow you to reuse whatever action you want, it also keeps you from having to rewrite the code if you need to use the same action in a different place.
Compare these two implementations.
Non-Adapter Approach
$user = new User();
$user->CreateOrUpdate( //inputs );
$profile = new Profile();
$profile->CreateOrUpdate( //inputs );
If we needed to do this again in a different place, or even reuse this code in a different project, we would have to type everything all over again.
Better
That's opposed to doing something like this:
$account_domain = new Account();
$account_domain->NewAccount( //inputs );
In this situation, we have a wrapper class, which would be our Account domain class:
class Account()
{
public function NewAccount( //inputs )
{
$user = new User();
$user->CreateOrUpdate( //subset of inputs );
$profile = new Profile();
$profile->CreateOrUpdate( //subset of inputs );
}
}
This way, you can use your Account domain again whenever you need it—plus, you'd be able to wrap other classes under your domain class as well.
Factory Method Pattern
The factory method pattern is a creational design pattern which does exactly as it sounds: it's a class that acts as a factory of object instances.
The main goal of this pattern is to encapsulate the creational procedure that may span different classes into one single function. By providing the correct context to the factory method, it will be able to return the correct object.
When can I use this?
The best time to use the factory method pattern is when you have multiple different variations of a single entity. Let's say you have a button class; this class has different variations, such as ImageButton, InputButton and FlashButton. Depending on the place, you may need to create different buttons—this is where you can use a factory to create the buttons for you!
Let's begin by creating our three classes:
abstract class Button {
protected $_html;
public function getHtml()
{
return $this->_html;
}
}
class ImageButton extends Button {
protected $_html = "..."; //This should be whatever HTML you want for your image-based button
}
class InputButton extends Button {
protected $_html = "..."; //This should be whatever HTML you want for your normal button ();
}
class FlashButton extends Button {
protected $_html = "..."; //This should be whatever HTML you want for your flash-based button
}