arcanine
arcanine

Reputation: 1953

PHP Design Pattern

I have a reoccuring problem that I am currently tackling like so -

a POST variable coming in to the script which has a platform, the platform is from a list such as: xbox,ps3,pc,mobileapp,mobilegame etc

for each different platform I want to be able to do something different in my script but in some cases I want code to do very similar things at the moment I do something like this:

$platformArray = array(
   'ps3'=>array('displayName'=>'playstation 3','function'=>'funcPS3'),
   'xbox'=>array('displayName'=>'Xbox','function'=>'funcXbox')
)
//similar amongst all platforms code on line below
echo 'you have a :'.$platformArray[$_POST['platform']]['displayName'].' for playing       games';

call_user_func($platformArray[$_POST['platform']['function']);

function funcPS3(){
   echo 'ps3 specific code'; 
}

 function funcXbox(){
   echo 'xbox specific code';
 }

I want to move towards a OOP approach in my code, I want to use objects as my data storage medium rather than arrays as I'm doing now, but I do sometimes need to define attributes in the code ahead of time, how could I do the above but with objects?

Upvotes: 1

Views: 275

Answers (3)

Ruan Mendes
Ruan Mendes

Reputation: 92274

I'm going to work from a very naive OO version, to what is considered "good" OO code, using polymorphic behavior and avoiding global state.

1. Not polymorphic and has global static data

This is pretty bad because it is really just a wrapper object over procedural code. It needs a map of functions to call for each type of platform.

class Platform {    
    private static $platformArray = array(
       'ps3' => array(
           'displayName'=>'playstation 3',
           'function'=>'funcPS3'
        ),
       'xbox' => array(
           'displayName'=>'Xbox',
           'function'=>'funcXbox'
       )
    );

    private $type;

    public function __construct($type) {
        if (!array_key_exists($type, self::$platformArray)) {
             throw new Exception("Invalid Platform type $type" );
        }
        $this->type = $type;
    } 

    public function printCode() {
        // This was a question embedded within your question, you can use 
        // http://php.net/manual/en/function.call-user-func.php
        // and pass an instance with a method name.     
        return call_user_func( array($this, self::$platformArray[$this->type]) );
    }

    private function funcPS3(){
        echo 'ps3 specific code'; 
    }

    private function funcXbox(){
        echo 'xbox specific code';
    }    
}

$plat = new Platform($_POST['platform']);
$plat->printCode();

2. Polymorphic... but it still uses global data

By creating a base class you can implement behavior in subclasses, creating separate class for each concern. The big problem here is that subclasses need to register with a global registry.

abstract class Platform {
    abstract protected function getCode();
    public function printCode() {
        echo $this->getCode();
    }

    private function __construct() {} // so only factory can instantiate it
    private static $platformArray = array();

    public static function create($type) {
        if (!array_key_exists($type, self::$platformArray)) {
             throw new Exception("Invalid Platform type $type" );
        }
        return new self::$platformArray[$type];

    }         

    public static function addPlatform($type, $ctor) {
        if (!is_subclass_of($ctor, 'Platform')) {
             throw new Exception("Invalid Constructor for Platform $ctor" );   
        }
        self::$platformArray[$type] = $ctor;
    }
}

class PlatformXBox extends Platform{
     protected function getCode() {
         return 'xbox specific code';
     }
}
Platform::addPlatform('xbox', 'PlatformXBox');

class PlatformPs3 extends Platform {
     protected function getCode() {
         return 'ps3 specific code';
     }
}
Platform::addPlatform('ps3', 'PlatformPs3');

$plat = Platform::create($_POST['platform']);
$plat->printCode();

3. Polymorphic, no global data

By putting your code into a namespace, you avoid the static code in the base class and avoid the dangers of mapping post parameters directly into classes.

namespace platform {

interface IPlatform {
   public function getDisplayName();
   public function getCode();
}

class PlatformFactory {
    static public function create($platformType) {           
        $className = "\\platform\\$platformType";
        if ( !is_subclass_of($className, "\\platform\\IPlatform") ){
            return null;
        }
        return new $className;
    }
}

class Xbox implements IPlatform {
    public function getDisplayName(){
        return 'xbox';
    }
    public function getCode(){
       return 'xbox code';   
    }
}

class Ps3 implements IPlatform {
    public function getDisplayName(){
        return 'ps3';
    }
    public function getCode(){
        return 'ps3 code';   
    }
}

}

Now you can use those classes like the following

$platform = platform\PlatformFactory::create('xbox');
echo $platform->getCode() ."\n" ;

$platform2 = platform\PlatformFactory::create('ps3');
echo $platform2->getDisplayName()."\n";

$noPlatform = platform\PlatformFactory::create('dontexist');
if ($noPlatform) {
    echo "This is bad, plaftorm 'dontexist' shouldn't have been created";
} else {
    echo "Platform 'dontexist' doesn't exist";
}

Upvotes: 0

tereško
tereško

Reputation: 58444

I would recommend for you to start by understanding polymorphism. This lecture should be good start.

When you are trying to create behavior, based on some flag, you should implement two classes with same interface:

class Xbox
{
    private $displayName = 'XBox 360';

    public function identify()
    {
        // Xbox-specific stuff
        return  ':::::::::::'. $this->displayName;
    }
}

class PS3
{

    private $displayName = 'Playstation 3';

    public function identify()
    {
        // playstation-specific stuff
        return '+++'. $this->displayName . '+++';
    }
}

The two classes have method with same name that would do different things;

$platform = $_POST['platform'];
// classes in PHP are case-insensitive
// expected values would be:  xbox, Xbox, ps3, pS3
if ( !class_exists($platform) )
{
     echo "Platform '{$platform}' is not supported";
     exit; 
     // since continuing at this point would cause a fatal error, 
     // better to simply exit
}

$object = new $platform;
echo $object->identify();

Basically, in this case you really do not care, which type of platform you are working with. All you need to know is that they both have same public interface. This is called "polymorphic behavior".

Upvotes: 4

Willem
Willem

Reputation: 123

You might want to create a class called platforms and within the class a different method for each platform:

class platforms {
   //Create your variables here, also called properties.
   public $displayName;

   //Create a function, also called a method for each platform you intent to use.
   public function xboxPlatform(){
        //Code comes here what you want to do.
   }
}

Hope this helps.

Upvotes: 0

Related Questions