I am trying to create a simple factory pattern demo in PHP. I am not sure if my codes are the best practice. It seems that I have some duplicated codes but I am not sure how to improve it. Basically, I want to create 3 types of accounts (basic, premium and vip). Please advise. Thanks a lot.
abstract class
abstract class User {
function __construct() {
$this->db= new Database('mysql','localhost','mvcdb','root','');
}
abstract function checkUser();
function showAccountCredit(){
return $this->credits;
}
function getUserName(){
return $this->username;
}
}
I have 3 different user account types:
Basic Account
class BasicUser extends User {
function __construct($username) {
parent::__construct();
$this->username=$username;
$this->credit='10';
$this->accountType='Basic Account';
$data=$this->checkUser();
if(!empty($data)){
echo 'The username: '.$this->username.' already exists<br>';
return false;
}
$array=array('username'=>$this->username, 'password'=>'password','credit'=> $this->credit,'accountType'=>$this->accountType);
$this->db->insert('user',$array);
}
function checkUser(){
$array=array(':username'=>$this->username);
$results=$this->db->select('SELECT * FROM USER WHERE username=:username',$array);
if(!empty($results)){
$this->credit=$results[0]['credit'];
$this->accountType=$results[0]['accountType'];
}
return $results;
}
function showAccountCredit() {
echo 'Username: '.$this->username.'<br>';
echo 'Account Credit: '.$this->credit.'<br>';
echo 'Account Type: '.$this->accountType;
}
}
Premium Account
class PremiumUser extends User {
function __construct($username) {
parent::__construct();
$this->username=$username;
$this->credit='100';
$this->accountType='Premium Account';
$data=$this->checkUser();
if(!empty($data)){
echo 'The username: '.$this->username.' already exists<br>';
return false;
}
$array=array('username'=>$this->username, 'password'=>'password','credit'=> $this- >credit,'accountType'=>$this->accountType);
$this->db->insert('user',$array);
}
function checkUser(){
$array=array(':username'=>$this->username);
$results=$this->db->select('SELECT * FROM USER WHERE username=:username',$array);
if(!empty($results)){
$this->credit=$results[0]['credit'];
$this->accountType=$results[0]['accountType'];
}
return $results;
}
function showAccountCredit() {
echo 'Username: '.$this->username.'<br>';
echo 'Account Credit: '.$this->credit.'<br>';
echo 'Account Type: '.$this->accountType.'<br>';
}
}
VIP account:
class VipUser extends User {
function __construct($username) {
parent::__construct();
$this->username=$username;
$this->credit='1000';
$this->accountType='VIP Account';
$data=$this->checkUser();
if(!empty($data)){
echo 'The username: '.$this->username.' already exists<br>';
return false;
}
$array=array('username'=>$this->username, 'password'=>'password','credit'=> $this->credit,'accountType'=>$this->accountType);
$this->db->insert('user',$array);
}
function checkUser(){
$array=array(':username'=>$this->username);
$results=$this->db->select('SELECT * FROM USER WHERE username=:username',$array);
if(!empty($results)){
$this->credit=$results[0]['credit'];
$this->accountType=$results[0]['accountType'];
}
return $results;
}
function showAccountCredit() {
echo 'Username: '.$this->username.'<br>';
echo 'Account Credit: '.$this->credit.'<br>';
echo 'Account Type: '.$this->accountType;
}
}
UserFactory class
class UserFactory {
static function create($username,$accountType){
$accountType = strtolower($accountType);
switch($accountType){
case 'basic': return new BasicUser($username);
case 'premium':return new PremiumUser($username);
case 'vip': return new VipUser($username);
default :return new BasicUser($username);
}
}
index.php
$user1= UserFactory::create('Jerry', 'Vip');
$user1->showAccountCredit();
$user2= UserFactory::create('Bob', 'Basic');
$user2->showAccountCredit();
$user3= UserFactory::create('Betty', 'premium');
$user3->showAccountCredit();
Instead of having 3 different implementations of user, consider 3 different implementations of “UserType”. Separating responsibilities and composing objects together. Here’s another pattern in action, called strategy.
New class, UserType represents special behavior, User would then contain rest of generic stuff, reducing duplication you’ve described (or at least most of it)
Factory on the other hand can be used for loading objects from db:
You can go even step further and introduce IoC framework. Have a look on symfony2. Oh, and please dont use switch, rather do it like I did, polymorphism, dynamic calls.
EDIT:
Strategy == wrapped behavior. For example if you were checking user rights for seeing premium content, this is how it would usually look like:
And here’s how it could look like using “strategy”:
This way, everything what’s related to userType is separated, it’s easy to add new types without risk of breaking existing types.
BTW: This is nothing more than just plain old polymorphism. Strategy is just another shiny word for OOP essentials, like most of patterns. Do not waste time thinking about patterns, they’re good for inspiration however you’ll gain much more experience from real OOP, have a look on smalltalk – http://www.pharo-project.org/home