十二种常见设计模式代码详解

article/2025/10/3 0:25:12

零:设计模式分类

设计模式有创建型模式、结构型模式与行为型模式

  • 创建型:单例模式、工厂模式(简单工厂,工厂方法,抽象工厂)
  • 结构型:适配器模式、门面模式、装饰器模式、注册树模式、代理模式、管道模式
  • 行为型:策略模式、观察者模式、命令模式、迭代器模式

1. 创建型模式

创建型模式的一个重要思想就是封装,利用封装,把直接获得一个对象改为通过一个接口获得一个对象

  • 单例模式:我们把对象的生成从new改为通过一个静态方法,通过静态方法的控制,使得我们总是返回同一个实例给调用者,确保了系统只有一个实例。
  • 工厂模式:与单例模式相同,生成对象改为接口,还可以通过传参实例化不同的类
    • 简单工厂:为不同的类提供一个生产对象的工厂。要获取类的对象只能通过工厂生产获取
    • 工厂方法模式:为工厂方法提供接口,规范接口方法。每一个工厂针对一个类生产对象
    • 抽象工厂模式:在不指定具体类的情况下创建一系列相关或依赖对象,通常创建的类都实现相同的接口

2. 结构型模式

解析类和对象的内部结构和外部组合,通过优化程序结构解决模块之间的耦合问题

种类:

  • 装饰器模式
  • facade门面模式
  • 注册树模式(IOC/DI)
  • 适配器模式
  • Pipeline模式
  • 代理模式

3. 行为型模式

行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎么相互协作共同完成单个对象都无法单独完成的任务,它设计算法与对象间职责的分配

种类:

  • 策略模式
  • 观察者模式
  • 命令模式
  • 迭代器模式

一:单例模式

单例模式(singleton),即一种类的设计只会最多产生一个对象的设计思想
在这里插入图片描述

作用:

  • 主要用在数据库应用中,因为一个应用中会存在大量的数据库操作,使用单例模式则可以避免大量的new操作消耗资源。
  • 如果系统中需要有一个类来全局控制某些配置信息,那么使用单例模式可以很方便的实现
  • 在一次页面请求中,便于进行调试,因为所有的代码(例如数据库操作类)会集中在一个类中,我们可以在类中设置钩子,输出日志,从而避免到处var_dump、echo

应用场景:数据库连接、缓存操作、分布式存储

单例模式的要点:

  • 构造函数需要标记为private(访问控制:防止外部代码使用new操作符创建对象),单例类不能在其他类中实例化,只能被其自身实例化
  • 拥有一个保存类的实例的静态成员变量
  • 拥有一个访问这个实例的公共的静态方法(常用getInstance()方法进行实例化单例类,通过instanceof操作符可以检测到类是否已经被实例化)
    • 简称"三私一公":
    • 私有化构造方法:不让外部产生多个对象
    • 私有化克隆方法:不允许对象被克隆对象产生新对象
    • 私有化静态属性:运行进入类内部产生对象
    • 公有化静态方法:保存已经产生的对象
<?php
/** 单例模式*/// 数据库操作类
class Dbsql{// 私有属性存储对象private static $ins = null;// 私有化构造方法,避免在类外产生对象private function __construct(){}// 公有静态方法,创建实例public static function createIns(){// 不存在对象就创建对象self::$ins == null ? new self() : self::$ins;return self::$ins;}// 私有化克隆方法,避免在类外克隆对象private function __clone(){}// 功能方法// ...
}$m1 = Dbsql::createIns();
$m2 = Dbsql::createIns();var_dump($m1 === $m2);  // bool(true)

二:工厂模式

工厂模式是我们最常用的实例化对象模式,是用工厂方法代替new操作的一种模式。

优点:如果你想更改所实例化的类名等,则只需更改该工厂方法内容,不需要注意寻找代码中具体实例化的地方(new处)修改。为系统结构提供零花的动态扩展机制,减少了耦合。

分类:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

在这里插入图片描述

1. 简单工厂模式

简单工厂模式又称静态工厂模式,之所以这么说,是因为简单工厂模式是通过一个静态方法来创建对象的。

<?php
/** 简单工厂*/
class DbMysql{public function conn(){echo "连接Mysql";}
}class DbSqlit
{public function conn(){echo "连接sqlite";}
}class DbFactory
{public static function createIns($type){// 这么写的话,如果我要连接Oracle,就需要修改代码,不符合开闭原则switch ($type){case "mysql":return new DbMysql();case "sqlit":return new DbSqlit();default:throw new ErrorException("error type!");}}
}// 客户端
$mysql = DbFactory::createIns("mysql");
$mysql->conn();$mysql = DbFactory::createIns("sqlit");
$mysql->conn();

2. 工厂方法模式

简单工厂模式实现了产品类的代码跟客户端代码分离,但会有一个问题,优秀的代码是符合开闭原则,如果你要加一个C类产品,就要修改工厂类里面的代码,也就是要增加语句如:switch—case,对于这个问题,工厂方法模式可以解决

工厂方法模式就是为配一个产品提供一个独立的工厂类,通过不同的工厂实例来创建不同的产品实例。

优点:

  • 拥有良好封装性,代码结构清晰。对于每一个对象的创建都是有条件约束的。如:调用一个具体的产品对象,只需要知道这个产品的类名和约束参数就可以了,不用知道创建对象自身的复杂程度,降低模块之间的耦合度。
  • 拥有良好的拓展性,新增一个产品类,只需要适当的增加工厂类或者扩展一个工厂类,如下面这个例子,当需要增加一个数据库Oracle的操作,只需要增加一个Oracle类,厂类不用修改任务就可以完成系统扩展。
  • 屏蔽产品类。这一特点非常重要,产品类的实现如何变化,调用者都不需要关心,他只关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。

使用场景:

  • 支付宝、微信、银联的连接方式(connectMode),支付方式(payMode)。使用工厂模式,客户就不需要知道具体的连接方式和支付方式了,只需要调用connectMode和payMode即可
  • MySQL、SQL Server、Oracle等数据库的连接方式(connectMode)、查询方式(selectMode)等操作可以使用工厂模式进行封装
<?php
/** 工厂方法*/
interface Db
{public function conn();
}class DbMysql implements Db
{public function conn(){echo "连接mysql";}
}class DbSqlit implements Db
{public function conn(){echo "连接Sqlit";}
}interface Factory
{public static function createIns();
}class MysqlFactory implements Factory
{public static function createIns(){return new DbMysql();}
}$mysql = MysqlFactory::createIns();

3. 抽象工厂模式

抽象工厂模式的用意为:给客户提供一个接口,可以创建多个产品族中的产品对象,而且使用抽象工厂模式还要满足条件:

  • 系统中有多个产品族,而系统一次只可能消费其中一族产品
  • 同属于一个产品族的产品可以使用

产品族:位于不同产品等级结构中,功能相关联的产品组成的家族。下面例子中的汽车和空调就是两个产品树,奔驰C200使用了格力某型号空调,那他俩就是一个产品族,同理,奥迪A4和海尔某型号空调也是一个产品族

在这里插入图片描述

<?php
/** 抽象工厂*/
interface AutoProduct
{public function dirve();
}// 奥迪A4
class AudiA4Product implements AutoProduct
{public function dirve(){echo "奥迪A4";}
}// 奔驰C200
class BenzC200Product implements AutoProduct
{public function dirve(){echo "奔驰C200";}
}interface AirCondition
{public function blow();
}// 格力
class GreeAirCondition implements AirCondition
{public function blow(){echo "格力空调";}
}// 海尔
class HeierAirCondition implements AirCondition
{public function blow(){echo "海尔空调";}
}# 工厂接口
interface Factory
{public function getAuto();public function getAirCondition();
}# 工厂A  奔驰C200 + 格力
class AFactory implements Factory
{public function getAuto(){return new BenzC200Product();}public function getAirCondition(){return new GreeAirCondition();}public static function createIns(){}
}# 工厂B  奥迪4 + 海尔
class BFactory implements Factory
{public function getAuto(){return new AudiA4Product();}public function getAirCondition(){return new HeierAirCondition();}public static function createIns(){}
}// 工厂运作
$auto_carA = AFactory::getAuto();
$auto_airA = AFactory::getAirCondition();$auto_carB = BFactory::getAuto();
$auto_airB = BFactory::getAirCondition();

三:适配器模式

概念:将某个类的接口转换成与另一个接口兼容。适配器通过将原始接口进行转换,给用户提供一个兼容接口,使得原来因为接口不同而无法一起使用的类可以得到兼容
在这里插入图片描述
应用场景:老代码接口不适应新的接口需求,或者代码很多很乱不便于继续修改,或者使用第三方类库

<?php
/** 适配器模式*/
interface Weather
{public function show();
}class PHP_Weather implements Weather
{public function show(){// 得到的天气信息$weather = ["weather"   => "小雨","tep"       => 6,"win"       => 3];return serialize($weather);}
}$weather = new PHP_Weather();
$info = unserialize($weather->show());
echo "天气:{$info['weather']}<br>";
echo "温度:{$info['tep']}<br>";
echo "风力:{$info['win']}<br>";// 把上面的类变成兼容,使得py、java能直接使用
interface WeatherAdapter
{public function getWeather();
}
// java接入的接口
class Java_Weather implements WeatherAdapter
{protected $weather;public function __construct(Weather $weather) {$this->weather = $weather;}public function getWeather(){$info = unserialize($this->weather->show());// 即转为json格式return json_encode($info);}
}

四:装饰器模式

装饰器模式又叫装饰者模式,装饰模式是在不必改变原类文件和使用继承的情况下,动态的拓展一个对象的功能。他是通过创建一个包装对象,也就是装饰来包裹真实的对象。

实例:

  • 组件对象的接口:可以给这些对象动态的添加职责
  • 所有装饰器的父类:需要定义一个与组件接口一致的接口,并持有一个Component对象,该对象其实就是被装饰的对象
  • 具体的装饰器类:实现具体要向被装饰对象添加的功能。用来装饰具体的组件对象或者另外一个具体的装饰器对象。

使用场景:

  • 需要动态的给一个对象添加功能,这些功能可以再动态的撤销
  • 需要增加由一些基本功能的排列组合而产生的非常大量的功能,从而使继承关系变得不现实
  • 当不能采用生成子类的方法进行扩充时.一种情况是,可能有大量独立的扩展,为支持每一种组合将产生大量的子类,使得子类数目呈爆炸式增长。另一种情况可能是因为类定于被隐藏,或类定义不能用于生成子类
<?php
/** 装饰器模式*/// 组件对象接口
interface IComponent
{// 动态的给对象添加功能public function display();
}// 待装饰的对象
class Person implements IComponent
{protected $name;public function __construct($name){$this->name = $name;}public function display(){echo "装饰的" . $this->name . "<br>";}
}// 所有装饰器的父类
class Clothes implements IComponent
{protected $component;public function Decorate(IComponent $component){$this->component = $component;}public function display(){if(!empty($this->component)) {$this->component->display();}}
}// 具体的装饰器
class Shoes extends Clothes
{public function display(){echo "鞋子";parent::display();}
}
class Overcoat extends Clothes
{public function display(){echo "外套";parent::display();}
}$qd = new Person("乔丹");$shoes = new Shoes();
$shoes->Decorate($qd);
$shoes->display();$coat = new Overcoat();
$coat->Decorate($qd);
$coat->display();

五:注册树模式

注册树模式也叫注册模式或注册器模式,顾名思义,注册树就是把对象实例注册到一颗全局的对象树上,需要对象的时候就从书上取下来,就好比书上长的果子,需要的时候就摘一个下来,只是这个对象树的果子是摘不完的

不管前面用单例还是工厂建立的对象,都是一个个游兵散将,没有得到很好的管理,那如果用了注册树模式,就可以把创建出来的对象注册到全局树上,需要的时候取下来用,可以很好的管理创建的对象

<?php
/** 注册树模式*/
class DbMysql
{public function conn(){echo "连接Mysql";}
}
class DbSqlite
{public function conn(){echo "连接sqlite";}
}
class MysqlFactory
{public static function getIns(){return new DbMysql();}
}
class SqliteFactory
{public static function getIns(){return new DbSqlite();}
}// 注册数实现存储对象(IOC控制反转思想)
class RegisterTree
{protected static $objects;// 添加对象到注册树中public static function set($alias, $object){self::$objects[$alias] = $object;}// 从注册树中获取对象public static function get($alias){return self::$objects[$alias];}// 销毁注册树上的对象public function __unset($alias){unset(self::$objects[$alias]);}
}// 注册
RegisterTree::set("mysql", MysqlFactory::getIns());
RegisterTree::set("sqlite", SqliteFactory::getIns());// 客户端
$mysql = RegisterTree::get("mysql");
$mysql->conn();$sqlite = RegisterTree::get("sqlite");
$sqlite->conn();

六:门面模式

门面模式(Facade)又称外观模式,用于为子系统中的一组接口提供一致的界面。门面模式定义了一个高级接口,这个接口使得子系统更加容易使用:引入门面角色之后,用户只需要直接与门面角色交互,用户与子系统之间的复杂关系由门面角色来实现,从而降低了系统的耦合度

在这里插入图片描述
作用:

  • 为一些复杂的子系统提供一组接口
  • 提高子系统的独立性
  • 在层次化结构中,可以使用门面模式定义系统的每一层接口

优点:

  • 他对客户屏蔽了子系统组件,因而减少了客户处理的对象的数目并使得子系统使用起来更加方便
  • 实现了子系统与客户之间的松耦合关系
  • 如果应用需要,他不限制他们使用子系统类。因此可以在系统易用性与能用性之间加以选择
<?php
/** 门面模式*/class Camera
{public function turnOn(){echo "打开摄像机";}public function turnOff(){echo "关闭摄像机";}
}class Light
{public function turnOn(){echo "打开灯";}public function turnOff(){echo "关闭灯";}
}// 门面类
class Facade
{private $_camera;private $_light;public function __construct(){$this->_camera = new Camera();$this->_light = new Light();}// 启动接口public function activate(){$this->_camera->turnOn();$this->_light->turnOn();}// 关闭接口public function deactivate(){$this->_camera->turnOff();$this->_light->turnOff();}
}// 客户端
class Client
{private static $_securit;public static function main(){self::$_securit = new Facade();self::$_securit->activate();self::$_securit->deactivate();}
}Client::main();

七:管道模式

概念:管道(Pipeline)设计模式即流水线模式就是会将数据传递到一个任务序列中,管道扮演着流水线的角色,数据在这里被处理然后传递到下一个步骤

释意:

  • 管道,顾名思义,就是一个长长的流水管道,只不过加了许多阀门,所以管道模式大致需要三个角色,管道,阀门和载荷。
  • 目的是在管道中对载荷进行一系列的处理。因为可以对过滤器进行动态的添加,所以对载荷的处理可以变得更加灵活。但同时带来的问题是,在过滤器过多时,很难把握整体的处理逻辑,而且在某一个过滤器对载荷处理后,因为载荷改变,会造成下一个过滤器中的逻辑错误

在这里插入图片描述

<?php
/** 管道模式*/// 管道接口
interface PipelineBuilder
{public function __construct($payload); // $payload载荷public function pipe(StageBuilder $stage);  // 管道与阀门public function process();  // 过程
}// 实现管道接口
class Pipeline implements PipelineBuilder
{protected $payload;protected $pipes = [];public function __construct($payload){$this->payload = $payload;}public function pipe(StageBuilder $stage){$this->pipes = $stage;return $this;}public function process(){foreach ($this->pipes as $pipe) {call_user_func([$pipe, "handle"], $this->payload);}}
}// 阀门接口
interface StageBuilder
{public function handle($payload);
}// 实现阀门
class StageOneBuilder implements StageBuilder
{public function handle($payload){echo $payload . "真是个";}
}
class StageTwoBuilder implements StageBuilder
{public function handle($payload){echo "有趣的东西";}
}// 客户端使用
$pipeline = new Pipeline('joke');
$pipeline->pipe(new StageOneBuilder())->pipe(new StageTwoBuilder())->process();

八:代理模式

概念:代理模式(Proxy Pattern)构建了透明置于两个不同对象之内的一个对象,从而能够截取或代理这两个对象之间的通信或访问

应用场景:

  • 远程代理:就是为了一个对象在不同地址空间提供局部代表。隐藏一个对象存在于不同地址空间的事实
  • 虚拟代理:根据需要来创建开销很大的对象,通过它来存放实例化需要很长时间的真实对象
  • 安全代理:用来控制真实对象的访问对象
  • 智能指引:当调用真实对象的时候,代理处理一些事情

在这里插入图片描述

注意代理模式与装饰器和适配器模式的区分:装饰器一般是对对象进行装饰,其中的方法行为会有所增加,以修饰对象为主,适配器一般会改变方法行为,目的是保持接口的统一但得到不同的实现。

代理模式一般有几种形式:

  • 远程代理,例如第三方sdk
  • 虚代理,例如异步加载图片
  • 保护代理&智能指引,例如权限保护
<?php
/** 代理模式*/// 真实接口
interface Subject
{public function request();
}
// 真实类
class RealSubject implements Subject
{public function request(){echo "真实的操作";}
}// 代理模式代理真实类
class Proxy implements Subject
{protected $realSubject;public function __construct(){$this->realSubject = new RealSubject();}public function request(){echo "代理操作";$this->realSubject->request();}
}$proxy = new Proxy();
$proxy->request();

九:策略模式

概念:

  • 将一组特定的行为和算法封装成类,以适应某些特定的上下文环境,并让他们可以相互替换,这种模式就是策略模式
  • 简单理解就是由n个做法供你选择,根据你的需要选择某个策略得到结果

优点:

  • 使用设计模式之后,我们的代码冗余和耦合度变低,每个策略模块完成对应的功能
  • 当然缺点就是一个功能我们就要相应制作一个策略类,但是我们统观MVC架构当中(ThinkPHP, Yii2)。一个控制器对应一个视图,其实也是策略设计模式的体现

在这里插入图片描述

<?php
/** 策略模式*/// 用户接口
interface UserInterface
{/*** @purpose 展示广告* @return mixed*/public function showAd();/*** @purpose 展示商品分类* @return mixed*/public function showCategory();
}// 男性用户实现接口
class MaleUser implements UserInterface
{public function showAd(){echo "电子产品";}public function showCategory(){echo "手机、电脑、手表";}
}// 女性用户实现接口
class femaleUser implements UserInterface
{public function showAd(){echo "化妆品";}public function showCategory(){echo "衣服分类";}
}// 判断用户是男性还是女性,根据用户性别推荐不同内容
if ($_GET('sex') == 'male') {$user = new MaleUser();
} else {$user = new FemaleUser();
}$user->showAd();
$user->showCategory();

十:观察者模式

概念:

  • 观察者模式属于行为模式,是定义对象间的一种一对多的依赖关系,以便当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并自动刷新
  • 当一个对象状态发生改变后,会影响到其他几个对象的改变,这时候可以用观察者模式
  • 观察者模式符合接口隔离原则,实现了对象之间的松散耦合

角色:

  • 抽象主题(Subject):他把所有观察者对象的引用保存到一个聚集里,每个主题都可以有任何数量的观察者。抽象主题提供一个接口,可以增加和删除观察者对象
  • 具体主题(ConcreteSubject):将有关状态存入具体观察者对象;在具体主题内部状态改变时,给所有登记过的观察者发出通知
  • 抽象观察者(Observer): 为所有的具体观察者定义一个接口,在得到主题通知时更新自己
  • 具体观察者(ConcreteObserver):实现抽象观察者角色所要求的更新接口,以便使本身的状态与主题状态协调
<?php
/** 观察者模式*//*** Interface Subject* 抽象主题*/interface Subject
{/*** @purpose 新增观察者* @param Observer $observer* @return mixed*/public function attach(Observer $observer);/*** @purpose 移除观察者* @param Observer $observer* @return mixed*/public function detach(Observer $observer);/*** @purpose 通知观察者* @return mixed*/public function notifyObserver();
}/*** @purpose 具体主题* Class ConcreteSubjects*/
class ConcreteSubject implements Subject
{// 存储观察者private $observes;public function __construct(){$this->observes = [];}public function attach(Observer $observer){return array_push($this->observes, $observer);}public function detach(Observer $observer){$index = array_search($observer, $this->observes);if ($index === false || array_key_exists($index, $this->observes)) return false;unset($this->observes[$index]);return true;}public function notifyObserver(){if(!is_array($this->observes)) return false;foreach ($this->observes as $observe) {// 通知改变$observe->update();}return true;}
}/*** @purpose 抽象观察者* Interface Observer*/
interface Observer
{/*** @purpose 观察者状态改变* @return mixed*/public function update();
}/*** @purpose 具体观察者的* Class ConcreteObserver*/
class ConcreteObserver implements Observer
{private $name;public function __construct($name){$this->name = $name;}public function update(){echo "观察者" . $this->name . "已经收到通知<br>";}
}// 注册观察者,监听
$subject = new ConcreteSubject();// 增加观察者
$subject->attach(new ConcreteObserver("joke"));
$subject->attach(new ConcreteObserver("lisa"));
$subject->attach(new ConcreteObserver("lucy"));$subject->notifyObserver();

十一:命令模式

概念:

  • 将一个请求封装(命令的封装)为一个对象,从而使用你可用不同的请求对客户进行参数化。对请求排队或记录请求日志,以及支持可撤销的操作
  • 命令模式是对命令的封装。命令模式把发出命令的责任和执行命令的责任分隔开,委派给不同的对象
  • 请求的一方发出请求要求执行一个操作,接收的一方收到请求,并执行操作,命令模式允许请求的一方和接收的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收的,以及操作是否被执行、何时被执行,以及是怎么被执行的

在这里插入图片描述

角色:

  • 命令(Command)角色:声明一个给所有具体命令类的抽象接口,这是一个抽象角色
  • 具体命令(ConcreteCommand)角色:定义一个接受者和行为之间的弱耦合,实现Execute()方法,负责调用接收的相应操作,Execute()方法通常叫做执行方法
  • 客户(Client)角色:创建一个具体命令(ConcreteCommand)对象并确定其接收者
  • 请求者(Invoker)角色:负责调用命令对象执行请求,相关方法叫做行动方法
  • 接收者(Receiver)角色:负责具体实施和执行一个请求。任何一个类都可以成为接收者,实施和执行请求的方法叫做行动方法

优点

  • 命令模式把请求一个操作的对象与知道怎么执行一个操作的对象分离开
  • 命令类与其他任何别的类一样,可以修改和推广
  • 可以把命令对象聚合在一起,合成为合成命令
  • 可以很容易的加入新的命令类

缺点:可能会导致某些系统有过多的具体命令

应用场景:

  • 抽象出待执行的动作以参数化对象。Command模式是回调机制的一个面向对象的替代品
  • 在不同的时刻指定、排列和执行请求
  • 支持取消操作
  • 支持修改日志
  • 用构建在原语操作上的高层操作构建一个系统。Command模式提供了对事务进行建模的方法。Command有一个公共的接口,使得你可以用同一种方式调用所有的事务,同时使用该模式也易于添加新事务以扩展系统
<?php
/** 命令模式** 实例:复制粘贴功能*//*** @purpose 抽象命令接口* Interface Command*/
interface Command
{/*** @purpose 命令执行* @return mixed*/public function execute();
}/*** @purpose 宏命令接口* Interface MacroCommand*/
interface MacroCommand extends Command
{/*** @purpose 宏命令聚集管理方法,可以删除一个命令* @param Command $command* @return mixed*/public function remove(Command $command);/*** @purpose 宏命令聚集管理方法,可以添加一个命令* @param Command $command* @return mixed*/public function add(Command $command);
}// 复制命令
class CopyCommand implements Command
{private $receiver;public function __construct(Receiver $receiver){$this->receiver = $receiver;}public function execute(){$this->receiver->copy();}
}// 粘贴命令
class PasteCommand implements Command
{private $receiver;public function __construct(Receiver $receiver){$this->receiver = $receiver;}public function execute(){$this->receiver->paste();}
}// 命令接收者执行命令
class Receiver
{private $name;public function __construct($name){$this->name = $name;}public function copy(){echo "{$this->name}执行copy方法<br>";}public function paste(){echo "{$this->name}执行paste方法<br>";}
}// 命令请求者,用于发送命令
class Invoker
{private $command;public function __construct(Command $command){$this->command = $command;}public function action(){$this->command->execute();}
}// 实现宏命令
class DemoMacroCommand implements MacroCommand
{private $commands;public function __construct(){$this->commands = [];}public function add(Command $command){return array_push($this->commands, $command);}public function remove(Command $command){$index = array_search($command, $this->commands);if ($index === false || array_key_exists($index, $this->commands)) return false;unset($this->commands[$index]);return true;}public function execute(){if(!is_array($this->commands)) return false;foreach ($this->commands as $command) {// 通知改变$command->execute();}return true;}}// 客户端
$receiver = new Receiver("joke");
$copyCommand = new CopyCommand($receiver);
$pasteCommand = new PasteCommand($receiver);// 添加命令到宏命令中
$macroCommand = new DemoMacroCommand();
$macroCommand->add($copyCommand);
$macroCommand->add($pasteCommand);$invoker = new Invoker($macroCommand);
$invoker->action();

十二:迭代器模式

概念:迭代器模式是遍历集合的成熟模式,迭代器模式的关键是将遍历集合的任务交给一个叫做迭代器的对象,他在工作时遍历并选择序列中的对象,而客户端程序员不必知道或关心该集合序列底层的结构

说明

  • 迭代器模式(Iterator),又叫做游标(Cursor)模式,提供一种方法访问一个容器(Container)对象中各个元素,而又不需要暴露该对象的内部细节
  • 当你需要访问一个聚合对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑使用迭代器模式。另外,当需要对聚集有多种方式遍历时,可以考虑去使用迭代器模式。迭代器模式为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口

作用:

  • 我们想要向遍历数组那样,遍历对象,或是遍历一个容器
  • 迭代器模式可以隐藏遍历元素所需的操作

应用场景:

  • 访问一个聚合对象的内容而无需暴露他的内部表示
  • 支持对聚合对象的多种遍历
  • 为遍历不同的聚合结构提供一个统一的接口

角色:

  • Iterator(迭代器):迭代器定义访问和遍历元素的接口
  • ConcreteIterator(具体迭代器): 具体迭代器实现迭代器接口,对该聚合遍历时跟踪当前位置
  • Aggregate(聚合):聚合定义创建相应迭代器对象的接口
  • ConcreteIterator(具体聚合):具体聚合实现创建相应迭代器的接口,该操作返回ConcreteIterator的一个适当的实例
<?php
/** 迭代器模式*//*** 自定义迭代器实现系统定义迭代器接口* Class MyIterator*/
class MyIterator implements Iterator
{protected $data = [];protected $index;public function __construct($data){$this->data = $data;$this->index = 0;}/*** 返回当前元素* @return mixed|void*/public function current(){return $this->data[$this->index];}/*** 返回下一个*/public function next(){$this->index++;}/*** 返回key值* @return bool|float|int|string|void|null*/public function key(){return $this->index;}/*** 验证是否继续* @return bool|void*/public function valid(){return $this->index < count($this->data);}/*** 返回第一个元素*/public function rewind(){$this->index = 0;}
}$arr = ['张三', '李四', '王二', '麻子'];
$iterate = new MyIterator($arr);
foreach($iterate as $dynasty)
{echo "the ogj == {$dynasty}<br>";
}

如果有兴趣了解更多相关内容,欢迎来我的个人网站看看:瞳孔的个人空间


http://chatgpt.dhexx.cn/article/IN27Qlvq.shtml

相关文章

优雅代码的秘密,都藏在这6个设计原则中

优雅的代码&#xff0c;犹如亭亭玉立的美女&#xff0c;让人赏心悦目。而糟糕的代码&#xff0c;却犹如屎山&#xff0c;让人避而远之。 如何写出优雅的代码呢&#xff1f;那就要理解并熟悉应用这6个设计原则啦&#xff1a;开闭原则、单一职责原则、接口隔离原则 、迪米特法则…

低代码--低代码开发(LCDP)介绍

低代码开发&#xff08;LCDP&#xff09;介绍 1 介绍1.1 概述1.2 行业风向1.3 行业报告1.4 优点减少重复编程避免沟通隔阂提升效率 1.5 挑战完全抛弃代码的代价&#xff0c;就是平台能力与灵活性受限应用低代码平台阻力大技术局限老旧系统改造困难职业角色缺失应用者大多是技术…

网页设计个人主页代码

/ 01 / 前话 主题《周末の守候》采用Dreamweaver软件制作&#xff0c;主题包含了12页&#xff0c;页面能够相互跳转&#xff0c;运用了HTML5标签&#xff0c;DIVCSS布局&#xff0c;网站主题鲜明、内容丰富、健康、高雅且栏目设置合理&#xff0c;网站中页面色彩搭配合理&…

#低码系列#如何设计一个低代码平台?

低码系列文章 #低码系列#低代码来了&#xff0c;程序员会失业吗&#xff1f; 整体设计 用户群体 对于基础功能的实现&#xff0c;不需要开发人员介入。业务人员通过可视化页面&#xff0c;即可完成设计。从这个角度上看&#xff0c;低码平台面向的用户是业务人员、系统管理…

浅谈代码结构的设计

本文来自网易云社区 作者:陆秋炜 引言 :很久之前,在做中间件测试的时候,看到开发人员写的代码,有人的代码,看起来总是特别舒服,但有的开发代码,虽然逻辑上没有什么问题,但总给人感觉特别难受。后来成为了一位专职开发人员,渐渐发现,自己的代码也是属于“比较难受”…

领域驱动实践总结(基本理论总结与分析V+架构分析与代码设计+具体应用设计分析)

目录 领域驱动实践总结一:基本理论总结与分析 一、领域驱动设计两大设计:战略设计和战术设计 &#xff08;一&#xff09;战略设计 1.出发角度与目标 2.实现方式&#xff1a;事件风暴与模型确立&#xff08;用例分析、场景分析和用户旅程分析&#xff09; 3.用三步来划定…

如何设计一个低代码平台

编者按&#xff1a;近些年来&#xff0c;低代码发展火热&#xff0c;各种低代码平台如雨后春笋纷纷崛起&#xff0c;这些平台各定位不同&#xff0c;优劣不同&#xff0c;用户的选择空间很大。那么&#xff0c;如果用户想从零开始设计一个低代码平台&#xff0c;该如何做呢&…

QT纯代码设计UI界面Demo

目录 一、前言 二、界面 三、源码简析 四、Demo/源码 一、前言 UI的设计方法有几种&#xff1a; ①一种是使用Qt Designer&#xff0c;也就是可视化设计&#xff0c;这在小型项目中常见&#xff0c;优点就是可观简便&#xff1b; ②另一种就是纯代码设计UI&#xff0c;也…

Verilog RTL 代码设计新手上路

1. 做一个4选1的mux&#xff0c;并且进行波形仿真 和2选1的mux对比&#xff0c;观察资源消耗的变化&#xff1a; 实验分析&#xff1a;4选1的mux实际上就是在2选1的mux上进行拓展&#xff0c;选用2位的控制信号控制4位输入信号的选择输出 实验代码设计如下&#xff1a; …

代码设计流程

一、需求分析 1、需求分析的三层境界&#xff1a;实现者、分析者、引导者。 2、在需求中提取到合适的用例&#xff08;以抽卡系统为例&#xff09; 3、用例分析法 5W1H分析法 对上面的“抽卡”用例进行分析如下 5W内容What抽取卡牌Who玩家When游戏服务器开启期间Where抽卡…

代码设计的内功——代码设计原则

引言 好代码是设计出来的&#xff0c;也是重构出来的&#xff0c;更是不断迭代出来的。在我们接到需求&#xff0c;经过概要设计过后就要着手进行编码了。但是在实际编码之前&#xff0c;我们还需要进行领域分层设计以及代码结构设计。那么怎么样才能设计出来比较优雅的代码结构…

二维傅里叶变换频谱图的含义

二维傅里叶变换频谱图的含义 在一维傅里叶变换得到的频谱图中&#xff0c;每个点表示其对应的幅度频率与其坐标对应的简谐波。二位傅里叶变换的频谱图&#xff0c;简谐波的振幅由对应点处对应的亮度表示&#xff0c;每一个点表示的波形为其对应的横纵坐标所表示的简谐波的叠加…

二维傅里叶变换深度研究-图像与其频域关系

一&#xff1a;二维傅里叶变换的数学原理 1.2D离散傅里叶公式解释&#xff1a; 那么&#xff0c;其F(u,v) 本质就是&#xff1a; 后续说明时的”频域”均指的其傅里叶功率谱&#xff0c;后面为了演示方便&#xff0c;所有频域图均经过了fftshift移动到中心位置。 2.2D傅里叶频…

使用matlab对图像进行二维傅里叶变换

这学期选了《图像工程基础》这门课&#xff0c;课上老师留了一个作业&#xff1a;对图像进行二维傅里叶变换。 现在我使用matlab解决这个问题 1.实验基本指令 首先我试了一下matlab图像处理的基本指令 原图&#xff1a; 经过以下指令后 将图片导入matlab后&#xff0c;命名…

二维离散傅里叶变换

在学完一维的傅里叶变换后&#xff0c;紧接着就是二维的傅里叶变换了。直接上干货吧&#xff01;&#xff01;&#xff01; 途中会用到opencv读取与显示图片。 一. 公式 M表示图像的行数&#xff0c;N表示图像的列数。 经过欧拉公式可以得一下形式&#xff0c;这样就可以轻松…

Matlab图像的二维傅里叶变换频谱图特点研究

一、先放一些相关的结论&#xff1a; 1、傅里叶变换的幅值称为傅里叶谱或频谱。 2、F(u)的零值位置与“盒状”函数的宽度W成反比。 3、卷积定理&#xff1a;空间域两个函数的卷积的傅里叶变换等于两个函数的傅里叶变换在频率域中的乘积。f(t)*h(t) <> H(u)F(u) 4、采…

OpenCV学习——图像二值化处理及二维傅里叶变换

小古在本学期选修了《计算机视觉原理与应用》&#xff0c;最近有一份作业 —— 利用matlab或者OpenCV对图像进行一些处理&#xff0c;由于完全没有接触过matlab和OpenCV,但是学习了一些python语言&#xff0c;所以便利用opencv-python来完成作业。 1 图像二值化处理 1.1 图像…

二维傅里叶变换是怎么进行的?

1.首先回顾一下一维FT 通俗来讲&#xff0c;一维傅里叶变换是将一个一维的信号分解成若干个三角波。 对于一个三角波而言&#xff0c;需要三个参数来确定它&#xff1a;频率,幅度 A &#xff0c;相位。因此在频域中&#xff0c;一维坐标代表频率&#xff0c;而每个坐标对应的…

二维傅里叶变换需知

from: https://blog.csdn.net/wenhao_ir/article/details/51037744 代码如下&#xff0c;这个代码是实现灰度图像作二维傅里叶变换后的非线性变换哈~ clear all; Iimread(coins.png); Ffft2((im2double(I))); Ffftshift(F); Fabs(F); Tlog(F1); subplot(1,2,1); imshow(F,[]…

傅里叶级数、一维傅里叶变换到二维傅里叶变换数理推导

傅里叶级数、一维傅里叶变换到二维傅里叶变换数理推导 参考资料&#xff1a; 如何理解傅里叶级数公式 二重傅里叶级数 从傅里叶级数到傅里叶变换 高维傅里叶变换的推导 连续傅里叶变换和离散傅里叶变换 二维离散傅里叶变换 IDL实现傅里叶变换 想要用傅里叶变换的思维处理一个…