设计模式学习系列——工厂模式 工厂模式

  工厂模式属于创建型模式,它的目的就是创建对象。在创建对象时不会对客户端暴露创建逻辑,并且是通过使用一个共同的接口来指向新创建的对象。

       工厂模式还分为简单工厂,工厂方法模式和抽象工厂模式,个人觉得就是复杂度不同,他的目的就是隐藏细节,创建对象。

简单工厂

  简单工厂模式又叫静态工厂方法模式(Static FactoryMethod Pattern),是通过专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。

  参考php代码:

<?php
/*
 * 简单工厂模式示例
 */
     class Cat{
        public $name;
        public $instruction = "I'm a cat.";
        
        public function getInstruction(){
            return $this->instruction;
        }
    }
    
     class Dog{
        public $name;
        public $instruction = "I'm a Dog.";
        
        public function getInstruction(){
            return $this->instruction;
        }
    }    
    class AnimalFactory{
        public static function create($type){
            return new $type();
        }
    }

$cat = AnimalFactory::create("Cat");
echo $cat->getInstruction();//输出I'm a cat.
?>
View Code

AnimalFactory::create是一个构造器,根据参数返回不同的动物对象。

同理,javascript代码:

<!DOCTYPE html>
<html>
    <head>
        <title>简单工厂模式示例</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script>
            Cat = function(){
                this.instruction = "I'm a cat.";
                this.getInstruction = function(){
                    return this.instruction;
                }
            }
 
            Dog = function(){
                this.instruction = "I'm a dog.";
                this.getInstruction = function(){
                    return this.instruction;
                }
            }
            
            AnimalFactory = function(){
            }
            AnimalFactory.create = function(type){//使用js中的类方法来定义静态方法,类方法只能被类调用而不能被类的实例调用
                    return eval("new "+ type +"();");
            }
            
            var cat = AnimalFactory.create("Cat");
            console.log(cat.getInstruction());//I'm a cat.
        </script>    
    </head>
</html>
View Code

优点:用户在使用时可以直接根据工厂类去创建所需的实例,而无需了解这些对象是如何创建以及如何组织的。有利于整个软件体系结构的优化。简单,方便。

缺点:所有的创建逻辑都集中于工厂类中(即AnimalFactory中),违背了单一职责,导致系统丧失灵活性和可维护性。违背了“开放封闭原则”,就是违背了“系统对扩展开放,对修改关闭”的原则。另外,简单工厂模式的方法一般都是静态的,而静态工厂方法是无法让子类继承的,因此,简单工厂模式无法形成基于基类的继承树结构。

适用场景:

工厂方法模式

  工厂方法模式(FACTORY METHOD)是一种常用的对象创建型设计模式,此模式的核心精神是封装类中不变的部分,提取其中个性化善变的部分为独立类,通过依赖注入以达到解耦、复用和方便后期维护拓展的目的。

  核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品(这个也是简单工厂模式做不到的)。

   这句话怎么理解呢,以上面的例子看,AnimalFactory类不再负责创建具体产品,仅仅是作为一个接口了。创建具体产品这些事情交给具体工厂去做。这样的好处是再引进牛,猪等其他动物的时候不需要修改AnimalFactory类了。

  再来看工厂方法模式的四种角色:

       1)抽象工厂(Creator)角色:工厂方法模式的核心,任何在模式中创建的对象的工厂类必须实现这个接口。即要生产牛,猪等动物的具体工厂都要实现这个接口。

        2)具体工厂(Concrete Creator)角色:这是实现抽象工厂接口的具体工厂类,包含与应用程序密切相关的逻辑,并且受到应用程序调用以创建产品对象。生成猫,狗等产品的具体工厂。

  3)抽象产品(Product)角色:工厂方法模式所创建的对象的超类型,也就是产品对象的共同父类或共同拥有的接口。也就具体产品共同拥有东西放在父类中,提取出来。

  4)具体产品(Concrete Product)角色:这个角色实现了抽象产品角色所定义的接口。某具体产品有专门的具体工厂创建,它们之间往往一一对应。

  好的,理论说了这么多。看一看php的实例代码:

<?php
/*
 * 工厂方法模式示例
 */

    /*
     * 工厂接口(抽象工厂) 
     */
    interface  IAnimal{
        public function create();
    }
    /*
     * 具体工厂(Concrete Creator)角色:猫工厂 
     */
    class CatFactory implements IAnimal{
        public function create(){
            return new Cat();
        }
    }
    
    /*
     * 具体工厂(Concrete Creator)角色:狗工厂 
     */
    class DogFactory implements IAnimal{
        public function create(){
            return new Dog();
        }
    }
    
    /** 
     * 抽象产品(Product)角色:动物特征 
     */
     abstract class AnimalFeatures   { 
          private  $name = ""; 
          
          public function __construct($name="") {
              $this->setName($name);
          }
          public function setName($name){
              $this->name = $name;
          }
          public function getName(){
              return $this->name;
          }
          public function getInstruction(){
              return "I'm an animal.";
          }
     }
    
    /** 
     * 具体产品(Concrete Product)角色:猫
     * 
     */      
     class Cat extends AnimalFeatures{
         public $price = 50;
          //@Override 
         public function getInstruction(){
             return "I'm a cat.";
         }
     }
    
    /** 
     * 具体产品(Concrete Product)角色:狗
     * 
     */      
     class Dog extends AnimalFeatures{
         public $price = 60;
          //@Override 
         public function getInstruction(){
             return "I'm a dog.";
         }
     }     
$catFactory = new CatFactory();
$cat = $catFactory->create();
echo $cat->getName();//输出空
echo "<br/>";
$cat->setName("Jimmy");
echo $cat->getName();//输出Jimmy
echo "<br/>";
echo $cat->getInstruction();//输出I'm a cat.

?>
View Code

通过例子我们看到,IAnimal是一个抽象的接口,并不和实际应用打交道,应用程序通过实例化CatFactory生成工厂类,通过 CatFactory::create方法,实例化一个cat。AnimalFeatures将所有具体产品的公共属性抽象出来,具体的Dog和Cat在它基础上扩展。这个设计模式的好处是,如果有新的产品来,例如Pig,无需修改IAnimal方法,只需要增加一个PigFactory具体工厂实现类,再增加一个Pig具体产品类即可。

与之对应的js代码:

<!DOCTYPE html>
<html>
    <head>
        <title>工厂方法模式示例</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <script>
           /*
            * 为了简便,这里采用注释法来定义接口,并没有对实现类做严格的检测
            * 工厂接口(抽象工厂) 
            interface IAnimal{
            function create();
            }
            */ 
            
            /*
             * 具体工厂(Concrete Creator)角色:猫工厂 
             */
            CatFactory = function(){
                this.create = function(){
                    return new Cat();
                };
            }
            
            /*
             * 具体工厂(Concrete Creator)角色:狗工厂 
             */
            DogFactory = function(){
                this.create = function(){
                    return new Dog();
                };
            }            
            
            /** 
             * 抽象产品(Product)角色:动物特征 
             * (用一个抽象类来实现)
             */
             AnimalFeatures = function(name){
                 this.name = name;
             };
             AnimalFeatures.prototype = {
                 setName: function(name){this.name = name},
                 getName: function(){return this.name;},
                 getInstruction:function(){throw 'You must implement abstractMethod getInstruction';}//定义一个抽象方法
             };

            /** 
             * 具体产品(Concrete Product)角色:猫
             */     
             Cat = function(){
                 AnimalFeatures.apply(this);
             }
             Cat.prototype = Object.create(AnimalFeatures.prototype);
             Cat.prototype.constructor =Cat; 
             Cat.prototype.price = 50;
             Cat.prototype.getInstruction = function(){//重写抽象类getInstruction方法
                 return "I'm a cat.";
             }
                     
            /** 
             * 具体产品(Concrete Product)角色:狗
             */     
             Dog = function(){
                 AnimalFeatures.apply(this);
             }
             Dog.prototype = Object.create(AnimalFeatures.prototype);
             Dog.prototype.constructor =Cat; 
             Dog.prototype.price = 60;
             Dog.prototype.getInstruction = function(){//重写抽象类getInstruction方法
                 return "I'm a dog.";
             }
             
             var catFactory = new CatFactory();
             var cat = catFactory.create();
             console.log(cat.getName());//undefined
             cat.setName("Jimmy");
             console.log(cat.getName());//Jimmy
             console.log(cat.getInstruction());//I'm a cat.
        </script>    
    </head>
</html>
View Code

抽象工厂模式

   定义:就是对一组具有相同主题的工厂进行封装(*解释的很到位);

  例如:生产一台PC机,使用工厂方法模式的话,一般会有cpu工厂,内存工厂,显卡工厂...但是使用抽象工厂模式的话,只有一个工厂就是PC工厂,但是一个PC工厂涵盖了cpu工厂,内存工厂,显卡工厂等要做的所有事。

  注意,这里有一个相同主题的概念,不能将cpu工厂和面粉工厂封装成一个工厂,因为他们不是属于同一个产品族。

  另外,还有一个产品等级的概念,还是以生产PC机为例,所谓的产品等级指的是不同厂商生产的CPU,如Intel和AMD的CPU,他们是同一个产品等级,如果只涉及产品等级的话,是不需要应用抽象工厂模式,使用工厂方法模式即可;

  工厂方法模式解决的范畴是产品等级(AMD处理器,Intel处理器等);抽象工厂模式解决的范畴是产品族等级(联想PC、惠普PC等)。

  我们还是以动物来举例,假如你去宠物市场,猫,狗等宠物属于一个产品族,猫粮,狗粮等宠物吃的食物属于一个产品族。注意,这里猫和猫粮是一个并列关系,不是一个包含或者从属的关系。而养宠物这见事情可以抽象为一个总的工厂。这里就可以运用抽象工厂的概念。

  请看php代码:

<?php
/*
 * 抽象工厂模式示例
 */
error_reporting(E_ALL);
ini_set('display_errors','on');
    
    //产品族Food
    interface IFood{
        public function getNum();        
    }
    //产品——FoodBone类
    class FoodBone implements IFood{
        private $num;
        public function __construct($num) {
            $this->num = $num;
        }
        public function getNum(){
            return $this->num;
        }        
    }
    //产品——FoodFish类
    class FoodFish implements IFood{
        private $num;
        public function __construct($num) {
            $this->num = $num;
        }
        public function getNum(){
            return $this->num;
        }        
    }      
    
    //产品族Pet
    interface IPet{
        public function getName();
    }
    //产品——Cat类
    class Cat implements IPet{
        private $name;
        public function __construct($name) {
            $this->name = $name;
        }
        public function getName(){
            return $this->name;
        }
    }
    //产品——dog类
    class Dog implements IPet{
        private $name;
        public function __construct($name) {
            $this->name = $name;
        }
        public function getName(){
            return $this->name;
        }       
    }
    
    //养宠物的接口
    interface  IKeepPetFactory{
        public function createPet($name);
        public function createFood($num);
    }
    //养猫的工厂类
    class KeepCatFactory implements IKeepPetFactory{
        public function createPet($name){
            return new Cat($name);
        }
        public function createFood($num) {
            return new FoodFish($num);
        }
    }
    //养狗的工厂类
    class KeepDogFactory implements IKeepPetFactory{
        public function createPet($name){
            return new Dog($name);
        }
        public function createFood($num) {
            return new FoodBone($num);
        }
    }    
    
    
    $keepDog = new KeepDogFactory();
    $dog = $keepDog->createPet("Tom");
    $dogFood = $keepDog->createFood(5);
    
    echo $dog->getName();//Tom
    echo "<br />";
    echo $dogFood->getNum();//5

?>
View Code

    参考资料:

工厂模式介绍

简单工厂模式、工厂方法模式和抽象工厂模式有何区别?

工厂模式(百度百科)

简单工厂(百度百科)

设计模式(百度百科)

简单工厂模式例子

js中静态方法(属性)、实例方法(属性)、内部方法(属性)和原型的一点见解

 js的抽象方法

DesignPattern

【设计模式】抽象工厂模式