当前位置: 首页 > news >正文

技术攻略】php设计模式(一):简介及创建型模式

我们分三篇文章来总结一下设计模式在PHP中的应用,这是第一篇创建型模式。
一、设计模式简介

首先我们来认识一下什么是设计模式:

设计模式是一套被反复使用、容易被他人理解的、可靠的代码设计经验的总结。

设计模式不是Java的专利,我们用面向对象的方法在PHP里也能很好的使用23种设计模式。

那么我们常说的架构、框架和设计模式有什么关系呢?

架构是一套体系结构,是项目的整体解决方案;框架是可供复用的半成品软件,是具体程序代码。架构一般会涉及到采用什么样的框架来加速和优化某部分问题的解决,而好的框架代码里合理使用了很多设计模式。

二、提炼设计模式的几个原则:

开闭原则:模块应对扩展开放,而对修改关闭。

里氏代换原则:如果调用的是父类的话,那么换成子类也完全可以运行。

依赖倒转原则:抽象不依赖细节,面向接口编程,传递参数尽量引用层次高的类。

接口隔离原则:每一个接口只负责一种角色。

合成/聚合复用原则:要尽量使用合成/聚合,不要滥用继承。

三、设计模式的功用?

设计模式能解决:

替换杂乱无章的代码,形成良好的代码风格

代码易读,工程师们都能很容易理解

增加新功能时不用修改接口,可扩展性强

稳定性好,一般不会出现未知的问题

设计模式不能解决:

设计模式是用来组织你的代码的模板,而不是直接调用的库;

设计模式并非最高效,但是代码的可读性和可维护性更重要;

不要一味追求并套用设计模式,重构时多考虑;

四、设计模式分类

1、创建型模式:

单例模式、工厂模式(简单工厂、工厂方法、抽象工厂)、创建者模式、原型模式。

2、结构型模式:

适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式。

3、行为型模式:

模版方法模式、命令模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式、访问者模式。

五、创建型设计模式

1、单例模式

目的:保证一个类仅有一个实例,并提供一个访问它的全局访问点。

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

/**

  • 单例模式
    *

*/
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、工厂模式

实现:定义一个用于创建对象的接口,让子类决定实例化哪一个类。

应用场景:众多子类并且会扩充、创建方法比较复杂。

/**
*

  • 工厂模式
    */

//抽象产品
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、创建者模式

在创建者模式中,客户端不再负责对象的创建与组装,而是把这个对象创建的责任交给其具体的创建者类,把组装的责任交给组装类,客户端支付对对象的调用,从而明确了各个类的职责。

应用场景:创建非常复杂,分步骤组装起来。

/**

  • 优才网公开课示例代码
    *

  • 创建者模式
    */

//购物车
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:%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;
}
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();
?>

可以看出,使用创建者模式对内部数据复杂的对象封装数据组装过程后,对外接口就会非常简单和规范,增加修改新数据项也不会对外部造成任何影响。

原型模式

用原型实例指定创建对象的种类,并且通过拷贝这个原型来创建新的对象。

应用场景:类的资源非常多、性能和安全要求,一般和工厂方法结合使用。

/**

  • 原型模式

//声明一个克隆自身的接口
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,%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();
?>

这里可以看到,如果类的成员变量非常多,如果由外部创建多个新对象再一个个赋值,则效率不高代码冗余也容易出错,通过原型拷贝复制自身再进行微小修改就是另一个新对象了。

设计模式的第一部分,创建型模式就总结完了。下面还有两部分结构型设计模式和行为型设计模式下次继续分享。

相关文章:

  • 问题解答
  • 华为机试题-字符串分隔
  • 微软轻量级“代码生成器”—Repository Factory使用(下)
  • 《关于组织申报2017年度高新技术企业的通知》
  • 狼的故事11:以牙还牙
  • sql server 排序规则
  • windows server上联系人和用户的区别
  • tomcat 7下spring 4.x mvc集成websocket以及sockjs完全参考指南(含nginx/https支持)
  • 解决inline-block左右不能对齐
  • 使用ActiveReports for .net 进行报表开发(九)--End User (转)
  • test silverlight
  • 2017软考信息系统项目管理师软考热点
  • 本周链接列表(2007.12.10-2007.12.16)
  • burpsuit辅助sqlmap***
  • Python打包上传
  • [deviceone开发]-do_Webview的基本示例
  • 【css3】浏览器内核及其兼容性
  • 230. Kth Smallest Element in a BST
  • Angularjs之国际化
  • HTTP请求重发
  • JavaScript对象详解
  • Java知识点总结(JavaIO-打印流)
  • LeetCode18.四数之和 JavaScript
  • MySQL-事务管理(基础)
  • Promise面试题,控制异步流程
  • Redis在Web项目中的应用与实践
  • 复杂数据处理
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 微信小程序:实现悬浮返回和分享按钮
  • 赢得Docker挑战最佳实践
  • 《天龙八部3D》Unity技术方案揭秘
  • 3月27日云栖精选夜读 | 从 “城市大脑”实践,瞭望未来城市源起 ...
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • 资深实践篇 | 基于Kubernetes 1.61的Kubernetes Scheduler 调度详解 ...
  • ​secrets --- 生成管理密码的安全随机数​
  • #!/usr/bin/python与#!/usr/bin/env python的区别
  • %3cscript放入php,跟bWAPP学WEB安全(PHP代码)--XSS跨站脚本攻击
  • (2)MFC+openGL单文档框架glFrame
  • (Demo分享)利用原生JavaScript-随机数-实现做一个烟花案例
  • (非本人原创)史记·柴静列传(r4笔记第65天)
  • (非本人原创)我们工作到底是为了什么?​——HP大中华区总裁孙振耀退休感言(r4笔记第60天)...
  • (附源码)node.js知识分享网站 毕业设计 202038
  • (附源码)基于ssm的模具配件账单管理系统 毕业设计 081848
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (新)网络工程师考点串讲与真题详解
  • .NET牛人应该知道些什么(2):中级.NET开发人员
  • .NET正则基础之——正则委托
  • @Validated和@Valid校验参数区别
  • [ C++ ] STL_vector -- 迭代器失效问题
  • [ web基础篇 ] Burp Suite 爆破 Basic 认证密码
  • []T 还是 []*T, 这是一个问题
  • [ABC294Ex] K-Coloring
  • [AIGC] Java 和 Kotlin 的区别
  • [C#小技巧]如何捕捉上升沿和下降沿
  • [C++]STL之map