设计模式(一)
我们说在编程中要有面向对象的思维。而如果没有学过设计模式,那么对于面向对象的理解多半都是肤浅和片面的。下面我们通过对设计模式的学习,来加深对面向对象的认识。
1、简单工厂模式(静态工厂模式)
属于创建型模式,专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。
举个栗子,要实现计算器的功能
参考代码如下:
1 <?php 2 3 // 运算类-计算器 4 class Operation 5 { 6 private $_numberA = 0; 7 private $_numberB = 0; 8 9 public function numberA($value = ‘‘) 10 { 11 if (isset($value) && !empty($value)) { 12 $this->_numberA = $value; 13 return $this; 14 } 15 16 return $this->_numberA; 17 } 18 19 public function numberB($value = ‘‘) 20 { 21 if (isset($value) && !empty($value)) { 22 $this->_numberB = $value; 23 return $this; 24 } 25 return $this->_numberB; 26 } 27 28 public function getResult() 29 { 30 $result = 0; 31 return $result; 32 } 33 34 } 35 36 // 加减乘除类 37 // 加法类,继承运算类 38 class OperationAdd extends Operation 39 { 40 public function getResult() 41 { 42 $result = $this->numberA() + $this->numberB(); 43 return $result; 44 } 45 } 46 47 // 减法类,继承运算类 48 class OperationSub extends Operation 49 { 50 public function getResult() 51 { 52 $result = $this->numberA() - $this->numberB(); 53 return $result; 54 } 55 } 56 57 // 乘法类,继承运算类 58 class OperationMul extends Operation 59 { 60 public function getResult() 61 { 62 $result = $this->numberA() * $this->numberB(); 63 return $result; 64 } 65 } 66 67 // 除法类,继承运算类 68 class OperationDiv extends Operation 69 { 70 public function getResult() 71 { 72 if ($this->numberB() == 0) { 73 throw new Exception("除数不能为0。"); 74 } 75 $result = $this->numberA() / $this->numberB(); 76 return $result; 77 } 78 } 79 80 // 简单工厂模式 81 class OperationFactory 82 { 83 public static function createOperate(string $operate) 84 { 85 switch ($operate) { 86 case "+": 87 $oper = new OperationAdd(); 88 break; 89 case "-": 90 $oper = new OperationSub(); 91 break; 92 case "*": 93 $oper = new OperationMul(); 94 break; 95 case "/": 96 $oper = new OperationDiv(); 97 break; 98 default: 99 throw new Exception("运算符号必须是+-*/中的其中一个"); 100 } 101 return $oper; 102 } 103 } 104 105 $oper = OperationFactory::createOperate("*"); 106 $oper->numberA(2); 107 $oper->numberB(5); 108 echo $oper->getResult();
接下来,只需要输入运算符号,工厂就实例化出合适的对象,通过多态,返回父类的方式实现了计算器的结果。
如果之后,需要增加各种复杂的运算,比如平发根,立方根等,只要增加相应的运算子类,并且还要去修改运算类工厂,在switch中增加分支。
说明:对于调用工厂的客户端来说实现了开闭原则而工厂本身没有实现开闭原则。后面可以通过反射的机制来解决switch分支的问题。
2、工厂方法模式
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法使一个类的实例化延迟到子类。
还是拿上面实现计算器的功能来举例
参考代码如下:
1 <?php 2 3 // 运算类-计算器 4 class Operation 5 { 6 private $_numberA = 0; 7 private $_numberB = 0; 8 9 public function numberA($value = ‘‘) 10 { 11 if (isset($value) && !empty($value)) { 12 $this->_numberA = $value; 13 return $this; 14 } 15 16 return $this->_numberA; 17 } 18 19 public function numberB($value = ‘‘) 20 { 21 if (isset($value) && !empty($value)) { 22 $this->_numberB = $value; 23 return $this; 24 } 25 return $this->_numberB; 26 } 27 28 public function getResult() 29 { 30 $result = 0; 31 return $result; 32 } 33 34 } 35 36 37 // 加减乘除类 38 39 class OperationAdd extends Operation 40 { 41 public function getResult() 42 { 43 $result = $this->numberA() + $this->numberB(); 44 return $result; 45 } 46 } 47 48 class OperationSub extends Operation 49 { 50 public function getResult() 51 { 52 $result = $this->numberA() - $this->numberB(); 53 return $result; 54 } 55 } 56 57 class OperationMul extends Operation 58 { 59 public function getResult() 60 { 61 $result = $this->numberA() * $this->numberB(); 62 return $result; 63 } 64 } 65 66 class OperationDiv extends Operation 67 { 68 public function getResult() 69 { 70 if ($this->numberB() == 0) { 71 throw new Exception("除数不能为0。"); 72 } 73 $result = $this->numberA() / $this->numberB(); 74 return $result; 75 } 76 } 77 78 79 interface iFactory 80 { 81 public function createOperation(); 82 } 83 84 85 // 加法类工厂 86 class AddFactory implements iFactory 87 { 88 public function createOperation() 89 { 90 return new OperationAdd(); 91 } 92 } 93 94 // 减法类工厂 95 class SubFactory implements iFactory 96 { 97 public function createOperation() 98 { 99 return new OperationSub(); 100 } 101 } 102 103 // 乘法类工厂 104 class MulFactory implements iFactory 105 { 106 public function createOperation() 107 { 108 return new OperationMul(); 109 } 110 } 111 112 // 除法类工厂 113 class DivFactory implements iFactory 114 { 115 public function createOperation() 116 { 117 return new OperationDiv(); 118 } 119 } 120 121 // 客户端的实现 122 $operFactory=new AddFactory(); 123 $oper=$operFactory->createOperation(); 124 $oper->numberA(2); 125 $oper->numberB(8); 126 echo $oper->getResult();
说明:工厂方法本身是符合开闭原则的,但要说到客户端,客户总得自己决定创建什么样的对象吧,这个在服务端没法帮客户确定,所以客户端代码需要改是不可避免的。
3、抽象工厂模式 (Abstract Factory)
提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
1 <?php 2 3 class User 4 { 5 private $_id; 6 private $_name; 7 8 public function id($value = ‘‘) 9 { 10 if (isset($value) && !empty($value)) { 11 $this->_id = $value; 12 return $this; 13 } 14 return $this->_id; 15 } 16 17 public function name() 18 { 19 if (isset($value) && !empty($value)) { 20 $this->_name = $value; 21 return $this; 22 } 23 return $this->_name; 24 } 25 } 26 27 class Department 28 { 29 private $_id; 30 31 public function id($value = ‘‘) 32 { 33 if (isset($value) && !empty($value)) { 34 $this->_id = $value; 35 return $this; 36 } 37 return $this->_id; 38 } 39 } 40 41 interface iUser 42 { 43 public function insert(User $user); 44 45 public function getUser(int $id); 46 } 47 48 interface iDepartment 49 { 50 public function insert(Department $department); 51 52 public function getDepartment(int $id); 53 } 54 55 class SqlserverUser implements iUser 56 { 57 public function insert(User $user) 58 { 59 echo "在SQL Server中给User表增加一条记录<br/>"; 60 } 61 62 public function getUser(int $id) 63 { 64 echo "在SQL Server中根据ID=" . $id . "得到User表一条记录<br/>"; 65 } 66 67 } 68 69 class AccessUser implements iUser 70 { 71 public function insert(User $user) 72 { 73 echo "在Access中给User表增加一条记录<br/>"; 74 } 75 76 public function getUser(int $id) 77 { 78 echo "在Access中根据ID=" . $id . "得到User表一条记录<br/>"; 79 } 80 } 81 82 class SqlserverDepartment implements iDepartment 83 { 84 public function insert(Department $department) 85 { 86 echo "在SQL Server中给Department表增加一条记录<br/>"; 87 } 88 89 public function getDepartment(int $id) 90 { 91 echo "在SQL Server中根据ID=" . $id . "得到Department表一条记录<br/>"; 92 } 93 94 } 95 96 class AccessDepartment implements iDepartment 97 { 98 public function insert(Department $department) 99 { 100 echo "在Access中给Department表增加一条记录<br/>"; 101 } 102 103 public function getDepartment(int $id) 104 { 105 echo "在Access中根据ID=" . $id . "得到Department表一条记录<br/>"; 106 } 107 } 108 109 // 抽象工厂模式 110 interface iFactory 111 { 112 public function createUser(); 113 114 public function createDepartment(); 115 } 116 117 class SqlserverFactory implements iFactory 118 { 119 public function createUser() 120 { 121 return new SqlserverUser(); 122 } 123 124 public function createDepartment() 125 { 126 return new SqlserverDepartment(); 127 } 128 } 129 130 class AccessFactory implements iFactory 131 { 132 public function createUser() 133 { 134 return new AccessUser(); 135 } 136 137 public function createDepartment() 138 { 139 return new AccessDepartment(); 140 } 141 } 142 143 144 // 客户端代码如下: 145 $user = new User(); 146 $dep = new Department(); 147 $factory = new AccessFactory(); 148 // 生成User类的实例 149 $su = $factory->createUser(); 150 $su->insert($user); 151 $su->getUser(1); 152 // 生成Department类的实例 153 $depFac = $factory->createDepartment(); 154 $depFac->insert($dep); 155 $depFac->getDepartment(1);
4、策略模式(strategy)
定义了算法家族,分别封装起来,让它们之间可以互相替换,此模式让算法的变化,不会影响到使用算法的客户。
1 <?php 2 3 // 现金收费抽象类 4 abstract class CashSuper 5 { 6 public abstract function acceptCash($money); 7 } 8 9 // 正常收费子类 10 class CashNormal extends CashSuper 11 { 12 public function acceptCash($money) 13 { 14 return $money; 15 } 16 } 17 18 // 打折收费子类 19 class CashRebate extends CashSuper 20 { 21 private $moneyRebate = 1; 22 23 public function __construct($moneyRebate) 24 { 25 $this->moneyRebate = $moneyRebate; 26 } 27 28 public function acceptCash($money) 29 { 30 return $money * $this->moneyRebate; 31 } 32 } 33 34 // 返利收费子类 35 class CashReturn extends CashSuper 36 { 37 38 private $moneyCondition = 0; 39 private $moneyReturn = 0; 40 41 public function __construct($moneyCondition, $moneyReturn) 42 { 43 $this->moneyCondition = $moneyCondition; 44 $this->moneyReturn = $moneyReturn; 45 } 46 47 48 public function acceptCash($money) 49 { 50 $result = $money; 51 if ($money >= $this->moneyCondition) { 52 $result = $money - floor($money / $this->moneyCondition) * $this->moneyReturn; 53 } 54 return $result; 55 } 56 } 57 58 class CashContext 59 { 60 private $cs; 61 62 // 策略模式与简单工厂结合 63 public function __construct($type) 64 { 65 switch ($type) { 66 case "1": 67 // 正常收费 68 $this->cs = new CashNormal(); 69 break; 70 case "2": 71 // 满300返100 72 $this->cs = new CashReturn(300, 100); 73 break; 74 case "3": 75 //打8折 76 $this->cs = new CashRebate(0.8); 77 break; 78 default: 79 throw new Exception("该收费类型不存在。"); 80 } 81 } 82 83 // 获得计算费用的结果 84 public function getResult($money) 85 { 86 return $this->cs->acceptCash($money); 87 } 88 } 89 90 // 客户端主要代码 91 92 class Client 93 { 94 public function btnClick($type, $money) 95 { 96 // 选择所用具体实现的职责由客户端对象承担,并转给策略模式的context对象 97 $cashContext = new CashContext($type); 98 return $cashContext->getResult($money); 99 } 100 } 101 102 $client = new Client; 103 echo $client->btnClick(3, 1000);
5、装饰模式(decorator)
动态地给一个对象添加一些额外的职责,就增加功能来说,装饰模式比生成子类更为灵活。
1 <?php 2 3 4 class Person 5 { 6 private $name; 7 8 public function __construct($name) 9 { 10 $this->name = $name; 11 } 12 13 public function show() 14 { 15 echo "装扮的" . $this->name; 16 } 17 18 } 19 20 // 服饰类(Decorator) 21 class Finery extends Person 22 { 23 protected $component; 24 25 public function __construct() 26 { 27 } 28 29 //打扮 30 public function decorate(Person $component) 31 { 32 $this->component = $component; 33 } 34 35 public function show() 36 { 37 if ($this->component != null) { 38 $this->component->show(); 39 } 40 } 41 } 42 43 // 具体服饰类(ConcreteDecorator) 44 class TShirts extends Finery 45 { 46 public function show() 47 { 48 echo "大T恤 "; 49 parent::show(); 50 } 51 } 52 53 class BigTrouser extends Finery 54 { 55 public function show() 56 { 57 echo "垮裤 "; 58 parent::show(); 59 } 60 } 61 62 class Sneakers extends Finery 63 { 64 public function show() 65 { 66 echo "破球鞋 "; 67 parent::show(); 68 } 69 } 70 71 class Suit extends Finery 72 { 73 public function show() 74 { 75 echo "西装 "; 76 parent::show(); 77 } 78 } 79 80 class Tie extends Finery 81 { 82 public function show() 83 { 84 echo "领带 "; 85 parent::show(); 86 } 87 } 88 89 class LeatherShoes extends Finery 90 { 91 public function show() 92 { 93 echo "皮鞋 "; 94 parent::show(); 95 } 96 } 97 98 # 客户端代码 99 $xc = new Person("小菜"); 100 echo "第一种装扮:<br>"; 101 102 $pqx = new Sneakers(); 103 $kk = new BigTrouser(); 104 $dtx = new TShirts(); 105 $pqx->decorate($xc); 106 $kk->decorate($pqx); 107 $dtx->decorate($kk); 108 $dtx->show(); 109 110 echo "<hr>"; 111 112 echo "第二种装扮:<br>"; 113 $px = new LeatherShoes(); 114 $ld = new Tie(); 115 $xz = new Suit(); 116 $px->decorate($xc); 117 $ld->decorate($px); 118 $xz->decorate($ld); 119 $xz->show();
6、建造者模式
将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。
1 <?php 2 3 // 产品类,由多个部件组成 4 class Product 5 { 6 public $arr = []; 7 8 public function add(string $part) 9 { 10 // 添加产品部件 11 array_push($this->arr, $part); 12 } 13 14 public function show() 15 { 16 echo "\n产品 创建----"; 17 // 列举所有的产品部件 18 foreach ($this->arr as $part) { 19 echo $part; 20 } 21 } 22 } 23 24 // 抽象建造者类:确定产品由两个部件PartA 和 PartB组成,并声明一个得到产品建造后结果的方法getResult 25 abstract class Builder 26 { 27 public abstract function buildPartA(); 28 29 public abstract function buildPartB(); 30 31 public abstract function getResult(); 32 } 33 34 // 具体建造者类-建造具体的两个部件是部件A和部件B 35 class ConcreteBuilder1 extends Builder 36 { 37 private $product; 38 39 public function __construct(Product $product) 40 { 41 $this->product = $product; 42 } 43 44 public function buildPartA() 45 { 46 $this->product->add("部件A"); 47 } 48 49 public function buildPartB() 50 { 51 $this->product->add("部件B"); 52 } 53 54 55 public function getResult() 56 { 57 return $this->product; 58 } 59 } 60 61 62 class ConcreteBuilder2 extends Builder 63 { 64 private $product; 65 66 public function __construct(Product $product) 67 { 68 $this->product = $product; 69 } 70 71 public function buildPartA() 72 { 73 $this->product->add("部件X"); 74 } 75 76 public function buildPartB() 77 { 78 $this->product->add("部件Y"); 79 } 80 81 82 public function getResult() 83 { 84 return $this->product; 85 } 86 } 87 88 // 指挥者类-用来指挥建造过程 89 class Director 90 { 91 public function construct(Builder $builder) 92 { 93 $builder->buildPartA(); 94 $builder->buildPartB(); 95 } 96 } 97 98 // 客户端代码(客户端不知道具体的建造过程) 99 $product = new Product(); 100 $director = new Director(); 101 $b1 = new ConcreteBuilder1($product); 102 $b2 = new ConcreteBuilder2($product); 103 104 // 指挥者用ConcreteBuilder1的方法来建造产品 105 $director->construct($b1); 106 $p1 = $b1->getResult(); 107 $p1->show(); 108 109 // 指挥者用ConcreteBuilder2的方法来建造产品 110 $director->construct($b2); 111 $p2 = $b2->getResult(); 112 $p2->show();
说明:如果我们用了建造者模式,那么用户就只需指定需要建造的类型就可以得到它们,而具体建造的过程和细节就不需知道了。
7、代理模式(proxy)
为其他对象提供一种代理以控制对这个对象的访问。
1 <?php 2 3 interface iGiveGift 4 { 5 public function giveDolls(); 6 7 public function giveFlowers(); 8 9 public function giveChocolate(); 10 } 11 12 // 追求者类 13 class Pursuit implements iGiveGift 14 { 15 public $mm; 16 17 public function __construct(SchoolGirl $mm) 18 { 19 $this->mm = $mm; 20 } 21 22 public function giveDolls() 23 { 24 echo $this->mm->name() . " 送你洋娃娃<br/>"; 25 } 26 27 public function giveFlowers() 28 { 29 echo $this->mm->name() . " 送你鲜花<br/>"; 30 } 31 32 public function giveChocolate() 33 { 34 echo $this->mm->name() . " 送你巧克力<br/>"; 35 } 36 } 37 38 class Proxy implements iGiveGift 39 { 40 public $gg; 41 42 public function __construct(SchoolGirl $mm) 43 { 44 $this->gg = new Pursuit($mm); 45 } 46 47 public function giveDolls() 48 { 49 $this->gg->giveDolls(); 50 } 51 52 public function giveFlowers() 53 { 54 $this->gg->giveFlowers(); 55 } 56 57 public function giveChocolate() 58 { 59 $this->gg->giveChocolate(); 60 } 61 } 62 63 // 被追求者类 64 class SchoolGirl 65 { 66 private $name; 67 68 public function name($value = ‘‘) 69 { 70 if (isset($value) && !empty($value)) { 71 $this->name = $value; 72 return $this; 73 } 74 return $this->name; 75 } 76 } 77 78 // 客户端调用代码 79 $sc=new SchoolGirl(); 80 $sc->name("李娇娇"); 81 $proxy=new Proxy($sc); 82 $proxy->giveDolls(); 83 $proxy->giveFlowers(); 84 $proxy->giveChocolate();
8、单例模式(singleton)
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
1 <?php 2 3 class Single 4 { 5 private static $_instance = null; 6 7 // 私有化构造方法 8 private function __construct() 9 { 10 } 11 12 // 私有化克隆方法 13 private function __clone() 14 { 15 } 16 17 // 提供一个访问它的全局访问点 18 public static function getInstance() 19 { 20 if (!(self::$_instance instanceof self)) { 21 self::$_instance = new self(); 22 } 23 return self::$_instance; 24 } 25 26 public function test() 27 { 28 echo "调试方法成功"; 29 } 30 } 31 32 $single = Single::getInstance(); 33 $single->test(); 34 $single2 = Single::getInstance();
9、观察者模式(observer)
定义了一种一对多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
1 <?php 2 3 // 当用户注册成功时,邮件观察者或短信观察者则收到相应的通知,发送邮件和短信给用户 4 5 // 通知者-用户类 6 class User implements SplSubject 7 { 8 public $name; 9 public $email; 10 public $mobile; 11 // 当前观察者集合 12 private $observers = []; 13 14 public function __construct() 15 { 16 $this->observers = new SplObjectStorage(); 17 } 18 19 // 模拟注册 20 public function register($name, $email, $mobile) 21 { 22 $this->name = $name; 23 $this->email = $email; 24 $this->mobile = $mobile; 25 26 // business handle and register success 27 $reg_result = true; 28 if ($reg_result) { 29 $this->notify(); // 注册成功 所有的观察者将会收到此主题的通知 30 return true; 31 } 32 33 return false; 34 } 35 36 public function attach(SplObserver $observer) 37 { 38 $this->observers->attach($observer); 39 } 40 41 public function detach(SplObserver $observer) 42 { 43 $this->observers->detach($observer); 44 } 45 46 public function notify() 47 { 48 if ($this->observers) { 49 foreach ($this->observers as $observer) { 50 $observer->update($this); 51 } 52 } 53 } 54 } 55 56 class EmailObserver implements SplObserver 57 { 58 public function update(SplSubject $user) 59 { 60 echo "send email to " . $user->email . PHP_EOL; 61 } 62 } 63 64 class SmsObserver implements SplObserver 65 { 66 public function update(SplSubject $user) 67 { 68 echo "send sms to " . $user->mobile . PHP_EOL; 69 } 70 } 71 72 // 业务 73 $user = new User(); 74 $emailObserver = new EmailObserver(); 75 $smsObserver = new SmsObserver(); 76 77 // 添加观察者 78 $user->attach($emailObserver); 79 $user->attach($smsObserver); 80 81 // 删除Sms 观察者 82 $user->detach($smsObserver); 83 84 // 根据注册结果通知观察者,观察者做相应的处理 85 $user->register("小明", "123@qq.com", "18502365895");
参考资料:
程杰《大话设计模式》