Паттерн абстрактная фабрика PHP

Это замечательный пример реализации паттерна Абстрактная фабрика, код которого взят мной у этого автора (за что ему большое спасибо)).

Чтобы мы будем делать

мы будем создавать удобный инструмент (набор классов) получения набора книг, код которого построен так, чтобы "минимизировать" зависимость клиента от конкретного производителя и способа производства книг.

Код

/*
 * Классы книжных фабрик (фабрики)
 */

  /* обобщённый интерфейс фабрики -
 Это как раз и есть Абстрактная фабрика  - по этой сущности (этому классу)
и назван весь паттерн
*/
abstract class AbstractBookFactory { 
    abstract function makePHPBook();
    abstract function makeMySQLBook();
}

// конкретная книжная фабрика для издательства О'рейли
class OReillyBookFactory extends AbstractBookFactory { 
    private $context = "OReilly";
    function makePHPBook() { // метод создания книги о PHP
        return new OReillyPHPBook;
    }
    function makeMySQLBook() { // метод создания книги о MySQL
        return new OReillyMySQLBook;
    }
}

// конкретная книжная  фабрика для издательства Самс
class SamsBookFactory extends AbstractBookFactory {
    private $context = "Sams";
    function makePHPBook() { // метод создания книги о PHP
        return new SamsPHPBook;
    }
    function makeMySQLBook() {
        return new SamsMySQLBook;// метод создания книги о MySQL
    }
}

/*
 *  далее класс книг (продукты)
 */
// абстрактная книга
abstract class AbstractBook {
    abstract function getAuthor();
    abstract function getTitle();
}


// абстрактная книга по MYSQL
abstract class AbstractMySQLBook extends AbstractBook {
    private $subject = "MySQL";
}

//  книга по MYSQL от издательства О'РЕйли
class OReillyMySQLBook extends AbstractMySQLBook {
    private $author;
    private $title;
    function __construct() {
        $this->author = 'George Reese, Randy Jay Yarger, and Tim King';
        $this->title = 'Managing and Using MySQL';
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
}

//  книга по MYSQL от издательства Самс
class SamsMySQLBook extends AbstractMySQLBook {
    private $author;
    private $title;
    function __construct() {
        $this->author = 'Paul Dubois';
        $this->title = 'MySQL, 3rd Edition';
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
}


//  абстрактная книга по PHP 
abstract class AbstractPHPBook extends AbstractBook{
    private $subject = "PHP";
}

//  конкретная книга по PHP от издательства О'РЕйли
class OReillyPHPBook extends AbstractPHPBook {
    private $author;
    private $title;
    private static $oddOrEven = 'odd';
    function __construct()
    {
        //выбираем между двумя книгами
        if ('odd' == self::$oddOrEven) {
            $this->author = 'Rasmus Lerdorf and Kevin Tatroe';
            $this->title = 'Programming PHP';
            self::$oddOrEven = 'even';
        }
        else {
            $this->author = 'David Sklar and Adam Trachtenberg';
            $this->title = 'PHP Cookbook';
            self::$oddOrEven = 'odd';
        }
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
}


//  конкретная книга по PHP от издательства Самс
class SamsPHPBook extends AbstractPHPBook {
    private $author;
    private $title;
    function __construct() {
        //случайным образом выбираем одну книгу из двух
        mt_srand((double)microtime() * 10000000);
        $rand_num = mt_rand(0, 1);

        if (1 > $rand_num) {
            $this->author = 'George Schlossnagle';
            $this->title = 'Advanced PHP Programming';
        }
        else {
            $this->author = 'Christian Wenz';
            $this->title = 'PHP Phrasebook';
        }
    }
    function getAuthor() {
        return $this->author;
    }
    function getTitle() {
        return $this->title;
    }
}

На этом приведение кода участников паттерна завершено и мы можем перейти
к демонстрации его работы:

/*
 *   Инициаллизация
 */

  writeln('Начинаем тестировать паттерн Абстрактная фабрика');

  writeln('тестируем работу конкретной фабрики  издательства OReilly');
  $bookFactoryInstance = new OReillyBookFactory; // создаём экземпляр фабрики
  
// запускаем функцию тестирования передав в неё в качестве параметра 
// экземпляр нашей фабрики
   testConcreteFactory($bookFactoryInstance); // запустили тестирование


  writeln('тестируем работу конкретной фабрики  издательства Sams');
  $bookFactoryInstance = new SamsBookFactory; // создаём экземпляр фабрики
  testConcreteFactory($bookFactoryInstance); // запустили тестирование

  writeln("Тесты завершены");

/*
далее приводим код самой функции тестирование - она-то
как раз и демонстрирует "удобство" применения паттерна Абстрактная фабрика

*/
  function testConcreteFactory($bookFactoryInstance) // принимает объект конретной фабрики
  {
      // вызываем  метод создания PHP книги - наш код не зависит от того какое именно издательство
// передано (фабрика какого именно издательства передана) для реализации создания книг    
      $phpBookOne = $bookFactoryInstance->makePHPBook(); 
      writeln('Автор первой книги  по PHP: '.$phpBookOne->getAuthor());
      writeln('Заголовок первой книги  по PHP: '.$phpBookOne->getTitle());

      $phpBookTwo = $bookFactoryInstance->makePHPBook();
      writeln('Автор второй книги по PHP: '.$phpBookTwo->getAuthor());
      writeln('Заголовок второй книги по PHP:: '.$phpBookTwo->getTitle());

      $mySqlBook = $bookFactoryInstance->makeMySQLBook();
      writeln('Автор  книги по MySQL: '.$mySqlBook->getAuthor());
      writeln(' Заголовок книги по MySQL: '.$mySqlBook->getTitle());
  }

  function writeln($line_in) {
    echo $line_in."<br/>";
  }

Заметьте, что testConcreteFactory() - это как раз и есть тот самый Клиент (Client) из списка участников абстрактной фабрики
И ещё - в данном пример введено обобщение над продуктами -

abstract class AbstractBook

- в классическом варианте это не обязательно. То есть в примере выше - в ходе демонстрации клиент использует только методы абстракции
AbstractBook - то есть получает автора и заголовок но в классическом варианте - см. диаграмму "Структура" - такаого обобщения нет - то есть клиент "знает" о том как устроены интерфейсы видов продуктов - и например у PHP-книги может быть какой-то отдельный метод "Получить цвет обложки", которого у MySQL-книги нет -
то есть в Абстрактной фабрике продукты разных видов (то есть например - стулья, столы и кружки) в общем случае не обязаны иметь одинаковый набор методов -= интерфейс.

В результате - запусти тест - мы получим что-то вроде:

Начинаем тестировать паттерн Абстрактная фабрика
тестируем работу конкретной фабрики издательства OReilly

Автор первой книги по PHP: Rasmus Lerdorf and Kevin Tatroe
Заголовок первой книги по PHP: Programming PHP

Автор второй книги по PHP: David Sklar and Adam Trachtenberg
Заголовок второй книги по PHP:: PHP Cookbook

Автор книги по MySQL: George Reese, Randy Jay Yarger, and Tim King
Заголовок книги по MySQL: Managing and Using MySQL

тестируем работу конкретной фабрики издательства Sams
Автор первой книги по PHP: Christian Wenz
Заголовок первой книги по PHP: PHP Phrasebook

Автор второй книги по PHP: Christian Wenz
Заголовок второй книги по PHP:: PHP Phrasebook

Автор книги по MySQL: Paul Dubois
Заголовок книги по MySQL: MySQL, 3rd Edition

Тесты завершены

Key Words for FKN + antitotal forum (CS VSU):