初探PHP设计模式
设计模式不是一套具体的语言框架,是行之有效的编码规范,是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。合理使用设计模式将有助于初学者更加深入地理解面向对象思维。
一、三大基本模式
1、工厂模式
工厂模式分为简单工厂模式,工厂模式,抽象工厂模式,今天主要讲简单工厂模式。通过定义好的工厂方法或者类生成对象,而不是在代码中直接new 一个类来生成对象。使用工厂模式,可以避免当改变某个类的名字或者方法之后,在调用这个类的所有的代码中都修改它的名字或者参数。
声明一个初始类
class DataBase {
public function __construct()
{
echo __CLASS__;
}
}
创建一个工厂类,写一个工厂方法
class Factory {
static function creatDataBase()
{
$db = new DataBase();
return $db;
}
}
再在需要调用初始类的php代码处使用工厂类
spl_autoload_register('autoload1');
$db = SPL\Factory::creatDataBase();
function autoload1($class)
{
$dir = __DIR__;
$requireFile = $dir . "\\" . $class . ".php";
require $requireFile;
}
之后如果要修改初始类的信息,如名称,就可以直接修改与之对应的工厂类中的代码。
结果如下:
2、单例模式
在应用程序执行的时候,只能获得一个对象实例。主要是为了防止对资源的浪费,就比如数据库类的连接方法,普通情况下,多个类使用数据库就需要创建多个数据库对象,而实际情况数据库的对象只需要创建一次就可以在不同类中使用。
单例模式的要求:
1,不允许从外部调用以防止创建多个对象,需要将构造函数和析构函数必须声明为私有。
2,防止实例被克隆,这会创建对象的副本,需要将clone方法声明为私有
3,只有getInstance()方法为公有的。
namespace SPL;
class Singleton {
private static $instance;
private static $num = 0; private function __construct()
{
echo "创建次数:", self::$num + 1;
} /*只有第一次调用时声明类获取实例*/
private function __destruct()
{
} private function __clone()
{
} public static function getInstance()
{
if (self::$instance == null) {
self::$instance = new self();
}
return self::$instance;
}
}
调用:
$db=SPL\Singleton::getInstance();
$db=SPL\Singleton::getInstance();
$db=SPL\Singleton::getInstance();
结果如下:
3、注册模式
之前已经创建好的对象,可以储存在到某个全局可以使用的对象树上,在需要使用的时候,直接从该对象树上获取即可,并且任何地方直接去访问。通过工厂模式和单例模式创建对象,而使用注册模式去更好的使用对象。注册模式常常和工厂模式一起使用。
namespace SPL;
class Register {
protected static $object;
public static function set($alias,$object){
self::$object[$alias]=$object;
} public static function get($alias)
{
return self::$object[$alias];
}
public static function _unset($alias)
{
unset(self::$object[$alias]);
}
}
在工厂模式初始化的时候,将对象注册进去
namespace SPL;
class Factory {
static function creatDataBase()
{
$db = new DataBase();
Register::set('FtyDb',$db);
}
}
使用工厂模式产生的对象时就可以直接调用注册模式了
SPL\Factory::creatDataBase();
$db=SPL\Register::get('FtyDb');
结果如下:
4,适配器模式
将截然不同的函数接口封装成统一的API。PHP中的数据库操作有MySQL,MySQLi,PDO三种,可以用适配器模式统一成一致,使不同的数据库操作,统一成一样的API。
namespace \SPL\DataBase;
//接口
interface IDataBase {
public function conn($host, $user, $pwd, $dbname);
public function query($query);
public function close();
} //mysql
class Mysql implements IDataBase {
protected $conn;
public function conn($host, $user, $pwd, $dbname)
{
$this->conn = mysql_connect($host, $user, $pwd);
mysql_select_db($dbname, $this->conn);
}
public function query($query)
{
return mysql_query($query);
}
public function close()
{
mysql_close($this->conn);
}
} //mysqli
namespace SPL\DataBase;
class Mysqli implements IDataBase {
protected $conn;
public function conn($host,$user,$pwd,$dbname)
{
$this->conn=mysqli_connect($host,$user,$pwd,$dbname);
}
public function query($query)
{
return mysqli_query($this->conn,$query);
}
public function close()
{
mysqli_close($this->conn);
}
} //PDO
namespace SPL\DataBase;
class PDO implements IDataBase {
protected $conn;
public function conn($host, $user, $pwd, $dbname)
{
$this->conn = new \PDO("mysql:host=$host;dbname=$dbname", $user, $pwd);
}
public function query($query)
{
$this->conn->query($query);
}
public function close()
{
unset($this->conn);
}
}
接口中的方法要和继承该接口的类的方法相同。然后在需要数据库操作的地方使用该接口
require('SPL\DataBase\IDataBase.php');
$db=new \SPL\DataBase\Mysqli();
$res=$db->conn('localhost','root','root','mrmf');
$result=$db->query('show columns from user');
$rows = $result->fetch_array();
print_r($rows);
$db->close();
结果如下:
5,策略模式
将一组特定的行为和算法封装成类,以适应某些特定的上下文环境。
声明策略的接口文件,定义接口的实现类
//接口
namespace SPL\Strategy;
interface UserStrategy {
function showAd();
function showCategory();
}
//女士用户
class FemaleUser implements UserStrategy {
public function showAd()
{
echo "2022长袖连衣裙<br>";
} public function showCategory()
{
echo "女装";
}
}
//男士用户
class MaleUser implements UserStrategy {
public function showAd()
{
echo "2019清商务休闲裤<br>";
} public function showCategory()
{
echo "男装";
}
}
class Page {
private $strategy; public function index()
{
echo "name:", $this->strategy->showAd();
echo "category:", $this->strategy->showCategory();
} public function setStrategy(SPL\Strategy\UserStrategy $strategy)
{
$this->strategy = $strategy;
}
}
调用
$page = new Page();
if (isset($_GET['female'])) {
$strategy = new SPL\Strategy\FemaleUser();
} else {
$strategy = new SPL\Strategy\MaleUser();
}
$page->setStrategy($strategy);
$page->index();
结果如下:
6,数据对象映射模式
将对象和数据储存映射起来,对一个对象的操作会映射为对数据存储的操作,例如tp3的M()方法。
//创建一个验证码表
CREATE TABLE `verify` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键',
`sort` smallint(6) NOT NULL COMMENT '验证码类型',
`phone` varchar(20) NOT NULL COMMENT '手机号',
`code` varchar(6) NOT NULL COMMENT '验证码',
`ctime` varchar(19) NOT NULL COMMENT '发送时间',
`num` smallint(6) NOT NULL DEFAULT '1' COMMENT '每日发送次数',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='验证码表'; DataObject.php :
//创建一个与验证码表对应的类DataObject,添加一些方法。
namespace SPL;
class DataObject {
//声明类属性和数据表中的字段映射
public $db;
public $id;
public $sort;
public $phone;
public $code;
public $ctime;
public $num;
//构造方法实现数据表的查询功能
public function __construct($id)
{
$this->db = new DataBase\Mysqli();
$this->db->conn('localhost', 'root', 'root', 'mrmf');
$res = $this->db->query("select * from verify where id='{$id}' limit 1");
$data = $res->fetch_assoc();
$this->id = $data['id'];
$this->sort = $data['sort'];
$this->phone = $data['phone'];
$this->code = $data['code'];
$this->ctime = $data['ctime'];
$this->num = $data['num'];
}
//单独修改sort为默认值
public function setDefSort($id,$value)
{
$this->db = Factory::createDataObject($id);
$this->sort =$value;
} public function setDefPhone($id,$value)
{
$this->db = Factory::createDataObject($id);
$this->sort = $value;
}
//析构方法为数据表的修改功能
public function __destruct()
{
$this->db->query("update verify set sort={$this->sort},phone={$this->phone},code={$this->code},ctime={$this->ctime},num={$this->num} where id={$this->id}");
}
}
Factory :
//添加关于DataObject类的工厂方法
static function createDataObject($id)
{
$key = 'Db' . $id;
$db = Register::get($key);
if (!$db) {
$db = new DataBase\Mysqli();
$db->conn('localhost', 'root', 'root', 'mrmf');
Register::set('Db' . $id, $db);
}
return $db;
}
通过调用工厂方法实现操作数据表的功能
$db = new SPL\DataObject(1);
$db->setDefSort(1,12345);
结果如下
7,观察者模式
当一个对象状态发生变化时,依赖它的对象全部会收到通知,并自动更新。当用户登录事件发生后,要执行一连串操作。比如根据vip判断是否显示广告,根据是否是新用户显示优惠活动。传统的编程方式,就是在登录的代码之后直接加入处理的逻辑。当登录后的操作增多后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件的主体代码。而观察者模式实现了低耦合,非侵入式的通知与更新机制。
Observer.php
//定义一个观察者接口
interface Observer {
public function update($id);
} YnVip.php
//定义两个具体的观察者类
class YnVip implements Observer {
public function update($id='')
{
echo empty($id)?'no vip':'yes vip','<br>';
}
}
YnNewUser.php class YnNewUser implements Observer {
public function update($id='')
{
echo empty($id) ? 'no new user' : 'yes new user','<br>';
}
} commom.php
//定义一个基类作为事件触发类
class Common {
private $observer;
public $id;
//新增观察者
function addObserver(Observer $observer){
$this->observer[]=$observer;
}
//通知各个观察者(执行观察者中的方法)
function notify(){
foreach ($this->observer as $observer) {
$observer->update($this->id);
}
}
} //定义一个继承了Common类的子类
class Login extends Common {
public function login($id=''){
echo "执行登录",'<br>';
$this->id=$id;
}
}
我们可以看见,观察者就是定义各种功能的对象,当我们需要该功能时,就可以使用Common类中的添加观察者方法添加观察者
$user=new SPL\Observer\Login();
$user->login();
$user->addObserver(new YnVip());
$user->addObserver(new YnNewUser());
$user->notify();
结果如下:
当需要添加其它功能时,我们不必要耦合的在事件主体中添加,只需增加一个观察者类,再动态地在事件主体添加调用观察者对象的语句就可以了
8、原型模式
与工厂模式类似,都是用来创建对象;与工厂模式的实现不同,原型模式是先创建好一个原型对象,然后通过clone原型对象来创建新的对象。这样就免去了类创建时重复的初始化操作;在这些场景我们可以使用原型模式:
1,大对象的创建,创建一个大对象需要很大的开销,如果每次new就会消耗很大,原型模式仅需要内存拷贝即可。
2,一个对象的初始化需要很多其他对象的数据准备或其他资源的繁琐计算,那么可以使用原型模式。
3,当需要一个对象的大量公共信息,少量字段进行个性化设置的时候,也可以使用原型模式拷贝出现有对象的副本进行加工处理。
class Prototype {
private $data;
function __construct()
{
echo "调用class Prototype类<br>";
}
function init($width = 20, $height = 10)
{
$data = array();
for ($i = 0; $i < $height; $i++) {
for ($j = 0; $j < $width; $j++) {
$data[$i][$j] = '*';
}
}
$this->data = $data;
}
function rect($x1, $y1, $x2, $y2)
{
foreach ($this->data as $k1 => $line) {
if ($x1 > $k1 or $x2 < $k1) continue;
foreach ($line as $k2 => $char) {
if ($y1 > $k2 or $y2 < $k2) continue;
$this->data[$k1][$k2] = ' ';
}
}
}
function draw()
{
foreach ($this->data as $line) {
foreach ($line as $char) {
echo $char;
}
echo "<br>";
}
}
}
$prototype = new SPL\Prototype();
$prototype->init();
$prototype1=clone $prototype;
$prototype->rect(1,3,4,6);
$prototype->draw();
echo '—————————————————<br>';
$prototype2=clone $prototype;
$prototype->rect(2,6,3,8);
$prototype->draw();
结果如下:
注意,在我们克隆对象时并不会执行构造方法,因为克隆是在堆中进行的,而在类加载流程中才会调用构造函数,最后生成的对象会放到堆中。
9,装饰器模式
作为一种结构型模式, 装饰器(Decorator)模式就是对一个已有结构增加"装饰"。这些''装饰'可以理解为新增的功能模块。如果要在一个类中的某些功能模块添加一些额外的功能,传统的编程模式是,写一个子类继承它,然后重写实现类的方法。而使用装饰器模式,我们只需要在功能实现前添加一个装饰器对象就可以实现新的功能。
装饰模式和观察者模式在代码实现部分相差不多。
初探PHP设计模式的更多相关文章
- 初探Java设计模式4:JDK中的设计模式
JDK中设计模式 本文主要是归纳了JDK中所包含的设计模式,包括作用和其设计类图.首先来个总结,具体的某个模式可以一个一个慢慢写,希望能对研究JDK和设计模式有所帮助.一.设计模式是什么(1)反复出现 ...
- 初探Java设计模式3:行为型模式(策略,观察者等)
行为型模式 行为型模式关注的是各个类之间的相互作用,将职责划分清楚,使得我们的代码更加地清晰. 策略模式 策略模式太常用了,所以把它放到最前面进行介绍.它比较简单,我就不废话,直接用代码说事吧. 下面 ...
- 初探Java设计模式2:结构型模式(代理模式,适配器模式等)
行为型模式 行为型模式关注的是各个类之间的相互作用,将职责划分清楚,使得我们的代码更加地清晰. 策略模式 策略模式太常用了,所以把它放到最前面进行介绍.它比较简单,我就不废话,直接用代码说事吧. 下面 ...
- 初探Java设计模式1:创建型模式(工厂,单例等)
Java 设计模式 一直想写一篇介绍设计模式的文章,让读者可以很快看完,而且一看就懂,看懂就会用,同时不会将各个模式搞混.自认为本文还是写得不错的,花了不少心思来写这文章和做图,力求让读者真的能看着简 ...
- 初探Java设计模式5:一文了解Spring涉及到的9种设计模式
本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...
- 初探Java设计模式4:一文带你掌握JDK中的设计模式
转自https://javadoop.com/post/design-pattern 行为型模式 策略模式 观察者模式 责任链模式 模板方法模式 状态模式 行为型模式总结 本系列文章将整理到我在Git ...
- Java面试,如何在短时间内做突击
面试前很有必要针对性的多刷题,大部分童鞋实战能力强,理论不行,面试前不做准备很吃亏.这里整理了很多常考面试题,希望对你有帮助. 面试技术文 Java岗 面试考点精讲(基础篇01期) Java岗 面 ...
- 这些喜闻乐见的Java面试知识点,你都掌握了吗?
最近分享了一些有关学习方法和经验的文章,得到了很多读者的反馈,恰巧大家在昨天推文中的投票里一直选择了"Java基础的复习方法"这一项,那么今天我们就谈谈这方面的内容吧. 其实对于J ...
- JS设计模式初探
目的:设计模式众多,尝试用博客记录下学到的不同设计模式的优劣,方便以后查阅. 前言:半年前看高程的时候看到设计模式这章,云里雾里,不是看不明白,而是不明白为啥要如此麻烦只为创建一个对象.直到最近完成了 ...
随机推荐
- 2015-2016 ACM ICPC Baltic Selection Contest D - Journey(广搜)
- [C#]AdvPropertyGrid的使用示例(第三方控件:DevComponents.DotNetBar2.dll)
开发环境:Visual Studio 2019 .NET版本:4.5.2 效果如下: 1.初始化界面: 2.属性“人物”-自定义控件显示: 3.属性“地址”-自定义窗体显示: 4.属性“性别”-枚举显 ...
- c# json序列化不包括某列
.[Newtonsoft.Json.JsonIgnore]特性:使用Newtonsoft.Json序列化时字段不会被序列化. .[System.Web.Script.Serialization.Scr ...
- Web中线程与IIS线程池自动回收机制
开发Web项目后,部署到 IIS上 ,运行一直稳定,当Web程序中加入了定时任务,或者线程之类的机制后,第二天发现悲催了,定时任务并没有执行,此时重新登录一下网站,定时任务又重新执行.原来IIS默认有 ...
- 2018-2019-2 网络对抗技术 20165318 Exp7 网络欺诈防范
2018-2019-2 网络对抗技术 20165318 Exp7 网络欺诈防范 原理与实践说明 实践目标 实践内容概述 基础问题回答 实践过程记录 简单应用SET工具建立冒名网站 ettercap D ...
- 出现Strict Standards: Only variables should be passed by reference in的解决方法
出现Strict Standards: Only variables should be passed by reference in的解决方法 代码报错: <br /><b> ...
- SCIE和SCI
SCI和SCIE(SCI Expanded)分别是科学引文索引及科学引文索引扩展版(即网络版),主要是收录自然科学.工程技术领域最具影响力的重要期刊,包括2000多种外围刊. SCIE和SCI一样吗? ...
- ASP.NET Core使用Docker-Compose实现多容器应用部署
一.需求背景 人生苦短,我用.NET Core!前面的<ASP.NET Core使用Docker进行容器化托管和部署>基础课程我们学习了如何使用Docker来部署搭建ASP.NET Cor ...
- Maven 教程(21)— maven-compiler-plugin 插件详解--
原文地址:https://blog.csdn.net/liupeifeng3514/article/details/80236077 maven是个项目管理工具,如果我们不告诉它我们的代码要使用什么样 ...
- Linux驱动架构之pinctrl子系统分析(一)
1.前言在嵌入式系统中,许多SoC的内部都包含了pin控制器,通过芯片内部的pin控制器,我们可以配置一个或者一组引脚的状态和功能特性,Linux内核为了统一各SoC厂商的引脚管理,提供了pinctr ...