2

In my application I can have different rules in different cultural contexts. I have the following code.

<?php

class Rules_IT {

  const MYCONST_1 = 5;
  const MYCONST_2 = 3;
  const MYCONST_3 = 10;

  static public function myMethod(){
      return 'Something';
  }
}

class Rules_DE {

  const MYCONST_1 = 3;
  const MYCONST_2 = 2;
  const MYCONST_3 = 15;

  static public function myMethod(){
      return 'Something different';
  }
}


if($pattern == 'IT'){
  class_alias('Rules_'.$pattern, 'Rules');
}
else if($pattern == 'DE'){
  class_alias('Rules_'.$pattern, 'Rules');
}
else {
  // [...]
}

// And after I use the class Rules ... 
print Rules::myMethod();

It works. My IDE (NetBeans 7.x) don't know the class Rules and I can't use the code autocomplete, but isn't a big problem.

But there are some alternatives? Some different approach in the logic? Some design-pattern to handle the problem?

Thank you.

Marcello Verona
  • 540
  • 1
  • 6
  • 10
  • I recommend reading more books about programming - such as thi on http://www.amazon.co.uk/Head-First-Design-Patterns-Freeman/dp/0596007124/ref=sr_1_fkmr0_1?s=books – Danack Oct 11 '15 at 13:23

3 Answers3

2

You don't have the need to use class_alias or declare static methods. A basic factory class can help you.

class RulesFactory 
{
    /*
    public function __construct($validNames = array('IT', 'DE', 'EN')) {
        // ...
    }*/

    public /*static*/ function build($rules_name)
    {
        $className = "Rules_" . strtoupper($rules_name);
        if(class_exists($className)) {
            return new $className();
        } else {
            throw new InvalidArgumentException("Invalid rules name given.");
        }
    }
}

interface Rules
{
    public function myMethod();
    public function getParam($name);
}

abstract class AbstractRules implements Rules
{
    protected $param = [];

    public function getParam($name, $default = null)
    {
        if (isset($this->param[$name])) {
            return $this->param[$name];
        } else {
            return $default;
        }
    }
}

class Rules_IT extends AbstractRules
{
    protected $param = ['MYCONST_1' => 5, 'MYCONST_2' => 3, 'MYCONST_3' => 10];

    public function myMethod()
    {
        return 'Something';
    }
}

class Rules_DE extends AbstractRules
{
    protected $param = ['MYCONST_1' => 3, 'MYCONST_2' => 2, 'MYCONST_3' => 15];

    public function myMethod()
    {
        return 'Something different';
    }
}

You can use that with:

$factory = new RulesFactory();
$rules = $factory->build('IT');

echo $rules->myMethod(), "\n";
echo $rules->getParam('MYCONST_1');

You can also declare RulesFactory::build as a static method, in that case you can use it as

RulesFactory::build('IT')->myMethod();

MISC:

Why using getParam instead of using constants?

Because it gives you a more flexible way to define and call those parameters. Example:

abstract class AbstractRules implements Rules
{
    protected $param = [];

    public function getParam($name)
    {
        if (isset($this->param[$name])) {
            return $this->param[$name];
        }

        throw new InvalidArgumentException("$name parameter doesn't exists.");
    }   

    public function hasParam($name)
    {
        return isset($this->param[$name]);
    }

    public function addParam($name, $value)
    {
        $this->param[$name] = $value;
    }
}

Static property are not associated to any particular instance/object of a class, they belong to the class itself. You may don't want this. But if you do, you can simply do this:

class Rules_IT extends AbstractRules
{
    const MYCONST_1 = 5;
    const MYCONST_2 = 3;
    const MYCONST_3 = 10;

    public function myMethod()
    {
        return 'Something';
    }
}

$rules = (new RulesFactory())->build('IT');
echo $rules::MYCONST_1;

Why use an interface "Rules"? What is the benefit?

Interfaces are a way to represent contractual obligations. Why would I want to use Interfaces?. You could already take advantage of this with:

public function build($rules_name)
{
    $className = "Rules_" . ucwords($rules_name);
    if(class_exists($className)) {
        $object = new $className();

        if (!$object instanceof Rules) {
            throw new InvalidArgumentException("$className must implement Rules interface");
        }

        return $object
    }

    throw new InvalidArgumentException("Invalid rules name given.");
}
Community
  • 1
  • 1
Federkun
  • 36,084
  • 8
  • 78
  • 90
  • Interesting implementation. Two questions: 1. why use an interface "Rules"? What is the benefit? 2. The solution is brilliant for the methods, but not for the parameters (the constants, in my first question). I think I need a stronger and more reliable method to manage the parameters. I think is mandatory to check the existence of the key as miminum (and throw an exception if not), but can I implement variables or constants with this design pattern? Thank you – Marcello Verona Oct 11 '15 at 15:54
1

IMO, I would firstly create a Rules interface so any child of Rules would implement the interface.

interface Rules 
{
   //Something here. 
}

class Rules_IT implements Rules {

  const MYCONST_1 = 5;
  const MYCONST_2 = 3;
  const MYCONST_3 = 10;

  static public function myMethod(){
      return 'Something';
  }
}

class Rules_DE implements Rules {

  const MYCONST_1 = 3;
  const MYCONST_2 = 2;
  const MYCONST_3 = 15;

  static public function myMethod(){
      return 'Something different';
  }
}

The big problem I see is your conditional approach to deciding which Class needs to be used.

You could do the following for that:

$class = 'Rules_' . $pattern; 

if( class_exists($class) )
{
    class_alias($class, 'Rules');
}
jakehallas
  • 2,456
  • 1
  • 18
  • 26
0

May be you can try Strategy pattern, especially if your cultural rules will be complicated. https://en.wikipedia.org/wiki/Strategy_pattern

Uber Bot
  • 511
  • 3
  • 8