设计模式不是一套具体的语言框架,是行之有效的编码规范,是一套被反复使用、多数人知晓的、经过分类的、代码设计经验的总结。使用设计模式的目的:为了代码可重用性、让代码更容易被他人理解、保证代码可靠性。合理使用设计模式将有助于初学者更加深入地理解面向对象思维。

一、三大基本模式

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] = '&#8194';
}
}
}
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设计模式的更多相关文章

  1. 初探Java设计模式4:JDK中的设计模式

    JDK中设计模式 本文主要是归纳了JDK中所包含的设计模式,包括作用和其设计类图.首先来个总结,具体的某个模式可以一个一个慢慢写,希望能对研究JDK和设计模式有所帮助.一.设计模式是什么(1)反复出现 ...

  2. 初探Java设计模式3:行为型模式(策略,观察者等)

    行为型模式 行为型模式关注的是各个类之间的相互作用,将职责划分清楚,使得我们的代码更加地清晰. 策略模式 策略模式太常用了,所以把它放到最前面进行介绍.它比较简单,我就不废话,直接用代码说事吧. 下面 ...

  3. 初探Java设计模式2:结构型模式(代理模式,适配器模式等)

    行为型模式 行为型模式关注的是各个类之间的相互作用,将职责划分清楚,使得我们的代码更加地清晰. 策略模式 策略模式太常用了,所以把它放到最前面进行介绍.它比较简单,我就不废话,直接用代码说事吧. 下面 ...

  4. 初探Java设计模式1:创建型模式(工厂,单例等)

    Java 设计模式 一直想写一篇介绍设计模式的文章,让读者可以很快看完,而且一看就懂,看懂就会用,同时不会将各个模式搞混.自认为本文还是写得不错的,花了不少心思来写这文章和做图,力求让读者真的能看着简 ...

  5. 初探Java设计模式5:一文了解Spring涉及到的9种设计模式

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  6. 初探Java设计模式4:一文带你掌握JDK中的设计模式

    转自https://javadoop.com/post/design-pattern 行为型模式 策略模式 观察者模式 责任链模式 模板方法模式 状态模式 行为型模式总结 本系列文章将整理到我在Git ...

  7. Java面试,如何在短时间内做突击

    面试前很有必要针对性的多刷题,大部分童鞋实战能力强,理论不行,面试前不做准备很吃亏.这里整理了很多常考面试题,希望对你有帮助.   面试技术文 Java岗 面试考点精讲(基础篇01期) Java岗 面 ...

  8. 这些喜闻乐见的Java面试知识点,你都掌握了吗?

    最近分享了一些有关学习方法和经验的文章,得到了很多读者的反馈,恰巧大家在昨天推文中的投票里一直选择了"Java基础的复习方法"这一项,那么今天我们就谈谈这方面的内容吧. 其实对于J ...

  9. JS设计模式初探

    目的:设计模式众多,尝试用博客记录下学到的不同设计模式的优劣,方便以后查阅. 前言:半年前看高程的时候看到设计模式这章,云里雾里,不是看不明白,而是不明白为啥要如此麻烦只为创建一个对象.直到最近完成了 ...

随机推荐

  1. Datagrip 2017.2 激活

    解决方法 参考网址:https://jetbrains-server.ru/2017/03/31/datagrip-2016-2017-activation/page/2/ 亲测使用http://id ...

  2. Android Studio 星云常用配置工具箱

    1. 安装插件 1.1 Android View绑定框架 开源地址:https://github.com/JakeWharton/butterknife 插件地址: https://github.co ...

  3. Java集合详解4:一文读懂HashMap和HashTable的区别以及常见面试题

    <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...

  4. java基础之 hashmap

    Hashmap是一种非常常用的.应用广泛的数据类型,最近研究到相关的内容,就正好复习一下.网上关于hashmap的文章很多,但到底是自己学习的总结,就发出来跟大家一起分享,一起讨论. 1.hashma ...

  5. vscode配合less的编译

    1.安装Easy LESS插件 2.打开settings.json,添加以下代码: "less.compile": { "sourceMap": true, & ...

  6. INSERT,UPDATE,DELETE时不写日志

    我们在维护数据库的过程中,可能会遇到海量数据的存储和维护,但在有的情况下,需要先试验,然后再对实际的数据进行操作,那么在试验这个过程中,我们是不需要写日志的,因为当你对海量数据操作时,产生的日志可能会 ...

  7. SpringMvc通过controller上传文件代码示例

    上传文件这个功能用的比较多,不难,但是每次写都很别扭.记录在此,以备以后copy用. package com.**.**.**.web.api; import io.swagger.annotatio ...

  8. centos8 安装 docker

    centos 安装docker  官方参考地址:https://docs.docker.com/install/linux/docker-ce/centos/ 里面包含包下载地址: https://d ...

  9. Oracle 层次查询 connect by

      oracle 层次查询 语法:       SELECT ... FROM            [WHERE condition]                             --过 ...

  10. 词向量实践(gensim)

    词向量训练步骤: 分词并去停用词 词频过滤 训练 项目完整地址:https://github.com/cyandn/practice/tree/master/Word2Vec gensim中Word2 ...