我们分三篇文章来总结一下设计模式在PHP中的应用,这是第一篇创建型模式。
一、设计模式简介
首先我们来认识一下什么是设计模式:
设计模式是一套被反复使用、容易被他人理解的、可靠的代码设计经验的总结。
设计模式不是Java的专利,我们用面向对象的方法在PHP里也能很好的使用23种设计模式。
那么我们常说的架构、框架和设计模式有什么关系呢?
架构是一套体系结构,是项目的整体解决方案;框架是可供复用的半成品软件,是具体程序代码。架构一般会涉及到采用什么样的框架来加速和优化某部分问题的解决,而好的框架代码里合理使用了很多设计模式。
二、提炼设计模式的几个原则:
开闭原则:模块应对扩展开放,而对修改关闭。
里氏代换原则:如果调用的是父类的话,那么换成子类也完全可以运行。
依赖倒转原则:抽象不依赖细节,面向接口编程,传递参数尽量引用层次高的类。
接口隔离原则:每一个接口只负责一种角色。
合成/聚合复用原则:要尽量使用合成/聚合,不要滥用继承。
三、设计模式的功用?
设计模式能解决:
替换杂乱无章的代码,形成良好的代码风格
代码易读,工程师们都能很容易理解
增加新功能时不用修改接口,可扩展性强
稳定性好,一般不会出现未知的问题
设计模式不能解决:
设计模式是用来组织你的代码的模板,而不是直接调用的库;
设计模式并非最高效,但是代码的可读性和可维护性更重要;
不要一味追求并套用设计模式,重构时多考虑;
四、设计模式分类
1、创建型模式:
单例模式、工厂模式(简单工厂、工厂方法、抽象工厂)、创建者模式、原型模式。
2、结构型模式:
适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。
3、行为型模式:
模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。
五、创建型设计模式
1、单例模式
目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点。
应用场景:数据库连接、缓存操作、分布式存储。
<?php
/**
* 优才网公开课示例代码
*
* 单例模式
*
*@author 优才网全栈工程师教研组
*@see http://www.ucai.cn
*/
class DbConn
{
privatestatic $_instance = null;
protectedstatic $_counter = 0;
protected$_db;
//私有化构造函数,不允许外部创建实例
privatefunction __construct()
{
self::$_counter+= 1;
}
publicfunction getInstance()
{
if(self::$_instance == null)
{
self::$_instance= new DbConn();
}
returnself::$_instance;
}
publicfunction 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
/**
* 优才网公开课示例代码
*
* 工厂模式
*
*@author 优才网全栈工程师教研组
*@see http://www.ucai.cn
*/
//抽象产品
interface Person {
public function getName();
}
//具体产品实现
class Teacher implements Person {
function getName() {
return "老师\n";
}
}
class Student implements Person {
function getName() {
return "学生\n";
}
}
//简单工厂
class SimpleFactory {
publicstatic function getPerson($type) {
$person= null;
if($type == 'teacher') {
$person= new Teacher();
}elseif ($type == 'student') {
$person= new Student();
}
return$person;
}
}
//简单工厂调用
class SimpleClient {
functionmain() {
//如果不用工厂模式,则需要提前指定具体类
//$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 {
functiongetYear();
}
//另一条产品线的具体产品
class Grade1 implements Grade {
publicfunction getYear() {
return'2003级';
}
}
class Grade2 implements Grade {
publicfunction getYear() {
return'2004级';
}
}
//抽象工厂
interface AbstractFactory {
functiongetPerson();
functiongetGrade();
}
//具体工厂可以产生每个产品线的产品
class Grade1TeacherFactory implementsAbstractFactory {
publicfunction getPerson() {
returnnew Teacher();
}
publicfunction getGrade() {
returnnew Grade1();
}
}
class Grade1StudentFactory implementsAbstractFactory {
publicfunction getPerson() {
returnnew Student();
}
publicfunction getGrade() {
returnnew Grade1();
}
}
class Grade2TeacherFactory implementsAbstractFactory {
publicfunction getPerson() {
returnnew Teacher();
}
publicfunction getGrade() {
returnnew Grade2();
}
}
//抽象工厂调用
class FactoryClient {
functionprintInfo($factory) {
echo$factory->getGrade()->getYear().$factory->getPerson()->getName();
}
functionmain() {
$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
/**
* 优才网公开课示例代码
*
* 创建者模式
*
*@author 优才网全栈工程师教研组
*@see http://www.ucai.cn
*/
//购物车
class ShoppingCart {
//选中的商品
private $_goods = array();
//使用的优惠券
private $_tickets = array();
publicfunction addGoods($goods) {
$this->_goods[]= $goods;
}
public function addTicket($ticket) {
$this->_tickets[] = $ticket;
}
public function printInfo() {
printf("goods:%s,tickets:%s\n", 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;
}
functionbuild($data) {
foreach($data['goods'] as $goods) {
$this->_card->addGoods($goods);
}
foreach($data['tickets'] as $ticket) {
$this->_card->addTicket($ticket);
}
}
functiongetCrad() {
return$this->_card;
}
}
$cart = new ShoppingCart();
$builder = new CardBuilder($cart);
$builder->build($data);
echo "after builder:\n";
$cart->printInfo();
?>
可以看出,使用创建者模式对内部数据复杂的对象封装数据组装过程后,对外接口就会非常简单和规范,增加修改新数据项也不会对外部造成任何影响。
原型模式
用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。
应用场景:类的资源非常多、性能和安全要求,一般和工厂方法结合使用。
~~~
<?php
/**
* 优才网公开课示例代码
*
* 原型模式
*
*@author 优才网全栈工程师教研组
*@see http://www.ucai.cn
*/
//声明一个克隆自身的接口
~~~
interface Prototype {
function copy();
}
~~~
~~~
//产品要实现克隆自身的操作
~~~
class Student implements Prototype {
//简单起见,这里没有使用getset
public $school;
public $major;
public$name;
publicfunction __construct($school, $major, $name) {
$this->school= $school;
$this->major= $major;
$this->name= $name;
}
publicfunction printInfo() {
printf("%s,%s,%s\n",$this->school, $this->major, $this->name);
}
public function copy() {
return clone $this;
}
}
~~~
$stu1 = new Student('清华大学', '计算机', '张三');
$stu1->printInfo();
$stu2 = $stu1->copy();
$stu2->name = '李四';
$stu2->printInfo();
?>
这里可以看到,如果类的成员变量非常多,如果由外部创建多个新对象再一个个赋值,则效率不高代码冗余也容易出错,通过原型拷贝复制自身再进行微小修改就是另一个新对象了。
设计模式的第一部分,创建型模式就总结完了。下面还有两部分结构型设计模式和行为型设计模式下次继续分享。
- PHP技术文章
- PHP中session和cookie的区别
- php设计模式(一):简介及创建型模式
- php设计模式结构型模式
- Php设计模式(三):行为型模式
- 十款最出色的 PHP 安全开发库中文详细介绍
- 12个提问频率最高的PHP面试题
- PHP 语言需要避免的 10 大误区
- PHP 死锁问题分析
- 致PHP路上的“年轻人”
- PHP网站常见安全漏洞,及相应防范措施总结
- 各开源框架使用与设计总结(一)
- 数据库的本质、概念及其应用实践(二)
- PHP导出MySQL数据到Excel文件(fputcsv)
- PHP中14种排序算法评测
- 深入理解PHP原理之--echo的实现
- PHP性能分析相关的函数
- PHP 性能分析10则
- 10 位顶级 PHP 大师的开发原则
- 30条爆笑的程序员梗 PHP是最好的语言
- PHP底层的运行机制与原理
- PHP 性能分析与实验——性能的宏观分析
- PHP7 性能翻倍关键大揭露
- 鸟哥:写在PHP7发布之际一些话
- PHP与MySQL通讯那点事
- Php session内部执行流程的再次剖析
- 关于 PHP 中的 Class 的几点个人看法
- PHP Socket 编程过程详解
- PHP过往及现在及变革
- PHP吉祥物大象的由来
- PHP生成静态页面的方法
- 吊炸天的 PHP 7 ,你值得拥有!
- PHP开发中文件操作疑难问答
- MongoDB PHP Driver的连接处理解析
- PHP 杂谈《重构-改善既有代码的设计》之二 对象
- 在php中判断一个请求是ajax请求还是普通请求的方法
- 使用HAProxy、PHP、Redis和MySQL支撑10亿请求每周架构细节
- HTML、HTML5、XHTML、CSS、SQL、JavaScript、PHP、Web Services 是什么?
- 重构-改善既有代码的设计
- PHP场景中getshell防御思路分享
- 移动互联时代,你看看除了PHP你还会些什么
- 安卓系统上搭建本地php服务器环境
- PHP中常见的缓存技术!
- PHP里10个鲜为人知但却非常有用的函数
- 成为一名PHP专家其实并不难
- PHP 命令行?是的,您可以!
- PHP开发提高效率技巧
- PHP八大安全函数解析
- PHP实现四种基本排序算法
- PHP开发中的中文编码问题
- php.get.post
- php发送get、post请求的6种方法简明总结
- 中高级PHP开发者应该掌握哪些技术?
- 前端开发
- web前端知识体系大全
- 前端工程与性能优化(下)
- 前端工程与性能优化(上)
- 2016 年技术发展方向
- Web应用检查清单
- 如何成为一名优秀的web前端工程师
- 前端组件化开发实践
- 移动端H5页面高清多屏适配方案
- 2015前端框架何去何从
- 从前端看“百度迁徙”的技术实现(一)
- 从前端看“百度迁徙”的技术实现(二)
- 前端路上的旅行
- 大公司里怎样开发和部署前端代码?
- 5个经典的前端面试问题
- 前端工程师新手必读
- 手机淘宝前端的图片相关工作流程梳理
- 一个自动化的前端项目实现(附源码)
- 前端代码异常日志收集与监控
- 15年双11手淘前端技术总结 - H5性能最佳实践
- 深入理解javascript原型和闭包系列
- 一切都是对象
- 函数和对象的关系
- prototype原型
- 隐式原型
- instanceof
- 继承
- 原型的灵活性
- 简述【执行上下文】上
- 简述【执行上下文】下
- this
- 执行上下文栈
- 简介【作用域】
- 【作用域】和【上下文环境】
- 从【自由变量】到【作用域链】
- 闭包
- 完结
- 补充:上下文环境和作用域的关系
- Linux私房菜