【PHP】thinkphp设计模式讲解
一、设计模式简介
首先我们来认识一下什么是设计模式:
设计模式是一套被反复使用、容易被他人理解的、可靠的代码设计经验的总结。
设计模式不是Java的专利,我们用面向对象的方法在PHP里也能很好的使用23种设计模式。
那么我们常说的架构、框架和设计模式有什么关系呢?
架构是一套体系结构,是项目的整体解决方案;框架是可供复用的半成品软件,是具体程序代码。架构一般会涉及到采用什么样的框架来加速和优化某部分问题的解决,而好的框架代码里合理使用了很多设计模式。
二、提炼设计模式的几个原则:
开闭原则:模块应对扩展开放,而对修改关闭。
里氏代换原则:如果调用的是父类的话,那么换成子类也完全可以运行。
依赖倒转原则:抽象不依赖细节,面向接口编程,传递参数尽量引用层次高的类。
接口隔离原则:每一个接口只负责一种角色。
合成/聚合复用原则:要尽量使用合成/聚合,不要滥用继承。
三、设计模式的功用?
设计模式能解决
替换杂乱无章的代码,形成良好的代码风格
代码易读,工程师们都能很容易理解
增加新功能时不用修改接口,可扩展性强
稳定性好,一般不会出现未知的问题
设计模式不能解决:
设计模式是用来组织你的代码的模板,而不是直接调用的库;
设计模式并非最高效,但是代码的可读性和可维护性更重要;
不要一味追求并套用设计模式,重构时多考虑;
四、设计模式分类
1、创建型模式:
单例模式、工厂模式(简单工厂、工厂方法、抽象工厂)、创建者模式、原型模式。
2、结构型模式:
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
3、行为型模式:
模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
创建型模式:
单例模式、工厂模式(简单工厂、工厂方法、抽象工厂)、创建者模式、原型模式。
1、单例模式
目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用场景:数据库连接、缓存操作、分布式存储。
<?php
/**
*
* 单例模式
*
*/
class DbConn
{
private static $_instance = null;
protected static $_counter = 0;
protected $_db;
//私有化构造函数,不允许外部创建实例
private function __construct()
{
self::$_counter += 1;
}
public function getInstance()
{
if (self::$_instance == null)
{
self::$_instance = new DbConn();
}
return self::$_instance;
}
public function connect()
{
echo "connected: ".(self::$_counter)."n";
return $this->_db;
}
}
/*
* 不使用单例模式时,删除构造函数的private后再测试,第二次调用构造函数后,_counter变成2
*/
// $conn = new DbConn();
// $conn->connect();
// $conn = new DbConn();
// $conn->connect();
//使用单例模式后不能直接new对象,必须调用getInstance获取
$conn = DbConn::getInstance();
$db = $conn->connect();
//第二次调用是同一个实例,_counter还是1
$conn = DbConn::getInstance();
$db = $conn->connect();
?>
特别说明:这里getInstance里有if判断然后再生成对象,在多线程语言里是会有并发问题的。例如java的解决方案有二个,给方法加上synchronized关键词变成同步,或者把_instanc的初始化提前放到类成员变量定义时,但是这2种方式php都不支持。不过因为php不支持多线程所以不需要考虑这个问题了。
2、工厂模式
实现:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
应用场景:众多子类并且会扩充、创建方法比较复杂。
<?php
/**
* 工厂模式
*
*/
//抽象产品
interface Person {
public function getName();
}
//具体产品实现
class Teacher implements Person {
function getName() {
return "老师n";
}
}
class Student implements Person {
function getName() {
return "学生n";
}
}
//简单工厂
class SimpleFactory {
public static function getPerson($type) {
$person = null;
if ($type == 'teacher') {
$person = new Teacher();
} elseif ($type == 'student') {
$person = new Student();
}
return $person;
}
}
//简单工厂调用
class SimpleClient {
function main() {
// 如果不用工厂模式,则需要提前指定具体类
// $person = new Teacher();
// echo $person->getName();
// $person = new Student();
// echo $person->getName();
// 用工厂模式,则不需要知道对象由什么类产生,交给工厂去决定
$person = SimpleFactory::getPerson('teacher');
echo $person->getName();
$person = SimpleFactory::getPerson('student');
echo $person->getName();
}
}
//工厂方法
interface CommFactory {
public function getPerson();
}
//具体工厂实现
class StudentFactory implements CommFactory {
function getPerson(){
return new Student();
}
}
class TeacherFactory implements CommFactory {
function getPerson() {
return new Teacher();
}
}
//工厂方法调用
class CommClient {
static function main() {
$factory = new TeacherFactory();
echo $factory->getPerson()->getName();
$factory = new StudentFactory();
echo $factory->getPerson()->getName();
}
}
//抽象工厂模式另一条产品线
interface Grade {
function getYear();
}
//另一条产品线的具体产品
class Grade1 implements Grade {
public function getYear() {
return '2003级';
}
}
class Grade2 implements Grade {
public function getYear() {
return '2004级';
}
}
//抽象工厂
interface AbstractFactory {
function getPerson();
function getGrade();
}
//具体工厂可以产生每个产品线的产品
class Grade1TeacherFactory implements AbstractFactory {
public function getPerson() {
return new Teacher();
}
public function getGrade() {
return new Grade1();
}
}
class Grade1StudentFactory implements AbstractFactory {
public function getPerson() {
return new Student();
}
public function getGrade() {
return new Grade1();
}
}
class Grade2TeacherFactory implements AbstractFactory {
public function getPerson() {
return new Teacher();
}
public function getGrade() {
return new Grade2();
}
}
//抽象工厂调用
class FactoryClient {
function printInfo($factory) {
echo $factory->getGrade()->getYear().$factory->getPerson()->getName();
}
function main() {
$client = new FactoryClient();
$factory = new Grade1TeacherFactory();
$client->printInfo($factory);
$factory = new Grade1StudentFactory();
$client->printInfo($factory);
$factory = new Grade2TeacherFactory();
$client->printInfo($factory);
}
}
//简单工厂
//SimpleClient::main();
//工厂方法
//CommClient::main();
//抽象工厂
FactoryClient::main();
?>
三种工厂的区别是,抽象工厂由多条产品线,而工厂方法只有一条产品线,是抽象工厂的简化。而工厂方法和简单工厂相对,大家初看起来好像工厂方法增加了许多代码但是实现的功能和简单工厂一样。但本质是,简单工厂并未严格遵循设计模式的开闭原则,当需要增加新产品时也需要修改工厂代码。但是工厂方法则严格遵守开闭原则,模式只负责抽象工厂接口,具体工厂交给客户去扩展。在分工时,核心工程师负责抽象工厂和抽象产品的定义,业务工程师负责具体工厂和具体产品的实现。只要抽象层设计的好,框架就是非常稳定的。
3、创建者模式
在创建者模式中,客户端不再负责对象的创建与组装,而是把这个对象创建的责任交给其具体的创建者类,把组装的责任交给组装类,客户端支付对对象的调用,从而明确了各个类的职责。
应用场景:创建非常复杂,分步骤组装起来。
<?php
/**
* 创建者模式
*/
//购物车
class ShoppingCart {
//选中的商品
private $_goods = array();
//使用的优惠券
private $_tickets = array();
public function addGoods($goods) {
$this->_goods[] = $goods;
}
public function addTicket($ticket) {
$this->_tickets[] = $ticket;
}
public function printInfo() {
printf("goods:%s, tickets:%sn", implode(',', $this->_goods), implode(',', $this->_tickets));
}
}
//假如我们要还原购物车的东西,比如用户关闭浏览器后再打开时会根据cookie还原
$data = array(
'goods' => array('衣服', '鞋子'),
'tickets' => array('减10'),
);
//如果不使用创建者模式,则需要业务类里一步步还原购物车
// $cart = new ShoppingCart();
// foreach ($data['goods'] as $goods) {
// $cart->addGoods($goods);
// }
// foreach ($data['tickets'] as $ticket) {
// $cart->addTicket($ticket);
// }
// $cart->printInfo();
// exit;
//我们提供创建者类来封装购物车的数据组装
class CardBuilder {
private $_card;
function __construct($card) {
$this->_card = $card;
}
function build($data) {
foreach ($data['goods'] as $goods) {
$this->_card->addGoods($goods);
}
foreach ($data['tickets'] as $ticket) {
$this->_card->addTicket($ticket);
}
}
function getCrad() {
return $this->_card;
}
}
$cart = new ShoppingCart();
$builder = new CardBuilder($cart);
$builder->build($data);
echo "after builder:n";
$cart->printInfo();
?>
可以看出,使用创建者模式对内部数据复杂的对象封装数据组装过程后,对外接口就会非常简单和规范,增加修改新数据项也不会对外部造成任何影响。
3、 原型模式
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
应用场景: 类的资源非常多、性能和安全要求,一般和工厂方法结合使用。
<?php
/**
* 原型模式
*/
//声明一个克隆自身的接口
interface Prototype {
function copy();
}
//产品要实现克隆自身的操作
class Student implements Prototype {
//简单起见,这里没有使用get set
public $school;
public $major;
public $name;
public function __construct($school, $major, $name) {
$this->school = $school;
$this->major = $major;
$this->name = $name;
}
public function printInfo() {
printf("%s,%s,%sn", $this->school, $this->major, $this->name);
}
public function copy() {
return clone $this;
}
}
$stu1 = new Student('清华大学', '计算机', '张三');
$stu1->printInfo();
$stu2 = $stu1->copy();
$stu2->name = '李四';
$stu2->printInfo();
?>
这里可以看到,如果类的成员变量非常多,如果由外部创建多个新对象再一个个赋值,则效率不高代码冗余也容易出错,通过原型拷贝复制自身再进行微小修改就是另一个新对象了。
创建型模式就总结完了。下面还有两部分结构型设计模式和行为型设计模式稍后继续。
一、什么是结构型模式?
结构型模式是解析类和对象的内部结构和外部组合,通过优化程序结构解决模块之间的耦合问题。
二、结构型模式的种类:
适配器模式
桥接模式
装饰模式
组合模式
外观模式
享元模式
代理模式
1、 适配器模式(Adapter)
将一个类的接口转换成客户希望的另一个接口,适配器模式使得原本的由于接口不兼容而不能一起工作的那些类可以一起工作。
应用场景:老代码接口不适应新的接口需求,或者代码很多很乱不便于继续修改,或者使用第三方类库。
代码实现
/**
* 适配器模式
*/
//老的代码
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('张三');
echo $olduser->getName()."n";
$newuser = new UserInfo($olduser);
echo $newuser->getUserName()."n";
注意点:这里的新接口使用了组合方式,UserInfo内部有一个成员变量保存老接口User对象,模块之间是松耦合的,这种结构其实就是组合模式。不要使用继承,虽然UserInfo继承User也能达到同样的目的,但是耦合度高,相互产生影响。
2、 桥接模式
将抽象部分与它的实现部分分离,使它们都可以独立变化
特点:独立存在,扩展性强
应用:需要不断更换调用对象却执行相同的调用方法,实现扩展功能
代码实现
/**
*
* 桥接模式
*
*/
abstract class Person {
abstract function getJob();
}
class Student extends Person {
public function getJob() {
return '学生';
}
}
class Teacher extends Person {
public function getJob() {
return '老师';
}
}
class BridgeObj {
protected $_person;
public function setPerson($person) {
$this->_person = $person;
}
public function getJob() {
return $this->_person->getJob();
}
}
$obj = new BridgeObj();
$obj->setPerson(new Student());
printf("本次桥接对象:%sn", $obj->getJob());
$obj->setPerson(new Teacher());
printf("本次桥接对象:%sn", $obj->getJob());
3、 装饰模式
动态地给一个对象添加额外的职责。在原有的基础上进行功能增强。
特点:用来增强原有对象功能,依附于原有对象。
应用:用于需要对原有对象增加功能而不是完全覆盖的时候
代码实现
/**
*
* 装饰模式
*
*/
//产品
abstract class Person {
abstract function getPermission();
}
//被装饰者
class User extends Person {
public function getPermission() {
return '公开文档';
}
}
//装饰类
class PermUser extends Person {
protected $_user;
protected $_special = '';
function __construct($user) {
$this->_user = $user;
}
public function getPermission() {
return $this->_user->getPermission() . $this->_special;
}
}
//装饰类产品
class JavaUser extends PermUser {
protected $_special = ' java程序';
}
class CPlusUser extends PermUser {
protected $_special = ' c++程序';
}
$user = new User();
printf("permission:%sn", $user->getPermission());
$user = new JavaUser($user);
printf("permission:%sn", $user->getPermission());
$user = new CPlusUser($user);
printf("permission:%sn", $user->getPermission());
大家想想装饰和继承的区别在哪?
如果是上面的例子,如果用继承,是CPlusUser继承JavaUser还是反过来呢?谁也不知道最终使用者需要哪一种。
在多层关系的情况下,装饰是和顺序无关并且随时增加装饰,而继承只能是特定的顺序,所以装饰模式会更加的灵活。
4、组合模式
将对象组合成树形结构表示“部分-整体”的层次结构。
特点:灵活性强
应用:对象的部分-整体的层次结构,模糊组合对象和简单对象处理问题
代码实现
/**
* 组合模式
*
*/
//继承模式
class UserBaseInfo {
private $name;
function __construct($name) {
$this->name = $name;
}
public function getName() {
return $this->name;
}
}
class User extends UserBaseInfo {
private $login = false;
public function setLogin($islogin) {
$this->login = $islogin;
}
public function isLogin() {
return $this->login;
}
}
$user = new User('张三');
$user->setLogin(true);
if ($user->isLogin()) {
echo $user->getName()."已经登录了n";
} else {
echo $user->getName()."还没有登录n";
}
//组合模式
class LoginInfo {
protected $user;
protected $login = false;
public function setLogin($user, $isLogin) {
$this->user = $user;
$this->login = $isLogin;
}
public function isLogin() {
return $this->login;
}
}
$user = new User('张三');
$login = new LoginInfo();
$login->setLogin($user, true);
if ($login->isLogin()) {
echo $user->getName()."已经登录了n";
} else {
echo $user->getName()."还没有登录n";
}
//部分可以更换,用继承则不行
class Admin {
protected $level;
function __construct($level) {
$this->level = $level;
}
function getLevel() {
return $this->level;
}
}
$admin = new Admin(1);
$login->setLogin($admin, true);
if ($login->isLogin()) {
printf("级别为 %d 的管理员已经登录了n", $admin->getLevel());
} else {
printf("级别为 %d 的管理员还没有登录n", $admin->getLevel());
}
上面的例子分别展示了使用继承和组合来处理新功能,在简单的情况下看似区别不大,但在项目后期越来越复杂的时候组合模式的优势就越来越明显了。
例如上面的登录信息,如果要增加登录次数、最后登录时间、登录ip等信息,登录本身就会变成一个比较复杂的对象。如果以后有新的需求比如好友信息、用户的访问信息等,再要继承的话,用户类就会变得非常庞大,难免各父类之间没有冲突的变量和方法,而外部访问用户类的众多方法也变得很费劲。采用组合模式后,一个类负责一个角色,功能区分非常明显,扩展方便。
5、 外观模式(门面模式)
为了系统中的一组接口提供一个一致的界面
特点:向上抽取,有共性
应用:内部接口众多,由统一的接口来调用
代码实现
class Operation {
public function testPlus() {
printf("plus: %sn", (1 + 2 == 3 ? 'true' : 'false'));
}
public function testMinus() {
printf("minus: %sn", (3 - 2 == 2 ? 'true' : 'false'));
}
public function testTimes() {
printf("times: %sn", (2 * 3 == 6 ? 'true' : 'false'));
}
}
class Tester {
protected $_operation;
function __construct() {
$this->_operation = new Operation();
}
public function testAll() {
$this->_operation->testPlus();
$this->_operation->testMinus();
$this->_operation->testTimes();
}
}
//测试用例,测试全部接口
$tester = new Tester();
$tester->testAll();
门面模式估计大家在实际代码中都已经使用到了,接口较多时把相似功能的接口封装成一个接口供外部调用,这就是门面模式。
6、 享元模式
运用共享技术有效地支持大量细粒度对象,采用一个共享来避免大量有相同内容对象的开销。这种开销中最直观的就是内存的损耗。
特点:高效性,共享性
应用:系统底层的设计。例如字符串的创建。如果两个字符串相同,则不会创建第二个字符串,而是第二个的引用直接指向第一个字符串。$str1=”abc”,$str2=”abc”.则内存存储中只会创建一个字符串“abc”而引用$str1.$str2都会指向它。
7、 代理模式
为其他对象提供一个代理来控制对这个对象的访问,就是给某一对象提供代理对象,并由代理对象控制具体对象的引用。能够协调调用者和被调用者,能够在一定程度上降低系统的耦合性。
特点:低耦合性,独立性好,安全性
应用:客户访问不到或者被访问者希望隐藏自己,所以通过代理来访问自己。
代码实现
//内部对象
class User {
public function getName() {
return '张三';
}
public function getType() {
return '付费用户';
}
}
//代理接口定义,例如开放平台
interface UserInterface {
function getName();
}
//代理对象
class UserProxy implements UserInterface {
protected $_user;
function __construct() {
$this->_user = new User();
}
public function getName() {
return $this->_user->getName();
}
}
//内部调用
$user = new User();
printf("user name:%sn", $user->getName());
printf("user type:%sn", $user->getType());
//外部调用
// $user = new UserProxy();
// printf("user name:%sn", $user->getName());
// printf("user type:%sn", $user->getType()); //不能访问,及时知道内部对象有这个方法
三、总结
1、代理模式、适配器模式、门面模式、装饰模式的区别
a、 相同之处:都封装一个内部对象,调用内部对象的方法
b、 不同之处:各自有各自的特性和应用场景,不能相互替代。所以用的时候要仔细分析用那种合适。
2、 关于模式的选用问题
模式的选用要根据实际的业务需求,通过对业务逻辑的仔细分析,再根据模式具有的特性和应用场景进行合理的选择和区分。大部分情况下业务的场景决定了哪种模式,而不是选择哪个模式去实现一个业务,少数情况几种模式确实都能解决问题,那主要就是考虑以后的扩展了。
到这里我们已经了解了7种结构型模式,下一篇我们继续给大家介绍设计模式的行为型模式,先预览一下行为型模式的种类吧:
u 模版方法模式
u 命令模式
u 迭代器模式
u 观察者模式
u 终结者模式
u 备忘录模式
u 解释器模式
u 状态模式
u 策略模式
u 职责链模式
u 访问者模式
一、什么是行为型模式?
行为型模式就是描述类和对象之间的通信和职责的。简而言之,就是类和对象扮演什么角色,还有怎么扮演这个角色的问题。
二、行为型模式的种类
大体上分为三个大类:常见模式、已知模式、深度模式
1、常见模式包括: 模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、状态模式、职责链模式、策略模式
2、 已知模式包括:备忘录模式
3、深度模式包括:解释器模式 访问者模式
下面来介绍常见模式
Ø 常见模式
1、模版方法模式(Template):
定义一个操作中的算法骨架,而将一些实现步骤延迟到子类当中实现。 就像一个豆浆机,不管放进去的是红豆还是黑豆,出来的都是豆浆。
好处:扩展性好,封装不变的代码,扩展可变的代码。
弊端:灵活性差,不能改变骨架部分。
应用场景:一类或一组具有共性的事物中。
代码实现
/**
*
* 模板方法模式 Template
*
*/
function output($string) {
echo $string . "n";
}
class Request {
public $token = '';
public function __construct() {
$this->token = '0c6b7289f5334ed2b697dd461eaf9812';
}
}
class Response {
public function render($content) {
output(sprintf('response-render: %s', $content));
}
public function redirect($uri) {
output(sprintf('response-redirect: %s', $uri));
}
public function json($data) {
output(sprintf('response-data: %s', json_encode($data)));
}
}
//父类,抽象类
abstract class Controller{
//封装了输入输出
protected $request;
protected $response;
//返回数据
protected $data = 'data';
public function __construct($request, $response){
$this->request = $request;
$this->response = $response;
}
//执行请求函数,定义总体算法(template method),final防止被复写(不允许子类改变总体算法)
public final function execute(){
$this->before();
if ($this->valid()){
$this->handleRequest();
}
$this->after();
}
//定义hook method before,做一些具体请求的前置处理
//非abstract方法,子类可以选择覆盖或不覆盖,默认什么都不做
protected function before(){
}
//定义hook method valid,做请求的数据验证
//非abstract方法,子类可以选择覆盖或不覆盖,默认返回验证通过
protected function valid(){
return true;
}
//定义hook method handleRequest,处理请求
//定义为abstract方法,子类必须实现或也声明为抽象方法(由子类的子类负责实现)
abstract function handleRequest();
//定义hook method after,做一些请求的后置处理
//非abstract方法,子类可以选择覆盖或不覆盖,默认直接输出数据
protected function after(){
$this->response->render($this->data);
}
}
//子类1,实现父类开放的具体算法
class User extends Controller{
//覆盖before方法,实现具体算法,这是一个处理用户数据操作的控制器
//因此,我们选择在before里面判断用户是否已经登录了,这里简单判断下session数据
function before(){
if (empty($_SESSION['auth'])){
//没登录就直接跳转了,不再执行后续的操作
$this->response->redirect("user/login.php");
}
}
//覆盖valid方法,这里我们验证用户提交数据中有没有带验证token
function valid(){
if (isset($this->request->token)){
return true;
}
return false;
}
//覆盖handleRequest方法,必选,以为父类中声明了abstract了
function handleRequest(){
//做具体处理,一般根据参数执行不同的业务逻辑
}
//这个类我们选择不覆盖after方法,使用默认处理方式
}
//子类2,实现父类开放的具体算法
class Post extends Controller{
//这个类我们选择不覆盖before方法,使用默认处理方式
//这个类我们选择不覆盖valid方法,使用默认处理方式
//覆盖handleRequest方法,必选,以为父类中声明了abstract了
function handleRequest(){
//做具体处理,一般根据参数执行不同的业务逻辑
$this->data = array('title' => 'ucai');
}
//覆盖after方法,使用json格式输出数据
function after(){
$this->response->json($this->data);
}
}
class Client {
public static function test(){
$request = new Request();
$response = new Response();
//最终调用
$user = new User($request, $response);
$user->execute();
//最终调用
$post = new Post($request, $response);
$post->execute();
}
}
Client::test();
2、命令模式(Command) :
行为请求者与行为实现者解耦。就像军队里的“敬礼”,不管是谁听到 这个命令都会做出标准的敬礼动作。
好处:便于添加和修改行为,便于聚合多个命令。
弊端:造成过多具体的命令类。
应用场景:对要操作的对象,进行的相同操作。
代码实现
/**
*
* 命令模式 Command
*
*/
function output($string) {
echo $string . "n";
}
class Document {
private $name = '';
public function __construct($name) {
$this->name = $name;
}
public function showText() {
output(sprintf("showText: %s", $this->name));
}
public function undo() {
output(sprintf("undo-showText: %s", $this->name));
}
}
class Graphics {
private $name = '';
public function __construct($name) {
$this->name = $name;
}
public function drawCircle() {
output(sprintf("drawCircle: %s", $this->name));
}
public function undo() {
output(sprintf("undo-drawCircle: %s", $this->name));
}
}
class Client {
public static function test() {
$document = new Document('A');
$graphics = new Graphics('B');
$document->showText();
$graphics->drawCircle();
$document->undo();
}
}
Client::test();
<?php
/*
*
* 命令模式 Command
*
*/
function output($string) {
echo $string . "n";
}
interface Command {
public function execute();
public function undo();
}
class Document implements Command {
private $name = '';
public function __construct($name) {
$this->name = $name;
}
public function execute() {
output(sprintf("showText: %s", $this->name));
}
public function undo() {
output(sprintf("undo-showText: %s", $this->name));
}
}
class Graphics implements Command {
private $name = '';
public function __construct($name) {
$this->name = $name;
}
public function execute() {
output(sprintf("drawCircle: %s", $this->name));
}
public function undo() {
output(sprintf("undo-drawCircle: %s", $this->name));
}
}
class Client {
public static function test() {
$array = array();
array_push($array, new Document('A'));
array_push($array, new Document('B'));
array_push($array, new Graphics('C'));
array_push($array, new Graphics('D'));
foreach ($array as $command) {
$command->execute();
}
$top = array_pop($array);
$top->undo();
}
}
Client::test();
<?php
/**
*
* 命令模式 Command
*
*/
function output($string) {
echo $string . "n";
}
interface Command {
public function execute();
public function undo();
}
class Document {
private $name = '';
public function __construct($name) {
$this->name = $name;
}
public function showText() {
output(sprintf("showText: %s", $this->name));
}
public function undo() {
output(sprintf("undo-showText: %s", $this->name));
}
}
class Graphics {
private $name = '';
public function __construct($name) {
$this->name = $name;
}
public function drawCircle() {
output(sprintf("drawCircle: %s", $this->name));
}
public function undo() {
output(sprintf("undo-drawCircle: %s", $this->name));
}
}
class DocumentCommand implements Command {
private $obj = '';
public function __construct(Document $document) {
$this->obj = $document;
}
public function execute() {
$this->obj->showText();
}
public function undo() {
$this->obj->undo();
}
}
class GraphicsCommand implements Command {
private $obj = '';
public function __construct(Graphics $graphics) {
$this->obj = $graphics;
}
public function execute() {
$this->obj->drawCircle();
}
public function undo() {
$this->obj->undo();
}
}
class Client {
public static function test() {
$array = array();
array_push($array, new DocumentCommand(new Document('A')));
array_push($array, new DocumentCommand(new Document('B')));
array_push($array, new GraphicsCommand(new Graphics('C')));
array_push($array, new GraphicsCommand(new Graphics('D')));
foreach ($array as $command) {
$command->execute();
}
$top = array_pop($array);
$top->undo();
}
}
Client::test();
3、迭代器模式(Iterator):
访问聚合对象内容而不暴露内部结构。就像一个双色球彩票开奖一 样,每次都是摇出七个球,不能能摇不是七个球的中奖号码组合。
好处:以不同方式遍历一个集合。
弊端:每次遍历都是整个集合,不能单独取出元素。
应用场景:需要操作集合里的全部元素。
代码实现
/**
*
* 迭代器模式 Iterator
*
*/
function output($string) {
echo $string . "n";
}
class RecordIterator implements Iterator{
private $position = 0;
//注意:被迭代对象属性是私有的
private $records = array();
public function __construct(Array $records) {
$this->position = 0;
$this->records = $records;
}
function rewind() {
$this->position = 0;
}
function current() {
return $this->records[$this->position];
}
function key() {
return $this->position;
}
function next() {
++$this->position;
}
function valid() {
return isset($this->records[$this->position]);
}
}
class PostListPager {
protected $record = array();
protected $total = 0;
protected $page = 0;
protected $size = 0;
public function __construct($category, $page, $size) {
$this->page = $page;
$this->size = $size;
// query db
$total = 28;
$this->total = $total;
$record = array(
0 => array('id' => '1'),
1 => array('id' => '2'),
2 => array('id' => '3'),
3 => array('id' => '4'),
);
//
$this->record = $record;
}
public function getIterator() {
return new RecordIterator($this->record);
}
public function getMaxPage() {
$max = intval($this->total / $this->size);
return $max;
}
public function getPrevPage() {
return max($this->page - 1, 1);
}
public function getNextPage() {
return min($this->page + 1, $this->getMaxPage());
}
}
class Client {
public static function test(){
$pager = new PostListPager(1, 2, 4);
foreach ($pager->getIterator() as $key => $val) {
output(sprintf('Key[%d],Val[%s]', $key, json_encode($val)));
}
output(sprintf('MaxPage[%d]', $pager->getMaxPage()));
output(sprintf('Prev[%d]', $pager->getPrevPage()));
output(sprintf('Next[%d]', $pager->getNextPage()));
$iterator = $pager->getIterator();
while($iterator->valid()){
print_r($iterator->current());
$iterator->next();
}
$iterator->rewind();
}
}
Client::test();
4、观察者模式(Observer):
又叫发布订阅模式,当一个主体对象发生改变时,依赖它的多个观察 者对象都得到通知并自动更新响应。就像报社一样,今天发布的消息只要 是看这份报纸的人看到的都是同样的内容。如果发布另一份报纸,也是一 样的。
好处:广播式通信,范围大一呼百应,便于操作一个组团,“公有制”。
弊端:不能单独操作组团里的个体,不能实行按需分配。
应用场景:操作多个对象,并操作相同。
代码实现
/**
*
* 观察者模式 Observer
*
*/
function output($string) {
echo $string . "n";
}
//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
class Order{
//订单号
private $id = '';
//用户ID
private $userId = '';
//用户名
private $userName = '';
//价格
private $price = '';
//下单时间
private $orderTime = '';
//订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
public function __set($name, $value){
if (isset($this->$name)){
$this->$name = $value;
}
}
//获取订单属性
public function __get($name){
if (isset($this->$name)){
return $this->$name;
}
return "";
}
}
//假设的DB类,便于测试,实际会存入真实数据库
class FakeDB{
public function save($data){
return true;
}
}
class Client {
public static function test() {
//初始化一个订单数据
$order = new Order();
$order->id = 1001;
$order->userId = 9527;
$order->userName = "God";
$order->price = 20.0;
$order->orderTime = time();
//向数据库保存订单
$db = new FakeDB();
$result = $db->save($order);
if ($result){
//实际应用可能会写到日志文件中,这里直接输出
output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" );
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" );
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" );
}
}
}
Client::test();
<?php
/**
*
* 观察者模式 Observer
*
*/
function output($string) {
echo $string . "n";
}
//订单数据对象简单模拟,这个是实际需要被观察的对象(Subject),但是我们将其独立,然后
//通过构造方法传入到我们模式中的Subject中,这样使具体业务更加独立
class Order{
//订单号
private $id = '';
//用户ID
private $userId = '';
//用户名
private $userName = '';
//价格
private $price = '';
//下单时间
private $orderTime = '';
//订单数据填充简单模拟,实际应用中可能会读取用户表单输入并处理
public function __set($name, $value){
if (isset($this->$name)){
$this->$name = $value;
}
}
//获取订单属性
public function __get($name){
if (isset($this->$name)){
return $this->$name;
}
return "";
}
}
//被观察者, 负责维护观察者并在变化发生是通知观察者
class OrderSubject implements SplSubject {
private $observers;
private $order;
public function __construct(Order $order) {
$this->observers = new SplObjectStorage();
$this->order = $order;
}
//增加一个观察者
public function attach(SplObserver $observer) {
$this->observers->attach($observer);
}
//移除一个观察者
public function detach(SplObserver $observer) {
$this->observers->detach($observer);
}
//通知所有观察者
public function notify() {
foreach ($this->observers as $observer) {
$observer->update($this);
}
}
//返回主体对象的具体实现,供观察者调用
public function getOrder() {
return $this->order;
}
}
//记录业务数据日志 (ActionLogObserver),实际可能还要抽象一层以处理不同的Action(业务操作),这里省略
class ActionLogObserver implements SplObserver{
public function update(SplSubject $subject) {
$order = $subject->getOrder();
//实际应用可能会写到日志文件中,这里直接输出
output( "[OrderId:{$order->id}] [UseId:{$order->userId}] [Price:{$order->price}]" );
}
}
//给用户发送订单确认邮件 (UserMailObserver)
class UserMailObserver implements SplObserver{
public function update(SplSubject $subject) {
$order = $subject->getOrder();
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output( "Dear {$order->userName}: Your order {$order->id} was confirmed!" );
}
}
//给管理人员发订单处理通知邮件 (AdminMailObserver)
class AdminMailObserver implements SplObserver{
public function update(SplSubject $subject) {
$order = $subject->getOrder();
//实际应用会调用邮件发送服务如sendmail,这里直接输出
output( "Dear Manager: User {$order->userName}(ID:{$order->userId}) submitted a new order {$order->id}, please handle it ASAP!" );
}
}
//假设的DB类,便于测试,实际会存入真实数据库
class FakeDB{
public function save($data){
return true;
}
}
class Client {
public static function test() {
//初始化一个订单数据
$order = new Order();
$order->id = 1001;
$order->userId = 9527;
$order->userName = "God";
$order->price = 20.0;
$order->orderTime = time();
//绑定观察者
$subject = new OrderSubject($order);
$actionLogObserver = new ActionLogObserver();
$userMailObserver = new UserMailObserver();
$adminMailObserver = new AdminMailObserver();
$subject->attach($actionLogObserver);
$subject->attach($userMailObserver);
$subject->attach($adminMailObserver);
//向数据库保存订单
$db = new FakeDB();
$result = $db->save($order);
if ($result){
//通知观察者
$subject->notify();
}
}
}
Client::test();
5、中介者模式(Mediator):
用中介对象封装一系列的对象交互,中介使各对象不需要显式地相互引 用。
类似于邮局,邮寄者和收件者不用自己跑很远路,通过邮局就可以。
好处:简化了对象之间的关系,减少子类的生成。
弊端:中介对象可能变得非常复杂,系统难以维护。
应用场景:不需要显示地建立交互
代码实现
/**
*
* 中介者模式 Mediator
*
*/
function output($string) {
echo $string . "n";
}
abstract class Mediator { // 中介者角色
abstract public function send($message,$colleague);
}
abstract class Colleague { // 抽象对象
private $_mediator = null;
public function __construct($mediator) {
$this->_mediator = $mediator;
}
public function send($message) {
$this->_mediator->send($message,$this);
}
abstract public function notify($message);
}
class ConcreteMediator extends Mediator { // 具体中介者角色
private $_colleague1 = null;
private $_colleague2 = null;
public function send($message,$colleague) {
if($colleague == $this->_colleague1) {
$this->_colleague1->notify($message);
} else {
$this->_colleague2->notify($message);
}
}
public function set($colleague1,$colleague2) {
$this->_colleague1 = $colleague1;
$this->_colleague2 = $colleague2;
}
}
class Colleague1 extends Colleague { // 具体对象角色
public function notify($message) {
output(sprintf('Colleague-1: %s', $message));
}
}
class Colleague2 extends Colleague { // 具体对象角色
public function notify($message) {
output(sprintf('Colleague-2: %s', $message));
}
}
class Client {
public static function test(){
// client
$objMediator = new ConcreteMediator();
$objC1 = new Colleague1($objMediator);
$objC2 = new Colleague2($objMediator);
$objMediator->set($objC1,$objC2);
$objC1->send("to c2 from c1");
$objC2->send("to c1 from c2");
}
}
Client::test();
6、状态模式(State) :
对象在不同状态下表现出不同的行为。就像女朋友一样,高兴了牵你的 手,不高兴了遛狗。在两种状态下变现出不同的行为。
好处:避免if语句实用,方便增加新状态,封装了状态转换规则。
弊端:增加系统类和对象的数量。
应用场景:用于对象的不同功能的转换。
代码实现
/**
* 状态模式 State
*
*/
function output($string) {
echo $string . "n";
}
abstract class ILift {
//电梯的四个状态
const OPENING_STATE = 1; //门敞状态
const CLOSING_STATE = 2; //门闭状态
const RUNNING_STATE = 3; //运行状态
const STOPPING_STATE = 4; //停止状态;
//设置电梯的状态
public abstract function setState($state);
//首先电梯门开启动作
public abstract function open();
//电梯门有开启,那当然也就有关闭了
public abstract function close();
//电梯要能上能下,跑起来
public abstract function run();
//电梯还要能停下来
public abstract function stop();
}
/**
* 电梯的实现类
*/
class Lift extends ILift {
private $state;
public function setState($state) {
$this->state = $state;
}
//电梯门关闭
public function close() {
//电梯在什么状态下才能关闭
switch ($this->state) {
case ILift::OPENING_STATE: //如果是则可以关门,同时修改电梯状态
$this->setState(ILift::CLOSING_STATE);
break;
case ILift::CLOSING_STATE: //如果电梯就是关门状态,则什么都不做
//do nothing;
return ;
break;
case ILift::RUNNING_STATE: //如果是正在运行,门本来就是关闭的,也说明都不做
//do nothing;
return ;
break;
case ILift::STOPPING_STATE: //如果是停止状态,本也是关闭的,什么也不做
//do nothing;
return ;
break;
}
output('Lift colse');
}
//电梯门开启
public function open() {
//电梯在什么状态才能开启
switch($this->state){
case ILift::OPENING_STATE: //如果已经在门敞状态,则什么都不做
//do nothing;
return ;
break;
case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以开启
$this->setState(ILift::OPENING_STATE);
break;
case ILift::RUNNING_STATE: //正在运行状态,则不能开门,什么都不做
//do nothing;
return ;
break;
case ILift::STOPPING_STATE: //停止状态,淡然要开门了
$this->setState(ILift::OPENING_STATE);
break;
}
output('Lift open');
}
///电梯开始跑起来
public function run() {
switch($this->state){
case ILift::OPENING_STATE: //如果已经在门敞状态,则不你能运行,什么都不做
//do nothing;
return ;
break;
case ILift::CLOSING_STATE: //如是电梯时关闭状态,则可以运行
$this->setState(ILift::RUNNING_STATE);
break;
case ILift::RUNNING_STATE: //正在运行状态,则什么都不做
//do nothing;
return ;
break;
case ILift::STOPPING_STATE: //停止状态,可以运行
$this->setState(ILift::RUNNING_STATE);
}
output('Lift run');
}
//电梯停止
public function stop() {
switch($this->state){
case ILift::OPENING_STATE: //如果已经在门敞状态,那肯定要先停下来的,什么都不做
//do nothing;
return ;
break;
case ILift::CLOSING_STATE: //如是电梯时关闭状态,则当然可以停止了
$this->setState(ILift::CLOSING_STATE);
break;
case ILift::RUNNING_STATE: //正在运行状态,有运行当然那也就有停止了
$this->setState(ILift::CLOSING_STATE);
break;
case ILift::STOPPING_STATE: //停止状态,什么都不做
//do nothing;
return ;
break;
}
output('Lift stop');
}
}
class Client {
public static function test() {
$lift = new Lift();
//电梯的初始条件应该是停止状态
$lift->setState(ILift::STOPPING_STATE);
//首先是电梯门开启,人进去
$lift->open();
//然后电梯门关闭
$lift->close();
//再然后,电梯跑起来,向上或者向下
$lift->run();
//最后到达目的地,电梯挺下来
$lift->stop();
}
}
Client::test();
<?php
/**
*
* 状态模式 State
*
*/
function output($string) {
echo $string . "n";
}
/**
*
* 定义一个电梯的接口
*/
abstract class LiftState{
//定义一个环境角色,也就是封装状态的变换引起的功能变化
protected $_context;
public function setContext(Context $context){
$this->_context = $context;
}
//首先电梯门开启动作
public abstract function open();
//电梯门有开启,那当然也就有关闭了
public abstract function close();
//电梯要能上能下,跑起来
public abstract function run();
//电梯还要能停下来,停不下来那就扯淡了
public abstract function stop();
}
/**
* 环境类:定义客户感兴趣的接口。维护一个ConcreteState子类的实例,这个实例定义当前状态。
*/
class Context {
//定义出所有的电梯状态
static $openningState = null;
static $closeingState = null;
static $runningState = null;
static $stoppingState = null;
public function __construct() {
self::$openningState = new OpenningState();
self::$closeingState = new ClosingState();
self::$runningState = new RunningState();
self::$stoppingState = new StoppingState();
}
//定一个当前电梯状态
private $_liftState;
public function getLiftState() {
return $this->_liftState;
}
public function setLiftState($liftState) {
$this->_liftState = $liftState;
//把当前的环境通知到各个实现类中
$this->_liftState->setContext($this);
}
public function open(){
$this->_liftState->open();
}
public function close(){
$this->_liftState->close();
}
public function run(){
$this->_liftState->run();
}
public function stop(){
$this->_liftState->stop();
}
}
/**
* 在电梯门开启的状态下能做什么事情
*/
class OpenningState extends LiftState {
/**
* 开启当然可以关闭了,我就想测试一下电梯门开关功能
*
*/
public function close() {
//状态修改
$this->_context->setLiftState(Context::$closeingState);
//动作委托为CloseState来执行
$this->_context->getLiftState()->close();
}
//打开电梯门
public function open() {
output('lift open...');
}
//门开着电梯就想跑,这电梯,吓死你!
public function run() {
//do nothing;
}
//开门还不停止?
public function stop() {
//do nothing;
}
}
/**
* 电梯门关闭以后,电梯可以做哪些事情
*/
class ClosingState extends LiftState {
//电梯门关闭,这是关闭状态要实现的动作
public function close() {
output('lift close...');
}
//电梯门关了再打开,逗你玩呢,那这个允许呀
public function open() {
$this->_context->setLiftState(Context::$openningState); //置为门敞状态
$this->_context->getLiftState()->open();
}
//电梯门关了就跑,这是再正常不过了
public function run() {
$this->_context->setLiftState(Context::$runningState); //设置为运行状态;
$this->_context->getLiftState()->run();
}
//电梯门关着,我就不按楼层
public function stop() {
$this->_context->setLiftState(Context::$stoppingState); //设置为停止状态;
$this->_context->getLiftState()->stop();
}
}
/**
* 电梯在运行状态下能做哪些动作
*/
class RunningState extends LiftState {
//电梯门关闭?这是肯定了
public function close() {
//do nothing
}
//运行的时候开电梯门?你疯了!电梯不会给你开的
public function open() {
//do nothing
}
//这是在运行状态下要实现的方法
public function run() {
output('lift run...');
}
//这个事绝对是合理的,光运行不停止还有谁敢做这个电梯?!估计只有上帝了
public function stop() {
$this->_context->setLiftState(Context::$stoppingState); //环境设置为停止状态;
$this->_context->getLiftState()->stop();
}
}
/**
* 在停止状态下能做什么事情
*/
class StoppingState extends LiftState {
//停止状态关门?电梯门本来就是关着的!
public function close() {
//do nothing;
}
//停止状态,开门,那是要的!
public function open() {
$this->_context->setLiftState(Context::$openningState);
$this->_context->getLiftState()->open();
}
//停止状态再跑起来,正常的很
public function run() {
$this->_context->setLiftState(Context::$runningState);
$this->_context->getLiftState()->run();
}
//停止状态是怎么发生的呢?当然是停止方法执行了
public function stop() {
output('lift stop...');
}
}
/**
* 模拟电梯的动作
*/
class Client {
public static function test() {
$context = new Context();
$context->setLiftState(new ClosingState());
$context->open();
$context->close();
$context->run();
$context->stop();
}
}
Client::test();
7、职责链模式 (Chainof Responsibility):
多个对象有机会处理请求,为请求发送者和接收者解耦。就像银行里 的取款机,不管那一台都可以取到钱。
好处:简单化对象隐藏链结构,便于添加新职责节点。
弊端:请求可能没有接受者,或者被多个接收者调用,性能降低。
应用场景:处理多种请求。
代码实现
/**
* 职责链模式 Chain of Responsibility
*
*/
function output($string) {
echo $string . "n";
}
/**
* 加入在公司里,如果你的请假时间小于0.5天,那么只需要向leader打声招呼就OK了。
如果0.5<=请假天数<=3天,需要先leader打声招呼,然后部门经理签字。
如果3<请假天数,需要先leader打声招呼,然后到部门经理签字,最后总经经理确认签字,
如果请假天数超过10天,是任何人都不能批准的。
*/
/**
* 抽象处理者角色(Handler:Approver):定义一个处理请求的接口,和一个后继连接(可选)
*
*/
abstract class Handler
{
protected $_handler = null;
protected $_handlerName = null;
public function setSuccessor($handler)
{
$this->_handler = $handler;
}
protected function _success($request)
{
output(sprintf("%s's request was passed", $request->getName()));
return true;
}
abstract function handleRequest($request);
}
/**
* 具体处理者角色(ConcreteHandler:President):处理它所负责的请求,可以访问后继者,如果可以处理请求则处理,否则将该请求转给他的后继者。
*
*/
class ConcreteHandlerLeader extends Handler
{
function __construct($handlerName){
$this->_handlerName = $handlerName;
}
public function handleRequest($request)
{
if($request->getDay() < 0.5) {
output(sprintf('%s was told', $this->_handlerName)); // 已经跟leader招呼了
return $this->_success($request);
}
if ($this->_handler instanceof Handler) {
return $this->_handler->handleRequest($request);
}
}
}
/**
* Manager
*
*/
class ConcreteHandlerManager extends Handler
{
function __construct($handlerName){
$this->_handlerName = $handlerName;
}
public function handleRequest($request)
{
if(0.5 <= $request->getDay() && $request->getDay()<=3) {
output(sprintf('%s signed', $this->_handlerName)); // 部门经理签字
return $this->_success($request);
}
if ($this->_handler instanceof Handler) {
return $this->_handler->handleRequest($request);
}
}
}
class ConcreteHandlerGeneralManager extends Handler
{
function __construct($handlerName){
$this->_handlerName = $handlerName;
}
public function handleRequest($request)
{
if(3 < $request->getDay() && $request->getDay() < 10){
output(sprintf('%s signed', $this->_handlerName)); // 总经理签字
return $this->_success($request);
}
if ($this->_handler instanceof Handler) {
return $this->_handler->handleRequest($request);
} else {
output(sprintf('no one can approve request more than 10 days'));
}
}
}
/**
* 请假申请
*
*/
class Request
{
private $_name;
private $_day;
private $_reason;
function __construct($name= '', $day= 0, $reason = ''){
$this->_name = $name;
$this->_day = $day;
$this->_reason = $reason;
}
public function setName($name){
$this->_name = $name;
}
public function getName(){
return $this->_name;
}
public function setDay($day){
$this->_day = $day;
}
public function getDay(){
return $this->_day ;
}
public function setReason($reason ){
$this->_reason = $reason;
}
public function getReason( ){
return $this->_reason;
}
}
class Client {
public static function test(){
$leader = new ConcreteHandlerLeader('leader');
$manager = new ConcreteHandlerManager('manager');
$generalManager = new ConcreteHandlerGeneralManager('generalManager');
//请求实例
$request = new Request('ucai',4,'休息');
$leader->setSuccessor($manager);
$manager->setSuccessor($generalManager);
$result = $leader->handleRequest($request);
}
}
Client::test();
8、策略模式(Strategy):
定义一系列算法,把每一个算法封装起来,并且使它们可相互替换。 就像篮球队里的球员,场上的和场下休息的。教练可以让场上的下来,也 可以让场下的上阵。
好处:定义可重用的一系列算法和行为,并且消除了if else语句。
弊端:调用端必须知道所有策略类。
应用场景:用于对象间的替换。
代码实现
/**
* 策略模式 Strategy
*
*/
function output($string) {
echo $string . "n";
}
//策略基类接口
interface IStrategy {
public function OnTheWay();
}
class WalkStrategy implements IStrategy {
public function OnTheWay() {
output( '在路上步行');
}
}
class RideBickStrategy implements IStrategy {
public function OnTheWay() {
output( '在路上骑自行车');
}
}
class CarStrategy implements IStrategy {
public function OnTheWay() {
output( '在路上开车');
}
}
//选择策略类Context
class Context {
public function find($strategy) {
$strategy->OnTheWay();
}
}
class Client {
public static function test(){
$travel = new Context();
$travel->find(new WalkStrategy());
$travel->find(new RideBickStrategy());
$travel->find(new CarStrategy());
}
}
Client::test();
已知模式
1、备忘录模式(Memento):
保存对象在一时刻的状态。亲,还记得“老师来了记得叫我一下”的 同桌的他吗?
好处:给用户提供了一种可以恢复状态的机制。
弊端:消耗资源。
应用场景:用于需要保存的数据。
代码实现
/**
*
* 备忘录模式 Memento
*
*/
function output($string) {
echo $string . "n";
}
class Originator { // 发起人(Originator)角色
private $_state;
public function __construct() {
$this->_state = '';
}
public function createMemento() { // 创建备忘录
return new Memento($this->_state);
}
public function restoreMemento(Memento $memento) { // 将发起人恢复到备忘录对象记录的状态上
$this->_state = $memento->getState();
}
public function setState($state) { $this->_state = $state; }
public function getState() { return $this->_state; }
public function showState() {
output($this->_state);
}
}
class Memento { // 备忘录(Memento)角色
private $_state;
public function __construct($state) {
$this->setState($state);
}
public function getState() { return $this->_state; }
public function setState($state) { $this->_state = $state;}
}
class Caretaker { // 负责人(Caretaker)角色
private $_memento;
public function getMemento() { return $this->_memento; }
public function setMemento(Memento $memento) { $this->_memento = $memento; }
}
class Client {
public static function test(){
$org = new Originator();
$org->setState('open');
$org->showState();
/* 创建备忘 */
$memento = $org->createMemento();
/* 通过Caretaker保存此备忘 */
$caretaker = new Caretaker();
$caretaker->setMemento($memento);
/* 改变目标对象的状态 */
$org->setState('close');
$org->showState();
/* 还原操作 */
$org->restoreMemento($caretaker->getMemento());
$org->showState();
}
}
Client::test();
return;
try {
$db->beginTransaction();
$succ = $db->exec($sql_1);
if (!$succ) {
throw new Exception('SQL 1 update failed');
}
$succ = $db->exec($sql_2);
if (!$succ) {
throw new Exception('SQL 2 update failed');
}
$succ = $db->exec($sql_3);
if (!$succ) {
throw new Exception('SQL 3 update failed');
}
$db->commit();
} catch (Exception $exp) {
$db->rollBack();
}
深度模式
1、解释器模式(Interpreter):
定义语言的文法,并建立一个解释器解释该语言中的句子。每个用过 字典的童鞋都懂滴。
好处:可扩展性比较好,灵活性大
弊端:可能难以维护复杂的文法
应用场景:用于成对或者一对多的需求中
2、访问者模式(Visitor):
封装某些用于作用于某种数据结构中各元素的操作,可以在不改变数 据结构的前提下定义作用于这些元素的新操作。如银行排号机。
好处:将相关的事物集中到一个访问者对象中。
弊端:增加新数据结构很困难。
应用场景:排队,排号。
三、总结
本篇介绍了行为型模式,行为模式涉及到算法和对象职责间的分配,行为类
模式采用继承机制在类间分派行为,TemplateMethod和Interpreter是类行为
模式。行为对象模式使用对象复合而不是继承,一些行为对象模式描述了一组相
互对等的对象如何相互协作以完成其中任何一个对象都单独无法完成的任务,如
Mediator在对象间引入一个mediator对象提供了松耦合所需的间接性;
Chain of Responsibility提供了更松的耦合,它通过一条候选对象链隐式的向一
个对象发松请求,可以运行时刻决定哪些候选者参与到链中;Observer定义并保
持了对象间的依赖关系;其它的行为对象模式常将行为封装封装在一个对象中,
并将请求指派给它,Strategy模式将算法封装在对象中,这样可以方面的改变和
指定一个对象所使用的算法;Command模式将请求封装在对象中,这样它就可
以作为参数来传递,已可以存储在历史列表中或以其它方式使用;State模式封
装一个对象的状态,使得当这个对象的状态对象变化时,该对象可改变它的行
为;Visitor模式封装分布于多个类之间的行为;而Iterator模式则抽象了访问和
遍历一个集合中对象的方式。
猜你喜欢
- 【PHP】php将字符串拆分成数组有哪些方法
- 方法有:1、explode()函数,可以将字符串按指定的分隔符拆分成数组;2、str_split()函数,可以将字符串拆分成单个字符的数组;3、preg_split()函数,可以根据正则表达式将字符串拆分成数组;4、sscanf()函数,可以根据指定的格式解析字符串,并将解析结果存入数组中;5、字符串截取方法,通过使用字符串截取函数,可以将字符串按指定长度拆分成数组等。本教程操作系统:Windows10系统、PHP8.1.3版本、Dell G3电脑。在PHP中,将字符串拆分成数组有多种
- 【PHP】如何利用PHP实现RPC
- 一、什么是RPC什么是RPC RPC(Remote Procedure Call,远程过程调用)是一种计算机通信协议,用于使一个计算机程序可以调用另一个运行在不同计算机上的程序的过程或函数,并且无需了解底层网络细节。简而言之,RPC允许程序在不同的计算机或不同的进程之间通过网络进行通信,就好像调用本地函数一样。RPC的原理在RPC中,请求方(称为客户端)通过发送一条消息给目标方(称为服务器),请求执行某个远程过程(函数)。服务器接收到请求后,执行相应的过程,并将结果返回给
- 【PHP】TP5使用TaskScheduler来执行其他定时任务
- 在 ThinkPHP 5 中,你可以使用定时任务调度器(TaskScheduler)来执行其他定时任务。以下是一个示例代码,演示如何在一个定时任务中执行另一个定时任务:首先,你需要创建一个继承自 think\console\Command 的定时任务类,例如 TaskA:namespace app\command; use think\con
- 【PHP】php审计中有哪些危险函数
- php审计中危险函数有eval()函数、exec()函数、system()函数、passthru()函数、preg_replace()函数、unserialize()函数、include()和require()函数、file_get_contents()函数、unlink()函数、ysql_query()函数等等。详细介绍:1、eval()函数等等危险函数。本教程操作系统:windows10系统、PHP8.1.3版本、Dell G3电脑。在PHP审计过程中,有一些函数被认为是危险的,因
- 【PHP】MongoDB介绍
- MongoDBA是一个开源的、面向文档的NoSQLA数据库,它提供了高性能、可扩展的数据存储解决方案。MongoDB支持动态查询、高可用性、自动分片等功能,使其成为处理大规模数据集的优选数据库。MongoDB的核心特性包括:面向集合存储:数据被分组存储在集合中,集合类似于RDBMS中的表,但提供了更高的灵活性和无模式存储。无模式结构:存储在集合中的数据是无模式的,即每个文档可以有不同的字段和值类型,这为用户提供了极大的灵活性。高性能:MongoDB利用内存中的数据缓存来提高性能,并支持数据的
- 【PHP】PHP异步协程开发:加速数据存储与检索的效率
- 随着互联网应用规模和用户数量的不断增加,对于数据存储与检索的需求也愈发显著。传统的数据存储方式使用单线程阻塞 I/O 模型,无法满足海量数据的快速存储和检索需求。而采用异步协程开发模式,可以实现高效数据存储与检索,加速应用响应速度,提高系统的性能和稳定性。PHP 是一门广泛使用的编程语言,它在 Web 开发中有着广泛的应用场景。PHP 7 在语言内新增了异步协程支持,使得 PHP 开发人员可以更加方便的运用异步编程。下面将介绍 PHP 异步协程开发在数据存储和检索中的应用。一、基于 S
- 【PHP】json去除多余空格 php
- 随着互联网技术的不断发展,前后端交互的过程中通讯协议也逐渐从XML格式转变为JSON格式。在使用JSON格式时,发现有时候会出现多余的空格,这些空格不仅会增加数据传输的负担,而且在解析JSON数据时还会出现问题。因此,在实际开发中,我们需要把JSON数据中的多余空格去掉。本文将介绍如何通过PHP去除JSON数据中多余空格。一、多余空格产生的原因在理解如何去掉JSON中的多余空格之前,我们先来了解一下多余空格的产生原因。数据格式不规范一些程序员在编写代码时,可能会本着“小问题不是问题”的
- 【PHP】php哪些函数可以用来去幂方值
- hp可以用来去幂方值的函数有pow函数、双星号、exp函数、sqrt函数和log函数等。详细介绍:1、pow函数用于计算x的y次幂,x是底数,y是指数;2、双星号是幂运算符,用于计算一个数的幂;3、exp函数用于计算以e为底的x次幂,e是自然对数的底数,x是指数;4、sqrt函数用于计算一个数的平方根,x是计算平方根的数;5、log函数用于计算以指定底数为底的对数。本教程操作系统:windows10系统、PHP 8.1.3版本、DELL G3电脑。在PHP中,可以使用一些内置的函数来进行幂运算