4
\$\begingroup\$

I am still learning to develop my skills in OOP.

It uses a combination of the factory and its real singletons?

As I did more research, I have realized this design pattern is bad because of global state and it is hard to test.

I can't figure how how to convert this OOP to Dependency injection?

Section Class

class Section
{
 // Array contains of instantiations of Section
 protected static $instance;
 // Validation instance of validation
 public $validation;
 public $something;
 // instance method returns a new section instance.
 public static function instance($name = "default")
 {
 if ($exist = static::getInstance($name)) {
 return $exist;
 }
 static::$instance[$name] = new static($name);
 return static::$instance[$name];
 }
 // Return a specific instance
 public static function getInstance($instanceKey = null)
 {
 if (!isset(static::$instance[$instanceKey])) {
 return false;
 }
 return static::$instance[$instanceKey];
 }
 // Get Validation instance or create it
 public function validation()
 {
 if ($this->validation instanceof Validation) {
 return $this->validation;
 }
 if (empty($this->validation)) {
 $this->validation = Validation::instance($this);
 }
 return $this->validation;
 }
 public function add($something)
 {
 $this->something = $something;
 }
}

Validation Class (Just a random name for now)

class Validation
{
 // Related section instance
 public $section;
 protected function __construct($section)
 {
 $this->section = $section;
 }
 public static function instance($section = "default")
 {
 if (is_string($section)) {
 $section = Section::instance($section);
 }
 return new static($section);
 }
 public static function getInstance($instanceKey = null)
 {
 $temp = Section::getInstance($instanceKey);
 if (!$temp) {
 return false;
 }
 return $temp->validation();
 }
 // Alias for $this->section->add()
 public function add($name) {
 $this->section->add($name);
 return $this->section->something;
 }
}

Testing:

$section = Section::instance("Monkey");
$validation = $section->validation();
echo $validation->add("This is just a test");
asked Feb 27, 2012 at 22:25
\$\endgroup\$
2
  • \$\begingroup\$ Is this real code from your project? \$\endgroup\$ Commented Feb 28, 2012 at 1:33
  • \$\begingroup\$ @WinstonEwert I have written the code for learning purposes. Not for the project or anything. \$\endgroup\$ Commented Feb 28, 2012 at 1:44

1 Answer 1

6
\$\begingroup\$

First, a few minor comments:

Class properties should almost always be protected or private. This forces you to use a known tested interface (the class methods) to interact with the state stored in the class. The properties section, something and validation should have all been protected or private.

Your method add does not add, it sets. add might be appropriate for concatenation (although I'd prefer append or prepend). It is best used for real addition. Your add implementation would have been better named set.

I have added to your example class to highlight the benefits of Dependency Injection. I have used text and title in place of your something.

Validation Interface

The validation interface defines the way to check for validity. If we need to check for validity and we are passed an object that implements this interface we can be sure that it will work. By relying on an interface rather than a specific object we reduce our coupling from a specific object to any object that implements the interface.

interface IValidation
{
 public function isValid($str);
}

Section Class

The constructor accepts all of the parameters that the class needs (the dependencies are injected). I have used an array in the constructor (as a personal preference to remove a required order of parameters and to use named parameters with the associative array). Standard SPL exceptions are thrown if the validation objects do not match the Validation interface.

class Section
{
 // Text for the section.
 protected $text;
 // Validation for the text.
 protected $textValidation;
 /// Title for the section.
 protected $title;
 // Validation for the title.
 protected $titleValidation;
 public function __construct(Array $setup)
 {
 $setup += array('Text' => '',
 'Text_Validation' => NULL,
 'Title' => '',
 'Title_Validation' => NULL);
 if (!$setup['Text_Validation'] instanceof IValidation)
 {
 throw new \InvalidArgumentException(
 __METHOD__ . ' requires Text_Validator');
 }
 if (!$setup['Title_Validation'] instanceof IValidation)
 {
 throw new \InvalidArgumentException(
 __METHOD__ . ' requires Title_Validation');
 }
 $this->text = $setup['Text'];
 $this->textValidation = $setup['Text_Validation'];
 $this->title = $setup['Title'];
 $this->titleValidation = $setup['Title_Validation'];
 }
 public function setText($text)
 {
 $this->text = $text;
 }
 public function setTitle($title)
 {
 $this->title = $title;
 }
 // Return whether the section is valid.
 public function isValid()
 {
 return $this->textValidation->isValid($this->text) &&
 $this->titleValidation->isValid($this->title);
 }
}

Validation Class

Notice how this class implements the interface.

class Validation implements IValidation
{
 // The regex for a valid match.
 protected $validMatch;
 public function __construct($validMatch)
 {
 $this->validMatch = $validMatch;
 } 
 public function isValid($str)
 {
 return preg_match($this->validMatch, $str);
 }
}

Usage

We can see from the usage some of the benefits of dependency injection. There is no hardcoded dependencies in any of the classes and we can glue the components together when they are used. This allows us to define the exact type of validations that we require and pass them to the Section class with injection.

$alphanumUnderscores = new Validation('/^[[:alnum:]_]*$/');
$digits = new Validation('/^[[:digit:]]+$/');
$section = new Section(array('Text_Validation' => $alphanumUnderscores,
 'Title' => 'InvalidTitle',
 'Title_Validation' => $digits));
if (!$section->isValid())
{
 // The title should be digits.
 echo 'As expected the title is invalid.' . "\n";
}
$section->setTitle(9876);
if ($section->isValid())
{
 echo 'As expected the title is valid with digits.' . "\n";
}
answered Feb 28, 2012 at 5:12
\$\endgroup\$

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.