Design Patterns in PHP: Factory, Strategy and Singleton

What are design patterns

Design patterns are coding strategy followed by developers. It’s just like an architectural plan followed in building houses. As per theory it can defined as “A general solution to commonly occurring problems in software design”. Instead of talking about what Design patterns are let’s have a look at some real world examples. In this article we shall go through some of most frequently used and tried and tested design patterns in PHP which are Factory, Strategy and Singleton.

Factory method

Consider a class Classroom that depends on Class Student and Class Teacher for it’s creating.

$student_obj = new Student();
$teacher_obj = new Teacher();
$classroom_obj = new Classroom($student_obj, $teacher_obj);

Now the above code looks perfectly fine. But it would be quite cumbersome to rewrite the same three lines every where we require the object of Class Classroom. Also even if one does write it there’s another possible issue with it. Imagine after some days due to change in business logic the definition of our Classroom class changes. Instead of two parameter, student object and teacher object, it now has additional third object, $blackboard_obj. So new way of creating Classroom object is new Classroom($student_obj, $teacher_obj, $blackboard_obj);. Now we’ll have to make this change at every in our code where we have used the classroom object.
This is where Factory method comes in handy. Let’s see how.

class Classroom_Factory {
    public static function get_instance() {
        $student_obj = new Student();
        $teacher_obj = new Teacher();
        return new Classroom($student_obj, $teacher_obj);
    }
}

$classroom_obj = Classroom_Factory::get_instance();

As can be seen from above code, every time we need an object of Class Classroom we just need to call Classroom_Factory::get_instance();. So now we not only have to write only one line of code for getting a new instance of Classroom object but also if there is a change in definition of Classroom class we just have to make this change at one place, in the factory method get_instance().

Strategy

Consider an Ecommerce website http://abc.com that sells different products like Shoes, Apparels, Mobiles etc. Now each of this product has it’s own class. Like for Shoes we’ll have a class Shoe, for Apparels class Apparel and so on. Each class would be almost similarly in implementation except for few changes. Like every class will have a method get_all(), get_sku() or get_price().

Interface Product_Type {
    public function get_all();
    
    //skipped other function definitions to keep this example simple
}

Class Shoe implements Product_Type {
    public function get_all() { return DB::table('shoes')->all(); }
}

Class Apparel implements Product_Type {
    public function get_all() { return DB::table('apparels')->all(); }
}

Class Mobiles implements Product_Type {
    public function get_all() { return DB::table('mobiles')->all(); }
}

Now say we have a page http://abc.com/listing/{product_type} that shows all the product of the particular category. For example http://abc.com/listing/shoes lists all the shoes available in store.
Now in this particular case our Controller has to conditionally create an instance of the product type depending upon what user has types. Below is how one would conditionally create this instance in controller

if( $product_type == 'shoes' ) {
    $product_obj = new Shoe();
}
elseif( $product_type == 'apparels' ) {
    $product_obj = new Shoe();
}
elseif( $product_type == 'mobiles' ) {
    $product_obj = new Mobile();
}

$all_products = $product_obj->get_all();

Above code is perfectly fine but it could be written more elegantly using the Strategy design pattern. Let’s see how.

Class Listing {
    private $product_type;
    
    public function set_product_type($product_type) {
        if( $product_type == 'shoes' ) {
            $this->product_type = new Shoe();
        }
        elseif( $product_type == 'apparels' ) {
            $this->product_type = new Apparels();
        }
        elseif( $product_type == 'mobiles' ) {
            $this->product_type = new Mobile();
        }               
    }
    
    public function get_all() {
        return $this->product_type->get_all();
    }
}

$listing_obj = new Listing();
$listing_obj->set_product_type($product_type);
$all_products = $listing_obj->get_all();

Singleton

Singleton pattern is used for resources which are to shared globally. Best example would be database connection and queue engines. You surely wouldn’t want to create a new connection to database in every function/class that uses it. In Singleton pattern a class can be instantiated only once. Below is an example

class Db {
    private static $instance;
    public static function get_instance() {
       if (null === self::$instance) {
          self::$instance = new self();
       }
       
       return self::$instance;
    }
    
    protected function __construct() {
    }
    
    private function __clone() {
    }
    
    private function __wakeup() {
    }
}

$db_obj = Db::get_instance();

As can be seen in above example the constructor to Class Db is protected. Which means that this class can’t be instantiated using new Db(). Only way to create an instance is Db::get_instance(). This method creates new self instance, assigns to it’s static property instance when called for the first time. All subsequent call to this function will return this property and NOT create another new instance of the class.

Leave a Reply

Your email address will not be published. Required fields are marked *