创建性模式
单例模式(Singleton)
- 要求:只有一个实例,自行创建实例并对外提供这个实例
- 实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23class Mysql {
private static $conn;
private function __construct() {
self::$conn = new PDO("mysql:dbname=testdb;host=127.0.0.1", "root", "root");
}
public static function getInstance() {
if(!(self::$conn instanceof self)){
self::$conn = self;
}
return self::$conn;
}
//防止对象被复制
public function __clone(){
trigger_error('Clone is not allowed !');
}
//防止反序列化后创建对象
public function __wakeup() {
trigger_error('Unserialized is not allowed !');
}
}
$mysql = Mysql::getInstance();
简单工厂模式
- 要求:获取一个实例不通过new关键字获取,都是通过一个工厂类的工厂方法得到这个类的实例
- 规则:
- 工厂类必须有一个工厂方法
- 工厂类必须能够返回一个业务类实例
- 一次只能创建和返回一个实例
- 实现:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class SimpleFactory {
public function create($name) {
return new $name();
}
}
abstract class Vehicle {
abstract public function driveTo($dest);
}
class Bicycle extends Vehicle {
public function driveTo($dest) {
return '骑自行车去:'.$dest;
}
}
class Car extends Vehicle {
public function driveTo($dest) {
return '开汽车去:'.$dest;
}
}
//应用
$factory = new SimpleFactory();
$vehicle = $factory -> create('Bicycle');
print($vehicle -> driveTo('北京'));
结构性模式
适配器模式
接口的应用1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34interface Database {
public function connect();
public function query();
public function close();
}
class DBMysql implements Database {
public function connect() {
var_dump(__METHOD__);
}
public function query() {
var_dump(__METHOD__);
}
public function close() {
var_dump(__METHOD__);
}
}
class DBPdo implements Database {
public function connect() {
var_dump(__METHOD__);
}
public function query() {
var_dump(__METHOD__);
}
public function close() {
var_dump(__METHOD__);
}
}
$database = new DBMysql();//切换数据库只要改这一行就行了,因为后面的都是标准接口方法,不管哪个数据库都一样
$database->connect();
$database->query();
$database->close();
问题
有的三方接口并没有按照标准接口实现,如下:1
2
3
4
5
6
7
8
9
10
11
12//第三方数据库类
class Oracle {
public function oracleConnect(){
//Oracle 的逻辑
};
public function oracleQuery(){
//Oracle 的逻辑
};
public function oracleClose(){
//Oracle 的逻辑
};
}
这时候适配器就派上用场了,用这个适配器将异类的方法转换为接口标准方法
适配器模式1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16class Adapter implements Database {
private $adaptee;
function __construct($adaptee) {
$this->adaptee = $adaptee;
}
//这里把异类的方法转换成了 接口标准方法,下同
public function connect(){
$this->adaptee->oracleConnect();
};
public function query(){
$this->adaptee->oracleQuery();
};
public function close(){
$this->adaptee->oracleClose();
};
}
应用:1
2
3
4
5
6$adaptee = new Oracle();
$adapter = new Adapter($adaptee);//只要改这个类就行了,后面的都可以不用改;
$database = $adapter;
$database->connect();
$database->query();
$database->close();
装饰器模式
解决问题:为某个方法装饰可以自由删除或增加的逻辑(如果遇到)
场景:比如有一个煮咖啡的程序,默认是纯咖啡,如何方便的选择是否加糖、加奶、加巧克力呢?
1 | //主咖啡程序 |
一般逻辑:加糖
1 | class sweetCoffee extends plainCoffee { |
弊端:如果这时候加奶、加巧克力就需要继续继承重写makeCoffee
装饰器方式:在addCoffee前后增加逻辑处理
1 | //调整煮咖啡程序 |
编写装饰器:
1 | class sweetCoffeeDecorator{ |
应用:加糖加奶只需要增加装饰器即可,不需要只需注释掉。
1 | $coffee = new plainCoffee(); |
总结:当你extends用过后又遇到需要再次extends的情况时,不妨考虑一下装饰器模式
依赖注入
原理:把一个类不可能更换的部分 和 可更换的部分分离开来,通过注入的方式来使用,从而达到解耦的目的。
一个数据库连接类1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22//数据库连接
class Mysql{
private $host;
private $port;
private $username;
private $password;
private $db_name;
public function __construct(){
$this->host = '127.0.0.1';
$this->port = 22;
$this->username = 'root';
$this->password = '';
$this->db_name = 'my_db';
}
public function connect(){
return mysqli_connect($this->host,$this->username ,$this->password,$this->db_name,$this->port);
}
}
//使用:
$db = new Mysql();
$con = $db->connect();
依赖注入:将可变部分(配置)提取出来抽象为一个类,然后在注入到原类中,这样就完成了配置文件和连接逻辑的分离。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42//配置类
class MysqlConfiguration {
private $host;
private $port;
private $username;
private $password;
private $db_name;
public function __construct($host, $port, $username, $password, $db_name) {
$this->host = $host;
$this->port = $port;
$this->username = $username;
$this->password = $password;
$this->db_name = $db_name;
}
public function getHost() {
return $this->host;
}
public function getPort() {
return $this->port;
}
public function getUsername(){
return $this->username;
}
public function getPassword() {
return $this->password;
}
public function getDbName() {
return $this->db_name;
}
}
//Mysql变更为:
class Mysql {
private $configuration;
public function __construct(MysqlConfiguration $config) {
$this->configuration = $config;
}
public function connect() {
return mysqli_connect($this->configuration->getHost(), $this->configuration->getUsername(), $this->configuration->getPassword, $this->configuration->getDbName(), $this->configuration->getPort());
}
}
使用:$config是注入Mysql的,这就是所谓的依赖注入。1
2
3$config = new MysqlConfiguration('127.0.0.1','root','','my_db',22);
$db = new Mysql($config);
$con = $db->connect();
链式操作
1 |
|
行为性模式
观察者模式
需求:有一个listener和handler,当listener监听到一个事件发生,多个handler自动处理对应的逻辑
原理:
- 声明抽象事件发生者类(EventGenerator),负责添加观察者和逐一调用观察者的update方法进行通知。
- 声明观察者接口类(Observer),提供抽象方法update,所有的观察者都实现这个接口
- 具体的事件继承EventGenerator类,然后调用addObserver添加观察者,调用trigger进行触发。
实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48//声明一个抽象的事件发生者基类
abstract class EventGenerator{
private $observers = array();
//添加观察者方法
function addobserver(Observer $observer)
{
$this->observers[] = $observer;
}
//对每个添加的观察者进行事件通知
function notify()
{
//对每个观察者逐个去更新
foreach($this->observers as $observer)
{
$observer->update();
}
}
}
//声明一个观察者接口
interface observer {
function update($event_info = null);
}
//声明具体事件类,继承了主事件
class Event extends EventGenerator
{
function trigger()
{
echo "Event<br/>";
$this->notify();
}
}
//声明多个观察者
class Observer1 implements observer
{
function update($event_info = null)
{
echo "逻辑1<br/>";
}
}
class Observer2 implements observer
{
function update($event_info = null)
{
echo "逻辑2<br/>";
}
}
应用:1
2
3
4
5//Event基类里的foreach,可以实现一个事件对应多个观察者
$event = new Event;
$event->addObserver(new Observer1);
$event->addObserver(new Observer2);
$event->trigger();
策略模式
需求 :针对大量ifelse判断上下文的环境所作出的策略。
原理:提供一个策略接口(Strategy),其中定义待实现方法,其他策略实现这个接口,业务逻辑中只调用接口中定义的方法。
举例:商城的首页,男的进来看男性商品,女的进来看女性商品,不男不女…以此类推,各种条件下用不同策略展示不同商品
实现:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27//showStrategy.php 展示策略接口
interface showStrategy{
public function showCategory();
}
//maleShowStrategy.php 男性用户展示策略
class maleShowStrategy implements showStrategy { // 具体策略A
public function showCategory(){
echo '展示男性商品目录';
}
}
//femaleShowStrategy.php 女性用户展示策略
class femaleShowStrategy implements showStrategy { // 具体策略B
public function showCategory(){
echo '展示女性商品目录';
}
}
//page.php 展示页面
class Page{
private $_strategy;
public function __construct(Strategy $strategy) {
$this->_strategy = $strategy;
}
public function showPage() {
$this->_strategy->showCategory();
}
}
使用1
2
3
4
5
6
7
8if(isset($_GET['male'])){
$strategy = new maleShowStrategy();
}elseif(isset($_GET['female'])){
$strategy = new femaleShowStrategy();
}
//注意看这里上下,Page类不再依赖一种具体的策略,而是只需要绑定一个抽象的接口,这就是传说中的控制反转(IOC)。
$question = new Page($strategy);
$question->showPage();
总结
仔细看上面的例子,不复杂,我们发现有2个好处:
它把if else 抽离出来了,不需要在每个类里都写if else;
它成功的实现了控制反转,Page类里没有具体的依赖策略,这样我们就可以随时添加和删除 不同的策略。
模板方法
这是最常见的设计模式之一,其实质就是父类提供一系列模板方法,有的实现了逻辑,有的只是一个接口。而子类继承大部分共有方法,同时对接口方法进行不同的实现,从而完成对父类模板的个性化改造,起到一对多的解耦目的。
可以说PHP的抽象类就是为了实现这个设计模式而推出的功能,在PHP中,抽象类本身就是模板方法模式。
应用:
1 | //Journey.php 模板类 |
1 | //BeachJourney.php子类一 |
参考: