How to build a Singleton-like dependency injector replacement (Php)

Posted by Erparom on Programmers See other posts from Programmers or by Erparom
Published on 2013-10-18T19:05:00Z Indexed on 2013/10/18 22:15 UTC
Read the original article Hit count: 349

I know out there are a lot of excelent containers, even frameworks almost entirely DI based with good strong IoC classes. However, this doesn't help me to "define" a new pattern. (This is Php code but understandable to anyone)

Supose we have:

//Declares the singleton
class bookSingleton {
   private $author;
   private static $bookInstance;
   private static $isLoaned = FALSE;  

   //The private constructor
   private function __constructor() {
      $this->author = "Onecrappy Writer Ofcheap Novels";
   }

   //Sets the global isLoaned state and also gets self instance
   public static function loanBook() {
      if (self::$isLoaned === FALSE) {
         //Book already taken, so return false
         return FALSE;
      } else {
         //Ok, not loaned, lets instantiate (if needed and loan)
         if (!isset(self::$bookInstance)) {
             self::$bookInstance = new BookSingleton();
         }
         self::$isLoaned = TRUE;
      }
   }

   //Return loaned state to false, so another book reader can take the book
   public function returnBook() {
      $self::$isLoaned = FALSE;
   }

   public function getAuthor() { return $this->author; }
}

Then we get the singelton consumtion class:

//Consumes the Singleton
class BookBorrower() {
   private $borrowedBook;
   private $haveBookState;

   public function __construct() {
     this->haveBookState = FALSE;
   }

   //Use the singelton-pattern behavior
   public function borrowBook() {
      $this->borrowedBook = BookSingleton::loanBook();
      //Check if was successfully borrowed
      if (!this->borrowedBook) {
         $this->haveBookState = FALSE;
      } else {
         $this->haveBookState = TRUE;
      }
   }

   public function returnBook() {
      $this->borrowedBook->returnBook();
      $this->haveBookState = FALSE;   
   }

   public function getBook() {
      if ($this->haveBookState) {
         return "The book is loaned, the author is" . $this->borrowedbook->getAuthor();
      } else {
         return "I don't have the book, perhaps someone else took it";
      }
   }
}

At last, we got a client, to test the behavior

function __autoload($class) {
    require_once $class . '.php';
}

function write ($whatever,$breaks) {

    for($break = 0;$break<$breaks;$break++) {
        $whatever .= "\n";
    }
    echo nl2br($whatever);
}
write("Begin Singleton test", 2);

$borrowerJuan = new BookBorrower();
$borrowerPedro = new BookBorrower();

write("Juan asks for the book", 1);
$borrowerJuan->borrowBook();
write("Book Borrowed? ", 1);
write($borrowerJuan->getAuthorAndTitle(),2);

write("Pedro asks for the book", 1);
$borrowerPedro->borrowBook();
write("Book Borrowed? ", 1);
write($borrowerPedro->getAuthorAndTitle(),2);

write("Juan returns the book", 1);
$borrowerJuan->returnBook();
write("Returned Book Juan? ", 1);
write($borrowerJuan->getAuthorAndTitle(),2);

write("Pedro asks again for the book", 1);
$borrowerPedro->borrowBook();
write("Book Borrowed? ", 1);
write($borrowerPedro->getAuthorAndTitle(),2);

This will end up in the expected behavior:

Begin Singleton test

Juan asks for the book Book Borrowed? The book is loaned, the author is = Onecrappy Writer Ofcheap Novels

Pedro asks for the book Book Borrowed? I don't have the book, perhaps someone else took it

Juan returns the book Returned Book Juan? I don't have the book, perhaps someone else took it

Pedro asks again for the book Book Borrowed? The book is loaned, the author is = Onecrappy Writer Ofcheap Novels


So I want to make a pattern based on the DI technique able to do exactly the same, but without singleton pattern. As far as I'm aware, I KNOW I must inject the book inside "borrowBook" function instead of taking a static instance:

public function borrowBook(BookNonSingleton $book) {
    if (isset($this->borrowedBook) || $book->isLoaned()) {
        $this->haveBook = FALSE;
        return FALSE;
    } else {
        $this->borrowedBook = $book;
        $this->haveBook = TRUE;
        return TRUE;
    }
}

And at the client, just handle the book: $borrowerJuan = new BookBorrower(); $borrowerJuan->borrowBook(new NonSingletonBook()); Etc... and so far so good, BUT... Im taking the responsability of "single instance" to the borrower, instead of keeping that responsability inside the NonSingletonBook, that since it has not anymore a private constructor, can be instantiated as many times... making instances on each call.

So, What does my NonSingletonBook class MUST be in order to never allow borrowers to have this same book twice? (aka) keep the single instance.

Because the dependency injector part of the code (borrower) does not solve me this AT ALL. Is it needed the container with an "asShared" method builder with static behavior? No way to encapsulate this functionallity into the Book itself? "Hey Im a book and I shouldn't be instantiated more than once, I'm unique"

© Programmers or respective owner

Related posts about php

Related posts about dependency-injection