php设计模式,二:创建型设计模式

创建型模式:

单例模式、工厂模式(简单工厂、工厂方法、抽象工厂)、创建者模式、原型模式。

1、单例模式

目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

应用场景:数据库连接、缓存操作、分布式存储。

 1 <?php
 2 /**
 3  *
 4  * 单例模式
 5  *
 6  */
 7      
 8 class DbConn
 9 {
10      
11        private static $_instance = null;
12        protected static $_counter = 0;
13        protected $_db;
14           
15        //私有化构造函数,不允许外部创建实例
16        private function __construct()
17        {
18               self::$_counter += 1;
19        }
20      
21        public function getInstance()
22        {
23               if (self::$_instance == null)
24               {
25                      self::$_instance = new DbConn();
26               }
27               return self::$_instance;
28        }
29      
30        public function connect()
31        {
32               echo "connected: ".(self::$_counter)."n";
33               return $this->_db;
34        }
35      
36 }
37      
38 /*
39  * 不使用单例模式时,删除构造函数的private后再测试,第二次调用构造函数后,_counter变成2
40 */
41 // $conn = new DbConn();
42 // $conn->connect();
43 // $conn = new DbConn();
44 // $conn->connect();
45      
46 //使用单例模式后不能直接new对象,必须调用getInstance获取
47 $conn = DbConn::getInstance();
48 $db = $conn->connect();
49 //第二次调用是同一个实例,_counter还是1
50 $conn = DbConn::getInstance();
51 $db = $conn->connect();
52 ?>

特别说明:这里getInstance里有if判断然后再生成对象,在多线程语言里是会有并发问题的。例如java的解决方案有二个,给方法加上synchronized关键词变成同步,或者把_instanc的初始化提前放到类成员变量定义时,但是这2种方式php都不支持。不过因为php不支持多线程所以不需要考虑这个问题了。

2、工厂模式

实现:定义一个用于创建对象的接口,让子类决定实例化哪一个类。

应用场景:众多子类并且会扩充、创建方法比较复杂。

  1     <?php
  2     /**
  3      * 工厂模式
  4      *
  5      */
  6          
  7     //抽象产品
  8     interface Person {
  9         public function getName(); 
 10     }
 11     //具体产品实现
 12     class Teacher implements Person {
 13         function getName() {
 14             return "老师n";
 15         }
 16     }
 17     class Student implements Person {
 18         function getName() {
 19             return "学生n";
 20         }
 21     }
 22          
 23     //简单工厂
 24     class SimpleFactory {
 25            public static function getPerson($type) {
 26                   $person = null;
 27                   if ($type == 'teacher') {
 28                          $person = new Teacher();
 29                   } elseif ($type == 'student') {
 30                          $person = new Student();
 31                   }
 32                   return $person;
 33            }
 34     }
 35          
 36     //简单工厂调用
 37     class SimpleClient {
 38            function main() {
 39                   // 如果不用工厂模式,则需要提前指定具体类
 40                   // $person = new Teacher();
 41                   // echo $person->getName();
 42                   // $person = new Student();
 43                   // echo $person->getName();
 44          
 45                   // 用工厂模式,则不需要知道对象由什么类产生,交给工厂去决定
 46                   $person = SimpleFactory::getPerson('teacher');
 47                   echo $person->getName();
 48                   $person = SimpleFactory::getPerson('student');
 49                   echo $person->getName();
 50            }
 51     }
 52          
 53          
 54     //工厂方法
 55     interface CommFactory {
 56         public function getPerson();
 57     }
 58     //具体工厂实现
 59     class StudentFactory implements CommFactory {
 60         function getPerson(){
 61             return new Student();
 62         }
 63     }
 64     class TeacherFactory implements CommFactory {
 65         function getPerson() {
 66             return new Teacher();
 67         }
 68     }
 69          
 70     //工厂方法调用
 71     class CommClient {
 72         static function main() {
 73                $factory = new TeacherFactory();
 74                echo $factory->getPerson()->getName();
 75                $factory = new StudentFactory();
 76                echo $factory->getPerson()->getName();
 77         }
 78     }
 79          
 80          
 81          
 82     //抽象工厂模式另一条产品线
 83     interface Grade {
 84            function getYear();
 85     }
 86     //另一条产品线的具体产品
 87     class Grade1 implements Grade {
 88            public function getYear() {
 89                   return '2003级';
 90            }
 91     }
 92     class Grade2 implements Grade {
 93            public function getYear() {
 94                   return '2004级';
 95            }
 96     }
 97     //抽象工厂
 98     interface AbstractFactory {
 99            function getPerson();
100            function getGrade();
101     }
102     //具体工厂可以产生每个产品线的产品
103     class Grade1TeacherFactory implements AbstractFactory {
104            public function getPerson() {
105                   return new Teacher();
106            }
107            public function getGrade() {
108                   return new Grade1();
109            }
110     }
111     class Grade1StudentFactory implements AbstractFactory {
112            public function getPerson() {
113                   return new Student();
114            }
115            public function getGrade() {
116                   return new Grade1();
117            }
118     }
119     class Grade2TeacherFactory implements AbstractFactory {
120            public function getPerson() {
121                   return new Teacher();
122            }
123            public function getGrade() {
124                   return new Grade2();
125            }
126     }
127     //抽象工厂调用
128     class FactoryClient {
129            function printInfo($factory) {
130                   echo $factory->getGrade()->getYear().$factory->getPerson()->getName();
131            }
132            function main() {
133                   $client = new FactoryClient();
134                   $factory = new Grade1TeacherFactory();
135                   $client->printInfo($factory);
136                   $factory = new Grade1StudentFactory();
137                   $client->printInfo($factory);
138                   $factory = new Grade2TeacherFactory();
139                   $client->printInfo($factory);
140            }
141     }
142          
143          
144     //简单工厂
145     //SimpleClient::main();
146     //工厂方法
147     //CommClient::main();
148     //抽象工厂
149     FactoryClient::main();
150          
151     ?>

三种工厂的区别是,抽象工厂由多条产品线,而工厂方法只有一条产品线,是抽象工厂的简化。而工厂方法和简单工厂相对,大家初看起来好像工厂方法增加了许多代码但是实现的功能和简单工厂一样。但本质是,简单工厂并未严格遵循设计模式的开闭原则,当需要增加新产品时也需要修改工厂代码。但是工厂方法则严格遵守开闭原则,模式只负责抽象工厂接口,具体工厂交给客户去扩展。在分工时,核心工程师负责抽象工厂和抽象产品的定义,业务工程师负责具体工厂和具体产品的实现。只要抽象层设计的好,框架就是非常稳定的。

3、创建者模式

在创建者模式中,客户端不再负责对象的创建与组装,而是把这个对象创建的责任交给其具体的创建者类,把组装的责任交给组装类,客户端支付对对象的调用,从而明确了各个类的职责。

应用场景:创建非常复杂,分步骤组装起来。

 1     <?php
 2     /**
 3      * 创建者模式
 4      */
 5          
 6     //购物车
 7     class ShoppingCart {
 8            //选中的商品
 9         private $_goods = array();
10         //使用的优惠券
11         private $_tickets = array();
12          
13            public function addGoods($goods) {
14                   $this->_goods[] = $goods;
15            }
16            
17         public function addTicket($ticket) {
18                $this->_tickets[] = $ticket;
19         }
20          
21         public function printInfo() {
22                printf("goods:%s, tickets:%sn", implode(',', $this->_goods), implode(',', $this->_tickets));
23         }
24     }
25          
26     //假如我们要还原购物车的东西,比如用户关闭浏览器后再打开时会根据cookie还原
27     $data = array(
28            'goods' => array('衣服', '鞋子'),
29            'tickets' => array('减10'),
30     );
31          
32     //如果不使用创建者模式,则需要业务类里一步步还原购物车
33     // $cart = new ShoppingCart();
34     // foreach ($data['goods'] as $goods) {
35     //   $cart->addGoods($goods);
36     // }
37     // foreach ($data['tickets'] as $ticket) {
38     //   $cart->addTicket($ticket);
39     // }
40     // $cart->printInfo();
41     // exit;
42          
43          
44     //我们提供创建者类来封装购物车的数据组装
45     class CardBuilder {
46            private $_card;
47            function __construct($card) {
48                   $this->_card = $card;
49            }
50            function build($data) {
51                   foreach ($data['goods'] as $goods) {
52                          $this->_card->addGoods($goods);
53                   }
54                   foreach ($data['tickets'] as $ticket) {
55                          $this->_card->addTicket($ticket);
56                   }
57            }
58            function getCrad() {
59                   return $this->_card;
60            }
61     }
62          
63     $cart = new ShoppingCart();
64     $builder = new CardBuilder($cart);
65     $builder->build($data);
66     echo "after builder:n";
67     $cart->printInfo();
68          
69     ?>

可以看出,使用创建者模式对内部数据复杂的对象封装数据组装过程后,对外接口就会非常简单和规范,增加修改新数据项也不会对外部造成任何影响。

3、 原型模式

用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。

应用场景: 类的资源非常多、性能和安全要求,一般和工厂方法结合使用。

 1     <?php
 2     /**
 3      * 原型模式
 4      */
 5          
 6     //声明一个克隆自身的接口
 7     interface Prototype {
 8         function copy(); 
 9     }   
10          
11     //产品要实现克隆自身的操作
12     class Student implements Prototype {
13            //简单起见,这里没有使用get set
14         public $school;
15         public $major;
16            public $name;
17          
18            public function __construct($school, $major, $name) {
19                   $this->school = $school;
20                   $this->major = $major;
21                   $this->name = $name;
22            }
23          
24            public function printInfo() {
25                   printf("%s,%s,%sn", $this->school, $this->major, $this->name);
26            }
27            
28         public function copy() {
29                return clone $this;
30         }
31     }
32          
33     $stu1 = new Student('清华大学', '计算机', '张三');
34     $stu1->printInfo();
35          
36     $stu2 = $stu1->copy();
37     $stu2->name = '李四';
38     $stu2->printInfo();
39          
40     ?>

这里可以看到,如果类的成员变量非常多,如果由外部创建多个新对象再一个个赋值,则效率不高代码冗余也容易出错,通过原型拷贝复制自身再进行微小修改就是另一个新对象了。

创建型模式就总结完了。下面还有两部分结构型设计模式和行为型设计模式稍后继续。