大話 PHP 設計模式--建立型

無名花火發表於2019-09-02

隨著程式設計專案經驗的增加,從服務於業務邏輯到針對專案的全域性設計.認識到設計模式在開發過程中\
的重要性,遵循S.O.L.I.D五大基準原則.它擴充了我的視野,讓程式碼更加靈活,看起來更加富有美感.\
美是構建萬物的哲學思想.

我們學習的設計模式分為三類:建立者模式、結構型模式、行為型模式;建立型模式與物件的建立有關;結構型模式處理類或物件的組合;而行為型模式是對類或物件怎樣互動和怎樣分配職責進行描述;

內容:本文介紹的是PHP設計模式的建立型一篇.包括:單例模式(Singleton),多例模式(Multiton),工廠方法模式(Factory Method),抽象工廠模式(Abstract Factory),簡單工廠模式(Simple Factory),原型模式(Prototype),物件池模式(Pool),建造者模式(Builder)

(一)單例模式(Singleton)

  • 定義
    保證一個類只有一個例項,並且提供一個訪問它的全域性訪問點。系統記憶體中該類只存在一個物件,節省了系統資源,對於一些需要頻繁建立銷燬的物件,使用單例模式可以提高系統效能。
  • 程式碼示例
    class Singleton
    {
        /**
        * @var Singleton
        */
        private static $instance;
        /**
        * 不允許從外部呼叫以防止建立多個例項
        * 要使用單例,必須通過 Singleton::getInstance() 方法獲取例項
        */
        private function __construct()
        {
        }
        /**
        * 通過懶載入獲得例項(在第一次使用的時候建立)
        */
        public static function getInstance(): Singleton
        {
            if (null === static::$instance) {
                static::$instance = new static();
            }
            return static::$instance;
        }
        /**
        * 防止例項被克隆(這會建立例項的副本)
        */
        private function __clone()
        {
        }
        /**
        * 防止反序列化(這將建立它的副本)
        */
        private function __wakeup()
        {
        }
    }

    *(二)多例模式(Multiton)

  • 定義
    在多例模式中,多例類可以有多個例項,而且多例類必須自己建立、管理自己的例項,並向外界提供自己的例項。1. 通過例項容器儲存容器。2. 利用私有構造阻止外部構造。3. 提供getInstantce()方法獲取例項.
  • 程式碼示例  兩個物件通過一個類進行多次例項化
    abstract class Multiton { 
        private static $instances = array(); 
        public static function getInstance() { 
            $key = get_called_class() . serialize(func_get_args());
            if (!isset(self::$instances[$key])) { 
                $rc = new ReflectionClass(get_called_class());
                self::$instances[$key] = $rc->newInstanceArgs(func_get_args());
            }
            return self::$instances[$key]; 
        }
        /**
         * 該私有物件阻止例項被克隆
         */
        private function __clone()
        {
        }
        /**
         * 該私有方法阻止例項被序列化
         */
        private function __wakeup()
        {
        }
    } 
    class Hello extends Multiton { 
        public function __construct($string = 'World') { 
            echo "Hello $string\n"; 
        } 
    } 
    class GoodBye extends Multiton { 
        public function __construct($string = 'my', $string2 = 'darling') { 
            echo "Goodbye $string $string2\n"; 
        }
    }
    $a = Hello::getInstance('World'); 
    $b = Hello::getInstance('bob'); 
    // $a !== $b 
    $c = Hello::getInstance('World'); 
    // $a === $c 
    $d = GoodBye::getInstance(); 
    $e = GoodBye::getInstance();
    // $d === $e 
    $f = GoodBye::getInstance('your'); 
    // $d !== $f 

    *(三)工廠方法模式(Factory Method)

  • 定義
    將類的例項化(具體產品的建立)延遲到工廠類的子類(具體工廠)中完成,即由子類來決定應該例項化(建立)哪一個類
  • 程式碼示例 : 小成有一間塑料加工廠(僅生產A類產品);隨著客戶需求的變化,客戶需要生產B類產品。改變原有塑料加工廠的配置和變化非常困難,假設下一次客戶需要再發生變化,再次改變將增大非常大的成本;小成決定置辦塑料分廠B來生產B類產品。
    abstract class Product{
        public abstract function Show();
    }
    //具體產品A類
    class  ProductA extends  Product{
        public function Show() {
            echo "生產出了產品A";
        }
    }
    //具體產品B類
    class  ProductB extends  Product{
        public function Show() {
            echo "生產出了產品B";
        }
    }
    abstract class Factory{
        public abstract function Manufacture();
    }
    //工廠A類 - 生產A類產品
    class  FactoryA extends Factory{
        public function Manufacture() {
            return new ProductA();
        }
    }
    //工廠B類 - 生產B類產品
    class  FactoryB extends Factory{
        public function Manufacture() {
            return new ProductB();
        }
    }

    *(四)抽象工廠模式(Abstract Factory)

  • 定義
    在不指定具體類的情況下建立一系列相關或依賴物件。 通常建立的類都實現相同的介面。 抽象工廠的客戶並不關心這些物件是如何建立的,它只是知道它們是如何一起執行的。
  • 程式碼示例 : 有兩個工廠,A工廠負責運輸,B工廠生產數碼產品.
    interface Product
    {
        public function calculatePrice(): int;
    }
    class ShippableProduct implements Product
    {
        /**
         * @var float
         */
        private $productPrice;
        /**
         * @var float
         */
        private $shippingCosts;
        public function __construct(int $productPrice, int $shippingCosts)
        {
            $this->productPrice = $productPrice;
            $this->shippingCosts = $shippingCosts;
        }
        public function calculatePrice(): int
        {
            return $this->productPrice + $this->shippingCosts;
        }
    }
    class DigitalProduct implements Product
    {
        /**
         * @var int
         */
        private $price;
        public function __construct(int $price)
        {
            $this->price = $price;
        }
        public function calculatePrice(): int
        {
            return $this->price;
        }
    }
    class ProductFactory
    {
        const SHIPPING_COSTS = 50;
        public function createShippableProduct(int $price): Product
        {
            return new ShippableProduct($price, self::SHIPPING_COSTS);
        }
        public function createDigitalProduct(int $price): Product
        {
            return new DigitalProduct($price);
        }
    }

    *(五)簡單工廠模式(Simple Factory)

  • 定義
    簡單工廠模式是一個精簡版的工廠模式。工廠角色-具體產品-抽象產品
  • 程式碼示例 : 原文連結:https://www.cnblogs.com/aqsmoke/p/3955310....
    一個農場,要向市場銷售水果.農場裡有三種水果 蘋果、葡萄,我們設想:1、水果有多種屬性,每個屬性都有不同,但是,他們有共同的地方 | 生長、種植、收貨、吃。將來有可能會增加新的水果、我們需要定義一個介面來規範他們必須實現的方法.
    interface fruit{
        /**
         * 生長
         */
        public function grow();
        /**
         * 種植
         */
        public function plant();
        /**
         * 收穫
         */
        public function harvest();
        /**
         * 吃
         */
        public function eat();
    }
    class apple implements fruit{
        //蘋果樹有年齡
        private $treeAge;
        //蘋果有顏色
        private $color;
        public function grow(){
            echo "grape grow";
        }
        public function plant(){
            echo "grape plant";
        }
        public function harvest(){
            echo "grape harvest";
        }
        public function eat(){
            echo "grape eat";
        }
        //取蘋果樹的年齡
        public function getTreeAge(){
            return $this->treeAge;
        }
        //設定蘋果樹的年齡
        public function setTreeAge($age){
            $this->treeAge = $age;
            return true;
        }
    }
    class grape implements fruit{
        //葡萄是否有籽
        private $seedLess;
        public function grow(){
            echo "apple grow";
        }
        public function plant(){
            echo "apple plant";
        }
        public function harvest(){
            echo "apple harvest";
        }
        public function eat(){
            echo "apple eat";
        }
        //有無籽取值
        public function getSeedLess(){
            return $this->seedLess;
        }
        //設定有籽無籽
        public function setSeedLess($seed){
            $this->seedLess = $seed;
            return true;
        }
    }
    class farmer
    {
        //定義個靜態工廠方法
        public static function factory($fruitName){
            switch ($fruitName) {
                case 'apple':
                    return new apple();
                    break;
                case 'grape':
                    return new grape();
                    break;
                default:
                    throw new badFruitException("Error no the fruit", 1);
                    break;
            }
        }
    }
    class badFruitException extends Exception
    {
        public $msg;
        public $errType;
        public function __construct($msg = '' , $errType = 1){
            $this->msg = $msg;
            $this->errType = $errType;
        }  
    }
    /**
     * 獲取水果例項化的方法
     */
    try{
        $appleInstance = farmer::factory('apple');
        var_dump($appleInstance);
    }catch(badFruitException $err){
        echo $err->msg . "_______" . $err->errType;
    }

    *(六)原型模式(Prototype)

  • 定義
    相比正常建立一個物件 (new Foo () ),首先建立一個原型,然後克隆它會更節省開銷。
  • 程式碼示例 : 為每一本書設定標題
    abstract class BookPrototype
    {
        /**
        * @var string
        */
        protected $title = 0;
        /**
        * @var string
        */
        protected $category;
        abstract public function __clone();
        public function getTitle(): string
        {
            return $this->title;
        }
        public function setTitle($title)
        {
           $this->title = $title;
        }
    }
    class BarBookPrototype extends BookPrototype
    {
        /**
        * @var string
        */
        protected $category = 'Bar';
        public function __clone()
        {
        }
    }
    class FooBookPrototype extends BookPrototype
    {
        /**
        * @var string
        */
        protected $category = 'Foo';
        public function __clone()
        {
        }
    }
    $fooPrototype = new FooBookPrototype();
    $barPrototype = new BarBookPrototype();
    for ($i = 5; $i < 10; $i++) {
        $book = clone $fooPrototype;
        $book->setTitle('Foo Book No ' . $i);
        var_dump(new FooBookPrototype == $book);
    }
    for ($i = 0; $i < 5; $i++) {
        $book = clone $barPrototype;
        $book->setTitle('Bar Book No ' . $i);
        var_dump(new BarBookPrototype == $book);
    }

    *(七)物件池模式(Pool)

  • 定義
    物件池可以用於構造並且存放一系列的物件並在需要時獲取呼叫。在初始化例項成本高,例項化率高,可用例項不足的情況下,物件池可以極大地提升效能。在建立物件(尤其是通過網路)時間花銷不確定的情況下,通過物件池在短期時間內就可以獲得所需的物件。
  • 程式碼示例 
    class Factory {
        protected static $products = array();
        public static function pushProduct(Product $product) {
            self::$products[$product->getId()] = $product;
        }
        public static function getProduct($id) {
            return isset(self::$products[$id]) ? self::$products[$id] : null;
        }
        public static function removeProduct($id) {
            if (array_key_exists($id, self::$products)) {
                unset(self::$products[$id]);
            }
        }
    }
    Factory::pushProduct(new Product('first'));
    Factory::pushProduct(new Product('second'));
    print_r(Factory::getProduct('first')->getId());
    // first
    print_r(Factory::getProduct('second')->getId());
    // second

    *(八)建造者模式(Builder)

  • 定義
    將一個複雜物件的構建與它的表示分離,使得同樣的構建過程可以建立不同的表示
  • 2)程式碼示例  建造相同標準的卡車和汽車.類似於變形金剛,相同的零件進行不同的組合.
  • 分為 Director 導演者,負責構建、BuilderInterface 構建介面,規範建造標準、TruckBuilder 構建卡車類 CarBuilder 構建汽車類
    Vehicle 零部件公共類、Truck Car Engine Wheel Door 零部件類、DirectorTest 測試類
    class Director
    {
        public function build(BuilderInterface $builder): Vehicle
        {
            $builder->createVehicle();
            $builder->addDoors();
            $builder->addEngine();
            $builder->addWheel();
            return $builder->getVehicle();
        }
    }
    interface BuilderInterface
    {
        public function createVehicle();
        public function addWheel();
        public function addEngine();
        public function addDoors();
        public function getVehicle(): Vehicle;
    }
    class TruckBuilder implements BuilderInterface
    {
        /**
        * @var Truck
        */
        private $truck;
        public function addDoors()
        {
            $this->truck->setPart('rightDoor', new Door());
            $this->truck->setPart('leftDoor', new Door());
        }
        public function addEngine()
        {
            $this->truck->setPart('truckEngine', new Engine());
        }
        public function addWheel()
        {
            $this->truck->setPart('wheel1', new Wheel());
            $this->truck->setPart('wheel2', new Wheel());
            $this->truck->setPart('wheel3', new Wheel());
            $this->truck->setPart('wheel4', new Wheel());
            $this->truck->setPart('wheel5', new Wheel());
            $this->truck->setPart('wheel6', new Wheel());
        }
        public function createVehicle()
        {
            $this->truck = new Truck();
        }
        public function getVehicle(): Vehicle
        {
            return $this->truck;
        }
    }
    class CarBuilder implements BuilderInterface
    {
        /**
        * @var Car
        */
        private $car;
        public function addDoors()
        {
            $this->car->setPart('rightDoor', new Door());
            $this->car->setPart('leftDoor', new Door());
            $this->car->setPart('trunkLid', new Door());
        }
        public function addEngine()
        {
            $this->car->setPart('engine', new Engine());
        }
        public function addWheel()
        {
            $this->car->setPart('wheelLF', new Wheel());
            $this->car->setPart('wheelRF', new Wheel());
            $this->car->setPart('wheelLR', new Wheel());
            $this->car->setPart('wheelRR', new Wheel());
        }
        public function createVehicle()
        {
            $this->car = new Car();
        }
        public function getVehicle(): Vehicle
        {
            return $this->car;
        }
    }
    abstract class Vehicle
    {
        /**
        * @var object[]
        */
        private $data = [];
        /**
        * @param string $key
        * @param object $value
        */
        public function setPart($key, $value)
        {
            $this->data[$key] = $value;
        }
    }
    class Truck extends Vehicle
    {
    }
    class Car extends Vehicle
    {
    }
    class Engine extends Vehicle
    {
    }
    class Wheel extends Vehicle
    {
    }
    class Door extends Vehicle
    {
    }
    class DirectorTest
    {
        public function testCanBuildTruck()
        {
            $truckBuilder = new TruckBuilder();
            return (new Director())->build($truckBuilder);
        }
        public function testCanBuildCar()
        {
            $carBuilder = new CarBuilder();
            return (new Director())->build($carBuilder);
        }
    }
    $directorTest = new DirectorTest();
    var_dump($directorTest->testCanBuildTruck());
    var_dump($directorTest->testCanBuildCar());

上善若水

相關文章