1. 程式人生 > >php 十一種設計模式

php 十一種設計模式

                                        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)); //

?>

第十一種:中介者模式