【PHP】PHP设计模式大全与应用场景
面向对象编程的基本原则:
1、单一职责:一个类,只需要做好一件事情。
2、开放封闭:一个类,应该是可扩展的,而不可修改的。
3、依赖倒置:一个类,不应该强依赖另外一个类。每个类对于另外一个类都是可替换的。
4、配置化:尽可能的使用配置,而不是硬编码。
5、面向接口编程:只需要关心接口,不需要关心实现。
1、单例设计模式
所谓单例模式,即在应用程序中最多只有该类的一个实例存在,一旦创建,就会一直存在于内存中!
单例设计模式常应用于数据库类设计,采用单例模式,只连接一次数据库,防止打开多个数据库连接。
一个单例类应具备以下特点:
单例类不能直接实例化创建,而是只能由类本身实例化。因此,要获得这样的限制效果,构造函数必须标记为private,从而防止类被实例化。
需要一个私有静态成员变量来保存类实例和公开一个能访问到实例的公开静态方法。
在PHP中,为了防止他人对单例类实例克隆,通常还为其提供一个空的私有__clone()方法。
单例模式的例子:
<?php
class Database {
private static $instance;
private function __construct(){}
private function __clone(){}
public static function getInstance(){
if (!(self::$instance instanceof self)) {
self::$instance = new self();
}
return self::$instance;
}
}
$a = Database::getInstance();
$b = Database::getInstance();
var_dump($a === $b); // true
2、工厂设计模式
主要是当操作类的参数变化时,只用改相应的工厂类就可以。
工厂设计模式常用于根据输入参数的不同或者应用程序配置的不同来创建一种专门用来实例化并返回其对应的类的实例。
我们举例子,假设矩形、圆都有同样的一个方法,那么我们用基类提供的API来创建实例时,通过传参数来自动创建对应的类的实例,他们都有获取周长和面积的功能。
工厂模式的例子:
<?php
interface InterfaceShape {
function getArea();
function getCircumference();
}
/**
* 矩形
*/
class Rectangle implements InterfaceShape {
private $width;
private $height;
public function __construct($width, $height){
$this->width = $width;
$this->height = $height;
}
public function getArea(){
return $this->width* $this->height;
}
public function getCircumference(){
return 2 * $this->width + 2 * $this->height;
}
}
/**
* 圆形
*/
class Circle implements InterfaceShape {
private $radius;
function __construct($radius){
$this->radius = $radius;
}
public function getArea(){
return M_PI * pow($this->radius, 2);
}
public function getCircumference(){
return 2 * M_PI * $this->radius;
}
}
/**
* 形状工厂类
*/
class FactoryShape {
public static function create(){
switch (func_num_args()) {
case 1:
return new Circle(func_get_arg(0));
case 2:
return new Rectangle(func_get_arg(0), func_get_arg(1));
default:
# code...
break;
}
}
}
$rect = FactoryShape::create(5, 7);
var_dump($rect); // object(Rectangle)#1 (2) { ["width":"Rectangle":private]=>int(5) ["height":"Rectangle":private]=>int(7) }
echo PHP_EOL;
$circle = FactoryShape::create(3);
var_dump($circle); // object(Circle)#2 (1) { ["radius":"Circle":private]=>int(3) }
3、观察者模式
观察者模式是挺常见的一种设计模式,使用得当会给程序带来非常大的便利,使用得不当,会给后来人一种难以维护的想法。
什么是观察者模式?一个对象通过提供方法允许另一个对象即观察者 注册自己)使本身变得可观察。当可观察的对象更改时,它会将消息发送到已注册的观察者。这些观察者使用该信息执行的操作与可观察的对象无关。结果是对象可以相互对话,而不必了解原因。观察者模式是一种事件系统,意味着这一模式允许某个类观察另一个类的状态,当被观察的类状态发生改变的时候,观察类可以收到通知并且做出相应的动作;观察者模式为您提供了避免组件之间紧密耦。看下面例子你就明白了!
<?php
/*
观察者接口
*/
interface InterfaceObserver
{
function onListen($sender, $args);
function getObserverName();
}
// 可被观察者接口
interface InterfaceObservable
{
function addObserver($observer);
function removeObserver($observer_name);
}
// 观察者抽象类
abstract class Observer implements InterfaceObserver
{
protected $observer_name;
function getObserverName(){
return $this->observer_name;
}
function onListen($sender, $args){
}
}
// 可被观察类
abstract class Observable implements InterfaceObservable
{
protected $observers = array();
public function addObserver($observer){
if ($observer instanceof InterfaceObserver) {
$this->observers[] = $observer;
}
}
public function removeObserver($observer_name){
foreach ($this->observers as $index => $observer) {
if ($observer->getObserverName() === $observer_name) {
array_splice($this->observers, $index, 1);
return;
}
}
}
}
// 模拟一个可以被观察的类
class A extends Observable
{
public function addListener($listener){
foreach ($this->observers as $observer){
$observer->onListen($this, $listener);
}
}
}
// 模拟一个观察者类
class B extends Observer
{
protected $observer_name = 'B';
public function onListen($sender, $args){
var_dump($sender);
echo PHP_EOL;
var_dump($args);
echo PHP_EOL;
}
}
// 模拟另外一个观察者类
class C extends Observer
{
protected $observer_name = 'C';
public function onListen($sender, $args){
var_dump($sender);
echo PHP_EOL;
var_dump($args);
echo PHP_EOL;
}
}
$a = new A();
// 注入观察者
$a->addObserver(new B());
$a->addObserver(new C());
// 可以看到观察到的信息
$a->addListener('D');
// 移除观察者
$a->removeObserver('B');
// 打印的信息:
// object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }
// string(1) "D"
// object(A)#1 (1) { ["observers":protected]=> array(2) { [0]=> object(B)#2 (1) { ["observer_name":protected]=> string(1) "B" } [1]=> object(C)#3 (1) { ["observer_name":protected]=> string(1) "C" } } }
// string(1) "D"
4、适配器模式
将一个类的接口转换成客户希望的另一个接口,适配器模式使得原本的由于接口不兼容而不能一起工作的那些类可以一起工作。
应用场景:老代码接口不适应新的接口需求,或者代码很多很乱不便于继续修改,或者使用第三方类库。例如:php连接数据库的方法:mysql,,mysqli,pdo,可以用适配器统一
<?php
// 老的代码
class User {
private $name;
function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
// 新代码,开放平台标准接口
interface UserInterface {
function getUserName();
}
class UserInfo implements UserInterface {
protected $user;
function __construct($user) {
$this->user = $user;
}
public function getUserName() {
return $this->user->getName();
}
}
$oldUser = new User('abc');
echo $oldUser->getName()."n".PHP_EOL;
$newUser = new UserInfo($oldUser);
echo $newUser->getUserName()."n";
// abcn
5、策略模式
将一组特定的行为和算法封装成类,以适应某些特定的上下文环境。
例如:一个电商网站系统,针对男性女性用户要各自跳转到不同的商品类目,并且所有广告位展示不同的广告。
UserStrategy.php
<?php
namespace IMooc;
interface UserStrategy {
function showAd();
function showCategory();
}
MaleUserStrategy.php
<?php
namespace IMooc;
class MaleUserStrategy implements UserStrategy {
function showAd(){
echo "IPhone6";
}
function showCategory(){
echo "电子产品";
}
}
FemaleUserStrategy.php
<?php
namespace IMooc;
class FemaleUserStrategy implements UserStrategy {
function showAd(){
echo "2014新款女装";
}
function showCategory(){
echo "女装";
}
}
<?php
interface FlyBehavior{
public function fly();
}
class FlyWithWings implements FlyBehavior{
public function fly(){
echo "Fly With Wings \n";
}
}
class FlyWithNo implements FlyBehavior{
public function fly(){
echo "Fly With No Wings \n";
}
}
class Duck{
private $_flyBehavior;
public function performFly(){
$this->_flyBehavior->fly();
}
public function setFlyBehavior(FlyBehavior $behavior){
$this->_flyBehavior = $behavior;
}
}
class RubberDuck extends Duck{
}
// Test Case
$duck = new RubberDuck();
/* 想让鸭子用翅膀飞行 */
$duck->setFlyBehavior(new FlyWithWings());
$duck->performFly();
/* 想让鸭子不用翅膀飞行 */
$duck->setFlyBehavior(new FlyWithNo());
$duck->performFly();
6、装饰器模式
定义:
装饰器模式(Decorator):动态的给一个对象添加一些额外的职责,就增加功能来说,装饰器比生成子类更加灵活。
结构:
Component:定义一个对象接口,可以给这些对象动态地添加职责。
ConcreteComponent:定义了一个具体的对象,也可以给这个对象添加一些职责。
Decorator:装饰抽象类,继承了 Component ,从外类来扩展 Component 类的功能,但对于 Component 来说,是无需知道 Decorator 的存在的。
ConcreteDecorator:具体的装饰对象,起到给 Component 添加职责的功能。
代码实例:
这里以一个游戏角色为例,角色本身自带基础攻击属性,也可以通过额外的武器装备增加属性值。这里的装备武器就是动态的给角色添加额外的职责。
// 1、角色Role.php,对应Component
/**
* 角色,抽象类
* Class Role
*/
abstract class Role
{
/**
* @return mixed
*/
abstract public function getName();
/**
* @return mixed
*/
abstract public function getAggressivity();
}
// 2、武器Arms.php,对应ConcreteComponent
/**
* 武器,继承抽象类
* Class Arms
*/
class Arms extends Role
{
/**
* 基础攻击力
* @var int
*/
private $aggressivity = 100;
/**
* @return string
*/
public function getName()
{
// TODO: Implement getName() method.
return '基础攻击值';
}
/**
* @return int
*/
public function getAggressivity()
{
// TODO: Implement getAggressivity() method.
return $this->aggressivity;
}
}
// 3、装饰抽象类RoleDecorator.php,对应Decorator
/**
* 装饰抽象类
* Class RoleDecorator
*/
abstract class RoleDecorator extends Role
{
/**
* @var Role
*/
protected $role;
/**
* RoleDecorator constructor.
* @param Role $role
*/
public function __construct(Role $role)
{
$this->role = $role;
}
}
// 4、剑Sword.php,对应ConcreteDecorator
/**
* 剑,具体装饰对象,继承装饰抽象类
* Class Sword
*/
class Sword extends RoleDecorator
{
/**
* @return mixed|string
*/
public function getName()
{
// TODO: Implement getName() method.
return $this->role->getName() . '+斩妖剑';
}
/**
* @return int|mixed
*/
public function getAggressivity()
{
// TODO: Implement getAggressivity() method.
return $this->role->getAggressivity() + 200;
}
}
// 5、枪Gun.php,对应ConcreteDecorator
/**
* 枪,具体装饰对象,继承装饰抽象类
* Class Gun
*/
class Gun extends RoleDecorator
{
/**
* @return mixed|string
*/
public function getName()
{
// TODO: Implement getName() method.
return $this->role->getName() . '+震天戟';
}
/**
* @return int|mixed
*/
public function getAggressivity()
{
// TODO: Implement getAggressivity() method.
return $this->role->getAggressivity() + 150;
}
}
// 6、调用
// 基础攻击值
$arms = new Arms();
echo $arms->getName();
echo $arms->getAggressivity() . '<br>';
// 基础攻击值+斩妖剑
$sword = new Sword(new Arms());
echo $sword->getName();
echo $sword->getAggressivity() . '<br>';
// 基础攻击值+震天戟
$gun = new Gun(new Arms());
echo $gun->getName();
echo $gun->getAggressivity() . '<br>';
// 基础攻击值+斩妖剑+震天戟
$person = new Gun(new Sword(new Arms()));
echo $person->getName();
echo $person->getAggressivity() . '<br>';
// 7、结果:
// 基础攻击值100
// 基础攻击值+斩妖剑300
// 基础攻击值+震天戟250
// 基础攻击值+斩妖剑+震天戟450
装饰器模式总结:
装饰器模式就是为已有功能动态地添加更多功能地一种方式。当系统需要新功能时,这些新加入的功能仅仅是为了满足一些特定情况下才会执行的特殊行为的需要。这时,装饰器模式提供了一个非常好的解决方案,它把每个要装饰的功能放在单独的类中,并让这个类包装它所要装饰的对象,因此,但需要执行特殊行为时,客户端代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象了。
装饰器模式把类中地装饰功能从类中移除,简化了原有的类。
有效地把类的核心职责和装饰功能区分开了。而且可以去除相关类中重复的装饰逻辑。
6、装饰器模式(示例2)
装饰器模式又叫装饰者模式。装饰模式是在不必改变原类文件和使用继承的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰来包裹真实的对象。
1、动态的添加修改类功能
2、一个类提供了一项功能,如果要在修改并添加额外的功能,传统方案需要写一个子类继承,并重新实现类方法
3、使用装饰器模式,仅需要在运行时增加一个装饰器对象
// 例如修改Canvas的draw方法
class Canvas {
private $data;
private $decorators; // 用于保存所有装饰器
public function init($hei,$wid){
for($i=0;$i<$hei;$i++){
for($i=0;$i<$wid;$i++){
$data[$i][$j] = "*";
}
}
$this->data = $data;
}
public function rect($a1,$a2,$b1,$b2) {
foreach($this->data as $k1->$line){
if($k1<$a1 or $k1 > $a2) continue;
foreach($line as $k2 => $item){
if($k2<$b2 or $k2> $b2) contine;
$this->data[$k1][$2] = ' ';
}
}
}
public function draw(){
foreach ($this->data as $line){
foreach ($lien as $item) {
echo $item;
}
echo PHP_EOL:
}
}
// 用于增加装饰器
public function addDecorator(Decorator $decorator){
$this->decorators[] = $decorator;
}
// 前置执行
public function before(){
foreach($this->decorators as $decorator) {
$decorator->before();
}
}
public function after(){
$decorators = array_reserse($this->decorator);
foreach($decorators as $decorator) {
$decorator->before();
}
}
}
// 装饰器接口 在某个方法之前,之后加入额外操作
interface Decorator {
public function beforDraw();
public function afterDraw();
}
class ColorDecorator implements Decorator {
private $color;
public function __construct($color){
$this->color = $color;
}
public function before(){
echo 'before'.$this->color;
}
public function after(){
echo 'after';
}
}
$c = new Canvas();
$c->addDecorator(new ColorDecorator('red')); // 增加不同的装饰器,进行不同的修改
$c->rect(1,6,2,12);
$c->draw();
7、注册树模式
特点:注册树模式通过将对象实例注册到一棵全局的对象树上,需要的时候从对象树上采摘的模式设计方法。
应用:不管你是通过单例模式还是工厂模式还是二者结合生成的对象,都统统给我“插到”注册树上。我用某个对象的时候,直接从注册树上取一下就好。这和我们使用全局变量一样的方便实用。而且注册树模式还为其他模式提供了一种非常好的想法。 (如下实例是单例,工厂,注册树的联合使用)
<?php
// 创建单例
class Single{
public $hash;
protected static $ins = null;
final protected function __construct(){
$this->hash=rand(1,9999);
}
public static function getInstance(){
if (self::$ins instanceof self) {
return self::$ins;
}
self::$ins = new self();
return self::$ins;
}
}
// 工厂模式
class RandFactory{
public static function factory(){
return Single::getInstance();
}
}
// 注册树
class Register{
protected static $objects;
public static function set($alias,$object){
self::$objects[$alias] = $object;
}
public static function get($alias){
return self::$objects[$alias];
}
public static function _unset($alias){
unset(self::$objects[$alias]);
}
}
Register::set('rand', RandFactory::factory());
$object = Register::get('rand');
print_r($object);
8、数据对象映射模式 orm
对象和数据存储映射,对对象的操作映射为对数据的存储操作。
// 映射到user表
class User {
public $id;
public $name;
public $regtime;
private $db;
public function __counstruct($id){
$this->db = (new Factory())->createDB);
$this->db->connect($host,$user,$pwd,$dbname); // 这里为工厂模式创建,可以改为这册器模式,进一步进行优化,例如一次业务中需要实例化这个类多次。 这里不能用单利模式,因为每个id应该为不同的实例
$rlt = $this->db->query("select * from user where id ='$id'")->fetchAll();
// 建立映射关系
$this->id = $rlt['id'];
$this->name = $rlt['name'];
$this->regtime = $rlt['regtime'];
}
public function __destruct(){
$this->db->exec("update user set name = $this->name regtime=$this->regtime where id = $id");
}
}
$user = new User(1); // 操作id为1的用户
$user->name="aa";
$user->regtiem = time();
9、原型模式
1、与工厂模式类似
2、与工厂模式实现不同,原型模式时先创建好一个原型对象,然后通过clone原型对象来创建新对象,免去了类创建时的初始化操作
3、原型模式适用于大对象创建,创建大对象开销大,每次new就会消耗很大,原型模式仅需内存拷贝
class Canvas {
private $data;
public function init($hei,$wid){
for($i=0;$i<$hei;$i++){
for($i=0;$i<$wid;$i++){
$data[$i][$j] = "*";
}
}
$this->data = $data;
}
public function rect($a1,$a2,$b1,$b2) {
foreach($this->data as $k1->$line){
if($k1<$a1 or $k1 > $a2) continue;
foreach($line as $k2 => $item){
if($k2<$b2 or $k2> $b2) contine;
$this->data[$k1][$2] = ' ';
}
}
}
public function draw(){
foreach ($this->data as $line){
foreach ($lien as $item) {
echo $item;
}
echo PHP_EOL:
}
}
}
$canvas = new Canvas1();
$canvas->init(3,6,4,12);
$canvas->draw();
// 传统,再new一个对象,画其他长方形
// 原型模式
$prototype = new Canvas();
$prototype->init();
$canvas2= clone $prototype; // 克隆一个, 剩余的都走clone就可以了,不需要再new了
$canvas2->rect(1,3,2,6);
$canvas2->draw();
10、迭代器模式
不了解内部实现前提下,遍历一个对象
// 继承内置的迭代器接口,实现五个方法
class Alluser implements \Iterator{
private $ids; // 存入所有需要迭代的数据
private $index; // 记录当前迭代器位置
public function __construct(){
$rlt = "select id from user";
$this->ids = $rlt->fetch();
}
public function current() {
return $this->ids[$this->index];
}
// 下一个元素
public function next(){
$this->index++;
}
// 验证元素是否存在
public function valid(){
return !empty($this->ids[$this->index]);
}
// 初始化迭代器到头部
public function rewind(){
$this->index = 0;
}
// 获取当前索引
public function key(){
return $this->index;
}
}
$users = new AllUser();
foreach($users as $user){
var_dump($user);
}
11、代理模式
1、在客户端和实体之间建立一个代理对象(proxy),客户端对实体进行操作全部委派给代理对象,隐藏具体的实现,例如,mysql的主从结构,不修改业务代码,在代理中实现读写分离
2、Proxy还可以与业务代码分离,部署到另外的服务器,业务代码中通过rpc来委派任务
class Proxy {
public function getUserName($id){
$db = Factory::getDatabase('slave');
$db->query("select * from user where id =$id");
}
public function setUserName(){
$db = Factory::getDatabase('master');
$db->query("update user set name=$name whereid=$id limit 1");
}
}
// 其实还应该封装下,在执行语句的时候自动根据语句连接主从库
猜你喜欢
- 【PHP】PHP8.1新特性大讲解之Enums枚举
- PHP 8.1:枚举它们终于来了——PHP 8.1中将添加对枚举的内置支持!有些人可能认为他们早就应该这样做了,但你没有听到我的抱怨;我很高兴他们做到了!这篇文章致力于深入研究新添加的功能。像往常一样,在我的 PHP 功能帖子中,我们首先对枚举的外观进行高级概述:enum Status { case DRAFT; case PUBLISHED; &
- 【PHP】php正则表达式有哪些
- php正则表达式有"/pattern/"、"^"、"$"、"."、"[]"、"[^]"、"[a-z]"、"[A-Z]"、"[0-9]"、"\d"、"\D"、"\w"、"\W"、"\s"、"\S&quo
- 【PHP】php7弃用的函数有哪些
- 本教程操作系统:windows10系统、PHP 8.1.3版本、DELL G3电脑。PHP 7是PHP编程语言的一个重要版本,引入了许多新特性和改进。同时,为了提高代码的质量和安全性,PHP 7还废弃了一些旧的函数。下面是一些在PHP 7中被弃用的函数的例子:1. mysql_ 系列函数:在PHP 7中,mysql_ 系列函数(如mysql_connect、mysql_query等)被弃用。这些函数是用于与MySQL数据库进行交互的旧API,而在PHP 5.5版本中已经引入了更现代化和安全的my
- 【PHP】ThinkPHP与Laravel一样吗
- thinkphp和laravel是不一样的。thinkphp和laravel虽然都是php开发框架,但是有很多区别:1、渲染末班的方式不同,thinkphp用“$this->display()”的方式渲染模版,laravel使用“return view()”方法;2、laravel是一个重路由的框架,而thinkphp要有控制器方法才能正常访问。thinkphp和laravel不一样ThinkPHP是免费开源的,快速的,简单的,面向对象的轻量级PHP开发框架,ThinkPHP可以支持win
- 【PHP】PHP面试题
- 1.详述一次完整的HTTP请求过程这个问题的核心是域名解析和服务器(nginx)解析这两部分,基本上这两部分详细阐述就可以了。步骤一、解析URL浏览器会解析当前的URL数据,判断此URL是否为合法的链接。如果是合法链接则正常的向下一步骤前进。如果不是合法的链接,则会执行搜索功能,例如执行百度、360、Google搜索等。步骤二、解析域名服务器是以ip的形式存在的。而域名需要解析到ip上,解析IP会有三个小的步骤:1)、从浏览器自身的缓存中解析此域名数据2)、从本地电脑的HOST文件中解析域名3)
- 【PHP】PHP接入微信官方支付(native·APIv3)
- 一、项目介绍两个文件实现微信官方支付(native·APIv3)的发起支付和回调应答功能二、准备资料商户号:需要使用到营业执照注册商户appid:小程序或者订阅号的appidAPIv3秘钥:32位秘钥,APIv2秘钥为16位,不要混淆证书序号:apiclient_key.pem文件中的秘钥,需要将该文件改为txt后缀,然后获取其中的秘钥三、支付代码1.index.php文件<?php //支付配置 $mchid = '';//微信支付商户号 P
- 【PHP】php中的compact()的用法
- compact()参数有两种变量名的字符串形式数组的变量名变量名的字符串$city = "San Francisco"; $state = "CA"; $event = "SIGGRAPH"; $location=["city",'state']; $result=compact('city','
- 【PHP】PHP8中支持数组的新函数,让数组操作变得更加便捷
- PHP是一种常用的Web编程语言,已经成为了众多Web应用的首选开发语言。在PHP8中,新增了不少有用的函数和特性,其中一个十分值得关注的改变就是对数组操作的优化。PHP8中新增了许多针对数组的函数,使得开发者可以更易于编写高效的代码并减少代码中的常见错误。在本文中,我们将介绍PHP8中一些有用的数组函数,并展示如何使用它们来提高自己的PHP编程技能。array_is_list()函数array_is_list()函数用于检查一个数组是否是“列表数组”,即数组中的索引是否是从0开始、且连续递增的