How to build a Singleton-like dependency injector replacement (Php)
- by Erparom
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"