php 十一种设计模式
第一种:工厂模式
工厂类:Factory.class.php
<?php
require_once 'Test.class.php';
class Factory{
static function createTest(){
$test = new Test();
return $test;
}
}
?>
需要实例化的类:Test.class.php
<?php
class Test{
public function index(){
return "index";
}
}
?>
执行类:index.php
<?php
require_once 'Factory.class.php';
$test = Factory::createTest();
echo $test->index(); //index
?>
PS:工厂模式的好处就是:当要实例化的类名改变时,只需要改变工厂类里的需要实例化的类的名字
第二种:单例模式
单例模式实现数据库的连接以及增删改查
DB.class.php:
<?php
// 单例模式
class DB{
private static $_instance; //存放实例对象
private $_pdo = null; //存放pdo对象
const DB_HOST = 'localhost';
const DB_USER = 'root';
const DB_PW = '12345678';
const DB_NAME = 'test';
// 构造函数私有化 只能类内调用
private function __construct(){
// 连接数据库
try{
$this->_pdo = new PDO('mysql:host='.self::DB_HOST.';dbname='.self::DB_NAME,self::DB_USER,self::DB_PW);
// 设置字符集
$this->_pdo->query("SET NAMES UTF8");
}catch(PDOException $e){
exit('数据库连接失败'.$e->getMessage());
}
}
// 单例模式的实现
static function getInstance(){
if(is_null(self::$_instance)){
self::$_instance = new self();
}
return self::$_instance;
}
// 查看信息
public function getInfo($_sql){
$_result = $this->_pdo->query($_sql,PDO::FETCH_OBJ);
return $_result;
}
// 插入信息 // 修改信息 // 删除信息
public function cud($_sql){
$_stmt = $this->_pdo->prepare($_sql);
$_stmt->execute();
return $_stmt;
}
}
?>
调用 index.php:
<?php
require_once 'DB.class.php';
$db = DB::getInstance();
// 查询数据
$result = $db->getInfo("SELECT id FROM test WHERE username = '123'");
while (!!$rows = $result->fetch()) {
print_r($rows);
}
// 插入一条数据
$stmt = $db->cud("INSERT INTO test(username) VALUES('测试')");
if($stmt->rowCount()){
echo "插入成功";
}
?>
PS:如果再结合一下工厂模式:
Factory.class.php:
<?php
require_once 'DB.class.php';
class Factory{
static function createDB(){
$db = DB::getInstance();
return $db;
}
}
?>
index.php:
<?php
require_once 'Factory.class.php';
$db = Factory::createDB(); //使用工厂模式调用单例模式得到DB类
// 查询数据
$result = $db->getInfo("SELECT id FROM test WHERE username = '123'");
while (!!$rows = $result->fetch()) {
print_r($rows);
}
// 插入一条数据
$stmt = $db->cud("INSERT INTO test(username) VALUES('测试')");
if($stmt->rowCount()){
echo "插入成功";
}
?>
第三种:注册器模式
结合前面的单例模式和工厂模式 操作数据库
Register.class.php:
<?php
class Register{
static protected $objects;//全局树 array
//设置
static function set($alias,$object){
self::$objects[$alias] = $object;
}
//获得
static function get($alias){
return self::$objects[$alias];
}
//注销
static function _unset($alias){
unset(self::$objects[$alias]);
}
}
index.php:
<?php
require_once 'Factory.class.php';
require_once 'Register.class.php';
// $db = Factory::createDB(); //使用工厂模式调用单例模式得到DB类
if(Register::get('db')){
$db = Register::get('db');
}else{
Register::set('db',Factory::createDB());
$db = Register::get('db'); //使用注册器模式
}
// 查询数据
$result = $db->getInfo("SELECT id FROM test WHERE username = '123'");
while (!!$rows = $result->fetch()) {
print_r($rows);
}
// 插入一条数据
$stmt = $db->cud("INSERT INTO test(username) VALUES('测试')");
if($stmt->rowCount()){
echo "插入成功";
}
?>
PS:还可以再将Register类做成单例模式。
第四种:适配器模式
可以将截然不同的函数封装成同意的API,例如php的数据库操作,包括mysql,mysqli,PDO
还是结合一下前面的三种设计模式:
创建接口 database.class.php:
<?php
interface Database{
public function connect();
public function select($_sql);
public function close();
}
?>
创建3个文件实现这个接口分别包括使用mysql,mysqli,PDO三种方法:
这里的PDO连接还使用之前的DB类,如下修改:
<?php
require 'Database.class.php'; //引入接口
// 单例模式
class DB implements Database{ //实现接口
private static $_instance; //存放实例对象
private $_pdo = null; //存放pdo对象
const DB_HOST = 'localhost';
const DB_USER = 'root';
const DB_PW = '12345678';
const DB_NAME = 'test';
// 单例模式的实现
static function getInstance(){
if(is_null(self::$_instance)){
self::$_instance = new self();
}
return self::$_instance;
}
// 构造函数私有化 只能类内调用
private function __construct(){
// 连接数据库
$this->connect();
}
// 实现接口的connect方法
public function connect(){
try{
$this->_pdo = new PDO('mysql:host='.self::DB_HOST.';dbname='.self::DB_NAME,self::DB_USER,self::DB_PW);
// 设置字符集
$this->_pdo->query("SET NAMES UTF8");
}catch(PDOException $e){
exit('数据库连接失败'.$e->getMessage());
}
}
// 实现接口的query方法
// 查看信息
public function select($_sql){
$_result = $this->_pdo->query($_sql,PDO::FETCH_OBJ);
return $_result;
}
// 实现接口的close方法
public function close(){
unset($this->_pdo);
}
// 插入信息 // 修改信息 // 删除信息
public function cud($_sql){
$_stmt = $this->_pdo->prepare($_sql);
$_stmt->execute();
return $_stmt;
}
}
?>
剩下的两个文件原理一样,逐一实现即可。
index.php调用:
<?php
require_once 'Factory.class.php';
require_once 'Register.class.php';
// $db = Factory::createDB(); //使用工厂模式调用单例模式得到DB类
if(Register::get('db')){
$db = Register::get('db');
}else{
Register::set('db',Factory::createDB());
$db = Register::get('db'); //使用注册器模式
}
// 查询数据
$result = $db->select("SELECT id FROM test WHERE username = '123'");
while (!!$rows = $result->fetch()) {
print_r($rows);
}
// 插入一条数据
$stmt = $db->cud("INSERT INTO test(username) VALUES('测试')");
if($stmt->rowCount()){
echo "插入成功";
}
?>
第五种:策略模式
将一组特定的行为和算法封装成类,以适应某些特定的上下文环境,例如:根据性别展示商品
1 简单的策略模式
四个文件:Lesson.class.php,Math.class.php,English.class.php,index.php;
通过操作Lesson.class.php,调用English.class.php或者Math.class.php;
Lesson.class.php:
<?php
class Lesson{
public $_num;
public $_strategy; //策略因子 存放对象
public function __construct($_num,$_strategy){
$this->_num = $_num;
$this->_strategy = $_strategy;
}
public function cost(){
return $this->_strategy->cost($this); //传递自身
}
}
?>
Math.class.php:
<?php
class Math{
public function cost(Lesson $_lesson){
return 100*$_lesson->_num; //调用Lesson的属性或者方法
}
}
?>
English.class.php:
<?php
class English{
public function cost(Lesson $_lesson){
return 200*$_lesson->_num; //调用Lesson的属性或者方法
}
}
?>
index.php:
<?php
require 'Lesson.class.php';
require 'English.class.php';
require 'Math.class.php';
$math = new Lesson(5,new Math());
$english = new Lesson(5,new English());
echo $english->cost(); //1000
echo "<br>";
echo $math->cost(); //500
?>
通过传入的类改变实现的方法内容。
2 复杂的策略模式:
假设出现了文科和理科,而且两者下的数学,英语是不一样的,存在相应的折扣;
使用抽象类对学科进行规范:L.class.php
uml图:
此时无论添加一个学科还是一个类别都是非常简单并且不会影响到之前已经写好的类文件
Lesson.class.php:
<?php
class Lesson{
public $_num;
public $_discoust; //折扣
public $_strategy; //策略因子 存放对象
public function __construct($_num,$_strategy){
$this->_num = $_num;
$this->_strategy = $_strategy;
}
public function cost(){
return $this->_strategy->cost($this); //传递自身
}
}
?>
Arts.class.php:
<?php
require_once 'Lesson.class.php';
class Arts extends Lesson{
public $_discount = 0.8;
}
?>
Science.class.php:
<?php
require_once 'Lesson.class.php';
class Science extends Lesson{
public $_discount = 0.6;
}
?>
L.class.php:
<?php
abstract class L{
abstract function cost(Lesson $_sesson);
}
?>
English.class.php:
<?php
require_once 'L.class.php';
class English extends L{
public function cost(Lesson $_lesson){
return 200*$_lesson->_num;
}
}
?>
Math.class.php:
<?php
require_once 'L.class.php';
class Math extends L{
public function cost(Lesson $_lesson){
return 100*$_lesson->_num;
}
}
?>
PS:如果要更改或者添加某个类别(文科/理科)的一个属性只需要在对应的类文件修改或添加;
如果要更改某个科目的一个属性只需要更改对应的类文件修改或添加;
总结:继承要优于分支判断,因为继承降低了耦合提升了内聚,但在需求变化后或者功能不断增加,继承就会显得特别臃肿。那么组合的策略模式要优于继承,因为他灵活,将子类的实现算法高度内聚,降低了各个类的关联耦合,实现了高内聚低耦合的特点;
第六种:观察者模式:
事件产生基类 EventGenerator.class.php:
<?php
abstract class EventGenerator{
private $_observers = array();
public function addObserver(Observer $observer){
$this->_observers[] = $observer;
}
public function notify(){
foreach ($this->_observers as $observer) {
$observer->update();
}
}
}
?>
观察对象基类:Observer.class.php:
<?php
interface Observer{
public function update();
}
?>
观察对象子类实现:Observer1.class.php:
<?php
require_once 'Observer.class.php';
class Observer1 implements Observer{
public function update(){
echo '逻辑改变了';
}
}
?>
当逻辑改变时被观察者改变:
index.php:
<?php
require_once 'EventGenerator.class.php';
require_once 'Observer1.class.php';
class Event extends EventGenerator{
public function index(){
echo "index"; //发生变化
$this->notify(); //改变绑定的观察对象
}
}
$event = new Event();
$event->addObserver(new Observer1()); //绑定需要观察的类
$event->index(); //index 逻辑改变了
?>
第七种:原型模式
与工厂模式不同,原型模式是先创建好一个对象,然后通过关键字clone来克隆新的对象,避免了类创建时重复的初始化操作,通常用于大的类的创建,创建一个大的对象需要一个很大的开销,每次new 就会消耗很大,原型模式仅需要拷贝内存即可。
index.php:
<?php
class code{
function __construct(){
echo "code";
}
function index(){
echo "index";
}
function __clone(){
echo "clone";
}
// 其他操作。。。
}
$code = new code(); //code
$code2 = clone $code; //clone
$code2->index(); //index
?>
第八种:装饰者模式:
动态地添加类的功能,一个类提供了一项功能,如果要修改并添加额外的功能,传统的编程模式:需要写一个子类继承他并重新实现类的方法,使用装饰者仅需要在运行时添加一个装饰器对象即可,实现了最大的灵活性。
装饰器基类:Draw.class.php:
<?php
abstract class Draw{
abstract function before();
abstract function after();
}
?>
装饰器子类:改变颜色
ColorDraw.class.php:
<?php
require_once 'Draw.class.php';
class ColorDraw extends Draw{
protected $color;
public function __construct($_color){
$this->color = $_color;
}
public function before(){
echo "<div style='color:".$this->color."'>";
}
public function after(){
echo "</div>";
}
}
?>
装饰器子类:改变字体大小:
FontDraw.class.php:
<?php
require_once 'Draw.class.php';
class FontDraw extends Draw{
protected $font;
public function __construct($_font){
$this->font = $_font;
}
public function before(){
echo "<div style='font-size:".$this->font."'>";
}
public function after(){
echo "</div>";
}
}
?>
实现:index.php:
<?php
require_once 'ColorDraw.class.php';
require_once 'FontDraw.class.php';
class demo{
private $decorator = array(); //存放装饰器对象
public function write($_info){
$this->before(); //装饰器功能实现
echo $_info;
$this->after(); //装饰器功能实现
}
// 添加装饰者
public function addDecorator(Draw $draw){
$this->decorator[] = $draw;
}
// 实现装饰者的方法
public function before(){
foreach ($this->decorator as $decorator) {
$decorator->before();
}
}
public function after(){
$decorator = array_reverse($this->decorator);
foreach ($this->decorator as $decorator) {
$decorator->after();
}
}
}
$demo = new demo();
$demo->addDecorator(new ColorDraw('red'));
$demo->addDecorator(new FontDraw('22px'));
$demo->write('hello world');
?>
第九种:迭代器模式:
通过一个迭代器类对数据进行循环遍历
在适配器模式下继续修改:
迭代器类:AllUser.class.php:
<?php
require_once 'Factory.class.php';
class AllUser implements Iterator{
protected $ids; //存放所有id
protected $data = array();
protected $index; //索引
public function __construct(){
$db = Factory::createDB();
$result = $db->select("SELECT id FROM test",PDO::FETCH_OBJ);
$this->ids = $result->fetchAll();
}
public function current(){
$db = Factory::createDB();
$id = $this->ids[$this->index]->id;
$result = $db->select("SELECT * FROM test WHERE id =".$id."",PDO::FETCH_OBJ);
return $result->fetch(); //返回一个对象
}
public function next(){
$this->index++;
}
public function valid(){
return $this->index < count($this->ids);
}
public function rewind(){
$this->index = 0;
}
public function key(){
return $this->index;
}
}
?>
index.php:
<?php
require_once 'Factory.class.php';
require_once 'AllUser.class.php';
$db = Factory::createDB(); //使用工厂模式调用单例模式得到DB类
$user = new AllUser();
foreach ($user as $key => $value) {
echo ($value->username); //得到所有用户的用户名
echo ($value->id); //得到所有用户的id
}
?>
PS:迭代器类必须实现接口Iterator 并且实现5个方法current(),next(),valid(),rewind(),key()
第十种:代理模式:
在客户端与实体之间建立一个代理对象proxy,客户端对实体进行操作委托给代理对象隐藏具体的实现细节。
代理基类:Uproxy.class.php:
<?php
interface Uproxy{
function getData($id);
}
?>
代理子类:proxy.class.php:
<?php
require_once 'Uproxy.class.php';
require_once 'Factory.class.php';
class proxy implements Uproxy{
public function getData($id){
$db = Factory::createDB();
$result = $db->select("SELECT * FROM test WHERE id =".$id."",PDO::FETCH_OBJ);
return $result->fetch(); //查询这条记录
}
}
?>
index.php:
<?php
require_once 'Factory.class.php';
require_once 'proxy.class.php';
$db = Factory::createDB(); //使用工厂模式调用单例模式得到DB类
$proxy = new proxy();
var_dump($proxy->getData(1)); //
?>
第十一种:中介者模式