PHP设计模式之 单例模式 工厂模式 实例讲解
单例模式又称为职责模式,它用来在程序中创建一个单一功能的访问点,通俗地说就是实例化出来的对象是唯一的。
所有的单例模式至少拥有以下三种公共元素:
1. 它们必须拥有一个构造函数,并且必须被标记为private
2. 它们拥有一个保存类的实例的静态成员变量
3. 它们拥有一个访问这个实例的公共的静态方法
单例类不能再其它类中直接实例化,只能被其自身实例化。它不会创建实例副本,而是会向单例类内部存储的实例返回一个引用。
单例模式的优缺点:
主要优点:
1、提供了对唯一实例的受控访问。
2、由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象单例模式无疑可以提高系统的性能。
3、允许可变数目的实例。
主要缺点:
1、由于单利模式中没有抽象层,因此单例类的扩展有很大的困难。
2、单例类的职责过重,在一定程度上违背了“单一职责原则”。
3、滥用单例将带来一些负面问题,如为了节省资源将数据库连接池对象设计为的单例类,可能会导致共享连接池对象的程序过多而出现连接池溢出;如果实例化的对象长时间不被利用,系统会认为是垃圾而被回收,这将导致对象状态的丢失。
例子:

1 class Single {
2 private $name;//声明一个私有的实例变量
3 private function __construct(){//声明私有构造方法为了防止外部代码使用new来创建对象。
4
5 }
6
7 static public $instance;//声明一个静态变量(保存在类中唯一的一个实例)
8 static public function getinstance(){//声明一个getinstance()静态方法,用于检测是否有实例对象
9 if(!self::$instance) self::$instance = new self();
10 return self::$instance;
11 }
12
13 public function setname($n){ $this->name = $n; }
14 public function getname(){ return $this->name; }
15 }
16
17
18 $oa = Single::getinstance();
19 $ob = Single::getinstance();
20 $oa->setname('hello world');
21 $ob->setname('good morning');
22 echo $oa->getname();//good morning
23 echo $ob->getname();//good morning

再来一个例子:
class Test{
private static $_instance;//保存类的静态成员变量
//定义一个私有的构造函数,确保单例类不能通过new关键字实例化,只能被其自身实例化
private final function __construct()
{
echo 'test_consttuct';
}
//定义克隆方法,确保单例类只能被克隆或者复制
private function __clone()
{
// TODO: Implement __clone() method.
}
//公共的静态方法
public static function getInstance()
{
//检测类是否被实例化
if(!(self::$_instance instanceof self))
{
self::$_instance = new test();
}
return self::$_instance;
}
}
test::getInstance();
简单工厂模式:
①抽象基类:类中定义抽象一些方法,用以在子类中实现
②继承自抽象基类的子类:实现基类中的抽象方法
③工厂类:用以实例化对象
看完文章再回头来看下这张图,效果会比较好


1 采用封装方式
2
3 <?php
4 class Calc{
5 /**
6 * 计算结果
7 *
8 * @param int|float $num1
9 * @param int|float $num2
10 * @param string $operator
11 * @return int|float
12 */
13 public function calculate($num1,$num2,$operator){
14 try {
15 $result=0;
16 switch ($operator){
17 case '+':
18 $result= $num1+$num2;
19 break;
20 case '-':
21 $result= $num1-$num2;
22 break;
23 case '*':
24 $result= $num1*$num2;
25 break;
26 case '/':
27 if ($num2==0) {
28 throw new Exception("除数不能为0");
29 }
30 $result= $num1/$num2;
31 break;
32 }
33 return $result;
34 }catch (Exception $e){
35 echo "您输入有误:".$e->getMessage();
36 }
37 }
38 }
39 $test=new Calc();
40 // echo $test->calculate(2,3,'+');//打印:5
41 echo $test->calculate(5,0,'/');//打印:您输入有误:除数不能为0
42 ?>

优点:以上代码使用了面向对象的封装特性,只要有了include这个类,其他页面就可以随便使用了
缺点:无法灵活的扩展和维护
比如:想要增加一个“求余”运算,需要在switch语句块中添加一个分支语句,代码需要做如下改动

1 添加分支语句
2
3 <?php
4 class Calc{
5 public function calculate($num1,$num2,$operator){
6 try {
7 $result=0;
8 switch ($operator){
9 //......省略......
10 case '%':
11 $result= $num1%$num2;
12 break;
13 //......省略......
14 }
15 }catch (Exception $e){
16 echo "您输入有误:".$e->getMessage();
17 }
18 }
19 }
20 ?>

代码分析:用以上方法实现给计算器添加新的功能运算有以下几个缺点
①需要改动原有的代码块,可能会在为了“添加新功能”而改动原有代码的时候,不小心将原有的代码改错了
②如果要添加的功能很多,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’,或者添加一些程序员专用的计算功能,比如:And, Or, Not, Xor,这样就需要在switch语句中添加N个分支语句。想象下,一个计算功能的函数如果有二三十个case分支语句,代码将超过一屏,不仅令代码的可读性大大降低,关键是,为了添加小功能,还得让其余不相关都参与解释,这令程序的执行效率大大降低
解决途径:采用OOP的继承和多态思想

1 简单工厂模式的初步实现
2 <?php
3 /**
4 * 操作类
5 * 因为包含有抽象方法,所以类必须声明为抽象类
6 */
7 abstract class Operation{
8 //抽象方法不能包含函数体
9 abstract public function getValue($num1,$num2);//强烈要求子类必须实现该功能函数
10 }
11 /**
12 * 加法类
13 */
14 class OperationAdd extends Operation {
15 public function getValue($num1,$num2){
16 return $num1+$num2;
17 }
18 }
19 /**
20 * 减法类
21 */
22 class OperationSub extends Operation {
23 public function getValue($num1,$num2){
24 return $num1-$num2;
25 }
26 }
27 /**
28 * 乘法类
29 */
30 class OperationMul extends Operation {
31 public function getValue($num1,$num2){
32 return $num1*$num2;
33 }
34 }
35 /**
36 * 除法类
37 */
38 class OperationDiv extends Operation {
39 public function getValue($num1,$num2){
40 try {
41 if ($num2==0){
42 throw new Exception("除数不能为0");
43 }else {
44 return $num1/$num2;
45 }
46 }catch (Exception $e){
47 echo "错误信息:".$e->getMessage();
48 }
49 }
50 }
51 ?>

这里采用了面向对象的继承特性,首先声明一个虚拟基类,在基类中指定子类务必实现的方法(getValue())
分析:通过采用面向对象的继承特性,我们可以很容易就能对原有程序进行扩展,比如:‘乘方’,‘开方’,‘对数’,‘三角函数’,‘统计’等等。

<?php
/**
* 求余类(remainder)
*
*/
class OperationRem extends Operation {
public function getValue($num1,$num2){
return $num1%$num12;
}
}
?>

我们只需要另外写一个类(该类继承虚拟基类),在类中完成相应的功能(比如:求乘方的运算),而且大大的降低了耦合度,方便日后的维护及扩展
现在还有一个问题未解决,就是如何让程序根据用户输入的操作符实例化相应的对象呢?
解决办法:使用一个单独的类来实现实例化的过程,这个类就是工厂
代码如下:

1 <?php
2 /**
3 * 工程类,主要用来创建对象
4 * 功能:根据输入的运算符号,工厂就能实例化出合适的对象
5 *
6 */
7 class Factory{
8 public static function createObj($operate){
9 switch ($operate){
10 case '+':
11 return new OperationAdd();
12 break;
13 case '-':
14 return new OperationSub();
15 break;
16 case '*':
17 return new OperationSub();
18 break;
19 case '/':
20 return new OperationDiv();
21 break;
22 }
23 }
24 }
25 $test=Factory::createObj('/');
26 $result=$test->getValue(23,0);
27 echo $result;
28 ?>

总结
区别
简单工厂模式(静态方法工厂模式) : 用来生产同一等级结构中的任意产品。(不能增加新的产品)
工厂模式 :用来生产同一等级结构中的固定产品。(支持增加任意产品)
抽象工厂 :用来生产不同产品种类的全部产品。(不能增加新的产品,支持增加产品种类)
适用范围
简单工厂模式:
工厂类负责创建的对象较少,操作时只需知道传入工厂类的参数即可,对于如何创建对象过程不用关心。
工厂方法模式:
满足以下条件时,可以考虑使用工厂模式方法
当一个类不知道它所必须创建对象的类时
一个类希望由子类来指定它所创建的对象时
当类将创建对象的职责委托给多个帮助子类中得某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时
抽象工厂模式:
满足以下条件时,可以考虑使用抽象工厂模式
系统不依赖于产品类实例如何被创建,组合和表达的细节。
系统的产品有多于一个的产品族,而系统只消费其中某一族的产品
同属于同一个产品族是在一起使用的。这一约束必须在系统的设计中体现出来。
系统提供一个产品类的库,所有产品以同样的接口出现,从而使客户端不依赖于实现。
以上几种,归根结底,都是将重复的东西提取出来,以方便整体解耦和复用,修改时方便。可根据具体需求而选择使用。
如果帮到了您,可以支持一下,谢谢您的支持!


PHP设计模式之 单例模式 工厂模式 实例讲解的更多相关文章
- PHP 工厂模式 实例讲解
简单工厂模式:①抽象基类:类中定义抽象一些方法,用以在子类中实现②继承自抽象基类的子类:实现基类中的抽象方法③工厂类:用以实例化对象 看完文章再回头来看下这张图,效果会比较好 1 采用封装方式 2 3 ...
- PHP中“简单工厂模式”实例讲解
原创文章,转载请注明出处:http://www.cnblogs.com/hongfei/archive/2012/07/07/2580776.html 简单工厂模式:①抽象基类:类中定义抽象一些方法, ...
- PHP中“简单工厂模式”实例讲解(转)
? 1 2 3 4 5 6 7 8 原创文章,转载请注明出处:http://www.cnblogs.com/hongfei/archive/2012/07/07/2580776.html 简单 ...
- C#设计模式(2)-简单工厂模式
引言 上一遍中介绍了设计模式中的单例模式-C#设计模式(1)-单例模式,本篇将介绍简单工厂模式,也是比较容易理解的一种模式: 简单工厂模式简介 什么是简单工厂模式? 定义一个工厂类,它可以根据参数的不 ...
- Java设计模式之二 ----- 工厂模式
在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模式,又 ...
- Java进阶篇设计模式之二 ----- 工厂模式
前言 在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模 ...
- C#设计模式(2)——简单工厂模式(转)
C#设计模式(2)——简单工厂模式 一.引言 这个系列也是自己对设计模式的一些学习笔记,希望对一些初学设计模式的人有所帮助的,在上一个专题中介绍了单例模式,在这个专题中继续为大家介绍一个比较容易理 ...
- Java设计模式之二工厂模式
在上一篇中我们学习了单例模式,介绍了单例模式创建的几种方法以及最优的方法.本篇则介绍设计模式中的工厂模式,主要分为简单工厂模式.工厂方法和抽象工厂模式. 简单工厂模式 简单工厂模式是属于创建型模式,又 ...
- java设计模式---三种工厂模式
工厂模式提供创建对象的接口. 工厂模式分为三类:简单工厂模式(Simple Factory), 工厂方法模式(Factory Method)和抽象工厂模式(Abstract Factory).GOF在 ...
随机推荐
- 【专题】概率期望DP
11.22:保持更新状态:主要发一些相关的题目和个人理解 (P.S.如果觉得简单,可以直接看后面的题目) upd 11.30 更完了 [NO.1] UVA12230 Crossing Rivers ...
- DB2数据常用指令
************************************************************** 这个只是个人平时总结,如果有更好的欢迎告诉我,一起学习一起成长 ***** ...
- c# ado.net eftity framework 返回多表查询结果
public static IQueryable GetWeiXinTuWenList() { using (var Model = new Model.WeiXinEntities()) { var ...
- 4星|《OKR实践指南》:老司机经验谈
OKR 实践指南:知乎任向晖.雷明灿作品 (知乎「一小时」系列) 作者所在的公司已经实施了OKR十个季度了.算是目前少有的OKR老司机.书中介绍的是作者的实践经验,在目前的OKR中文书中这本算是比较少 ...
- Matlab矩阵填充--Matlab interp2
Matlab interp2 为Matlab的矩阵填充函数, 填充关系: x=1:11; y=1:13; x1=1:0.1:12; y1=1:0.1:14; [x2,y2]=meshgrid(x1,y ...
- emlog通过pjax实现无刷新加载网页--完美解决cnzz统计和javascript失效问题
想要更详细了解pjax,需要查看官网 或者看本站文章:jQuery.pjax.js:使用AJAX和pushState无刷新加载网页(官网教程中文翻译) 效果看本站,音乐无刷新播放,代码高亮和复制js加 ...
- 关于MySQL Server影响ASP.NET网站使用的问题:未能加载文件或程序集MySql.Web.v20
最近开发的ASP.NET MVC 4网站,之前头头说如果遇到装过MySQL的机器就绕着走,还觉得奇怪 嘛,该来的迟早都会来 于是撞上了一台 启动网站再访问,总是出错,提示“未能加载文件或程序集”,名字 ...
- vue-router 懒加载
懒加载:也叫延迟加载,即在需要的时候进行加载,按需加载. 那vue 为什么需要懒加载呢? 使用 vue-cli构建的项目,在默认情况下,执行 npm run build 会将所有的 js代码打包为一 ...
- [Ynoi2015]盼君勿忘
题目大意: 给定一个序列,每次查询一个区间\([l,r]\)中所有子序列分别去重后的和\(\bmod p\)(每次询问模数不同). 解题思路: 在太阳西斜的这个世界里,置身天上之森.等这场战争结束之后 ...
- hdu 3001(三进制状压)
题目 解法 看到这道题,我们就会想到旅行商问题.但是这里每一个点可以经过最多两次,所以我们用三进制表示就好了. 代码 #include <iostream> #include <cs ...