I have tried to achieve the following task using Factory Pattern with PHP version 7.0.
Define the classes and their methods (including parameters and return types) for a system that consist of a bookshelf, books, magazines, and notebooks. The bookshelf should allow store and retrieval of the items as well as reporting on the state of the bookshelf (how many items it has, how many more items it can hold) and initializing the capacity (in number of items it can hold in total). The other items should allow reading of a single page given the page number that returns the text of the page. A book has an accessible title and author. A magazine has an accessible name. A notebook has an accessible owner.
<?php
abstract class BookShelfFactory{
/** Variables to hold BookShelf item and capacity */
private static $bookShelfItems = array();
private $bookShelfCapacity;
/** Initialize the capacity of BookShelf */
public function __construct(){
$this->bookShelfCapacity = 3;
}
/**
* Factory method that return abstract item.
* This lets subclasses return any concrete product without breaking the
* superclass' contract.
*/
abstract public function getBookShelfItem(): BookShelfItem;
/**
* When the factory method is used inside the Creator's business logic, the
* subclasses may alter the logic indirectly by returning different types of
* the item from the factory method.
*/
public function addItem(): int{
$item = $this->getBookShelfItem();
if (count(self::$bookShelfItems) < $this->bookShelfCapacity){
array_push(self::$bookShelfItems, $item);
end(self::$bookShelfItems);
return key(self::$bookShelfItems);
} else {
echo "The Bookshelf reach it's maximum capacity.\n";
return -1;
}
}
public function retrieveItem($itemIndex): void{
$instance = self::$bookShelfItems[$itemIndex];
$instance->getProductInfo();
}
public function reportStatus(): void{
$itemsInBookShelf = count(self::$bookShelfItems);
$remainingCapacity = $this->bookShelfCapacity - $itemsInBookShelf;
echo "Bookshelf contain {$itemsInBookShelf} items, ";
echo "remaining capacity is {$remainingCapacity}\n";
}
public function getItemContent($itemIndex, $pageNumber): string{
$instance = self::$bookShelfItems[$itemIndex];
$content = $instance->getPageContent($pageNumber);
return $content;
}
}
class BookFactory extends BookShelfFactory{
private $title, $author;
private $content = array();
public function __construct(string $title, string $author, array $content){
parent::__construct();
$this->title = $title;
$this->author = $author;
$this->content = $content;
}
public function getBookShelfItem(): BookShelfItem{
return new BookProduct($this->title, $this->author, $this->content);
}
}
class MagazineFactory extends BookShelfFactory{
private $name;
private $content = array();
public function __construct(string $name, array $content){
parent::__construct();
$this->name = $name;
$this->content = $content;
}
public function getBookShelfItem(): BookShelfItem{
return new MagazineProduct($this->name, $this->content);
}
}
class NotebookFactory extends BookShelfFactory{
private $owner;
private $content = array();
public function __construct(string $owner, array $content){
parent::__construct();
$this->owner = $owner;
$this->content = $content;
}
public function getBookShelfItem(): BookShelfItem{
return new NotebookProduct($this->owner, $this->content);
}
}
/**
* The Product interface declares behaviors of various types of products.
*/
interface BookShelfItem{
public function getProductInfo(): void;
public function getPageContent(int $pageNumber): string;
}
/**
* This concrete product implements the book
*/
class BookProduct implements BookShelfItem{
private $title, $author;
private $content = array();
public function __construct(string $title, string $author, array $content){
$this->title = $title;
$this->author = $author;
$this->content = $content;
}
public function getProductInfo(): void{
echo "Title of Book is {$this->title} authored by {$this->author}\n";
}
public function getPageContent(int $pageNumber): string{
// decrement 1 from page number as array is zero indexed
if (key_exists($pageNumber - 1, $this->content)){
return $this->content[$pageNumber - 1];
} else {
return "Given page doesn't exist.";
}
}
}
/**
* This concrete product implements the magazine
*/
class MagazineProduct implements BookShelfItem{
private $name;
private $content = array();
public function __construct(string $name, array $content){
$this->name = $name;
$this->content = $content;
}
public function getProductInfo(): void{
echo "Name of Magazine is {$this->name}\n";
}
public function getPageContent(int $pageNumber): string{
// decrement 1 from page number as array is zero indexed
if (key_exists($pageNumber - 1, $this->content)){
return $this->content[$pageNumber - 1];
} else {
return "Given page doesn't exist.";
}
}
}
/**
* This concrete product implements the notebook
*/
class NotebookProduct implements BookShelfItem{
private $owner;
private $content = array();
public function __construct(string $owner, array $content){
$this->owner = $owner;
$this->content = $content;
}
public function getProductInfo(): void{
echo "Owner of Notebook is {$this->owner}\n";
}
public function getPageContent(int $pageNumber): string{
// decrement 1 from page number as array is zero indexed
if (key_exists($pageNumber - 1, $this->content)){
return $this->content[$pageNumber - 1];
} else {
return "Given page doesn't exist.";
}
}
}
/**
* The client code can work with any subclass of BookShelfFactory since it
* doesn't depend on concrete classes.
*/
function clientCode(BookShelfFactory $creator){
$index = $creator->addItem();
if ($index >= 0){ // check if item is added
$creator->retrieveItem($index);
$pageNumber = 2;
echo "Content of page {$pageNumber}: ".$creator->getItemContent($index,$pageNumber)."\n";
$creator->reportStatus();
}
}
/**
* During the initialization phase, the app can decide which BookShelf item it
* wants to work with, create an object of the proper subclass, and pass it to
* the client code.
*/
echo "Testing BookFactory:\n";
clientCode(new BookFactory("Introduction to Algorithms",
"Ronald Rivest",
array("Book page1","Book page2")
)
);
echo "\n\n";
echo "Testing MagazineFactory:\n";
clientCode(new MagazineFactory("DataQuest magazine",
array("Magazine page1","Magazine page2")
)
);
echo "\n\n";
echo "Testing NotebookFactory:\n";
clientCode(new NotebookFactory("Whimpy Kid",
array("Notebook entry1","Notebook entry2")
)
);
echo "\n\n";
echo "Testing when capacity reached:\n";
clientCode(new BookFactory("The C Programming Language",
"keringhan & Ritchie",
array("Book page1","Book page2")
)
);
echo "\n\n";
The code is working fine as per best of my knowledge. I am looking for areas for improvement in the code following best practices.