- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章PHP设计模式之工厂模式(Factory)入门与应用详解由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
本文实例讲述了PHP设计模式之工厂模式(Factory)。分享给大家供大家参考,具体如下:
工厂模式的意思其实就是提供获取某个对象实例的一个接口,同时使调用代码避免确定实例化基类的步骤,实际上就是建立一个统一的类实例化的函数接口,完事统一调用,统一控制,它是PHP中常用的一种设计模式,一般会配合单例模式一起使用,来加载php类库中的类。来看一个简单的应用场景:
代码如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
<?php
//工厂模式 提供获取某个对象实例的一个接口,同时使调用代码避免确定实例化基类的步骤
//字符串类
class
String {
public
function
write() {}
}
//Json类
class
Json {
public
function
getJsonData() {}
}
//xml类
class
Xml {
public
function
buildXml() {}
}
//工厂类
class
Factory {
public
static
function
create(
$class
) {
return
new
$class
;
}
}
Factory::create(
"Json"
);
//获取Json对象
|
我们现在应该对于工厂模式有了一个大概的理解了,咱们接下来可以从字面上来理解一下.
工厂么,它就是生产产品的地方,它有原料,设备和产品,那么在PHP中,我们可以理解为,这个工厂模式可以通过一个工厂类(设备),来调用自身的静态方法(生产方式)来产生对象实例(产品),在上述实例中的Json类等,就相当于原料了.
理解了上面的一段话之后,我们就可以再深入的了解下这个工厂模式了.
我们来考虑以下场景,如果项目中,我们通过一个类创建对象,在快完成或者已经完成,要扩展功能的时候,发现原来的类的类名不是很合适或者发现类需要添加构造函数参数才能实现功能扩展,在这种情况下,大家就可以感受到“高内聚低耦合”的博大精深,我们可以尝试使用工厂模式解决这个问题.
还有就是最经典的数据库连接问题等等,都可以使用工厂模式来接觉问题,咱们也不废话,来看一下网上一个比较经典的案例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
interface
Transport{
public
function
go();
}
class
Bus
implements
Transport{
public
function
go(){
echo
"bus每一站都要停"
;
}
}
class
Car
implements
Transport{
public
function
go(){
echo
"car跑的飞快"
;
}
}
class
Bike
implements
Transport{
public
function
go(){
echo
"bike比较慢"
;
}
}
class
transFactory{
public
static
function
factory(
$transport
)
{
switch
(
$transport
) {
case
'bus'
:
return
new
Bus();
break
;
case
'car'
:
return
new
Car();
break
;
case
'bike'
:
return
new
Bike();
break
;
}
}
}
$transport
=transFactory::factory(
'car'
);
$transport
->go();
|
大家有了解过工厂模式应该都知道,工厂模式有三种,那就是一般工厂模式(静态工厂模式),工厂模式,还有就是抽象工厂模式,咱这里并未把所有的案例全部介绍完毕,不过嘞,咱们可以跟着网上的一个案例,来简单了解下工厂模式的三种变形的过程.
首先,我们来假设有个关于个人事务管理的项目,功能之一就是管理Appointment(预约)对象。我们的业务团队和A公司建立了关系,目前需要使用一个叫做BloggsCal格式来和他们交流预约相关的数据,但是业务部门提醒可能会有更多的数据格式,所以解码器可能会有多种,我们呢,为了避免在逻辑代码中使用过多的if else,可能就会需要使用工厂模式来将创造者和使用者分开.
那么,我们就需要两个类,一个类AppEncoder用于定义一个解码器,将A公司传来的数据解码;另外一个类CommsManager用于获取该解码器,就是调用AppEncoder类,用于与A公司进行通信。使用模式术语说,CommsManager就是创造者,AppEncoder就是产品(一个创造者、一个产品,将类的实例化和对象的使用分离开,这就是工厂模式的思想).
咱们先来通过简单工厂模式实现上述任务场景,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
//产品类
class
BloggsApptEncoder {
function
encode()
{
return
"Appointment data encoded in BloggsCal format\n"
;
}
}
//创造者类
class
CommsManager {
function
static
getBloggsApptEncoder()
{
return
new
BloggsApptEncoder();
}
}
|
大概明白了奥,好啦,现在又有新任务了,业务部门告诉我们需要新增一种数据格式MegCal,来完成数据交流,那么我们就需要新增对应的解码器类,然后直接在commsManager新增参数来标识需要实例化哪个解码器,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
class
CommsManager {
const
BLOGGS = 1;
const
MEGA = 2;
private
$mode
;
public
function
__construct(
$mode
)
{
$this
->mode =
$mode
;
}
function
getApptEncoder()
{
switch
(
$this
->mode) {
case
(self::MEGA):
return
new
MegaApptEncoder();
default
:
return
new
BloggsApptEncoder();
}
}
}
|
上述两个案例综合起来就是简单工厂模式了,它符合现实中的情况,而且客户端免除了直接创建产品对象的责任,而仅仅负责“消费”产品(正如暴发户所为).
接下来,我们从开闭原则上来分析下简单工厂模式,当新增一种数据格式的时候,只要符合抽象产品格式,那么只要通知工厂类知道就可以被使用了(即创建一个新的解码器类,继承抽象解码器ApptEncoder),那么对于产品部分来说,它是符合开闭原则的——对扩展开放、对修改关闭,但是对于工厂类不太理想,因为每增加一各格式,都要在工厂类中增加相应的商业逻辑和判断逻辑,这显自然是违背开闭原则的.
然而在实际应用中,很可能产品是一个多层次的树状结构,这时候由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,因此简单工厂模式只适用于业务简单的情况下或者具体产品很少增加的情况,而对于复杂的业务环境可能不太适应了,这个时候就应该由工厂方法模式来出场了.
又来新需求了,那就是每种格式的预约数据中,需要提供页眉和页脚来描述每次预约.
咱们先来用简单工厂模式来实现上述功能,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
// 简单工厂模式
class
CommsManager {
const
BLOGGS = 1;
const
MEGA = 2;
private
=
$mode
;
public
function
__construct(
$mode
)
{
$this
->mode =
$mode
;
}
// 生成解码器对应的页眉
public
function
getHeaderText()
{
switch
(
$this
->mode ) {
case
( self::MEGA ):
return
"MegaCal header\n"
;
default
:
return
"BloggsCal header\n"
;
}
}
// 生成解码器
public
function
getApptEncoder()
{
switch
(
$this
->mode ) {
case
( self::MEGA ):
return
new
MegaApptEncoder();
default
:
return
new
BloggsApptEncoder();;
}
}
}
|
从上述代码中,我们可以看到,相同的条件语句switch在不同的方法中出现了重复,而且如果添加新的数据格式,那么需要改动的类过多。所以需要对我们的结构进行修改,以求更容易扩展和维护.
我们可以使用创造者子类分别生成对应的产品,这样添加新的数据格式时,只需要添加一个创造者子类即可,方便扩展和维护,这也就是比简单工厂模式更复杂一点的工厂模式,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
// 工厂模式
abstract
class
CommsManager {
abstract
function
getHeaderText();
abstract
function
getApptEncoder();
abstract
function
getFooterText();
}
class
BloggsCommsManager
extends
CommsManager {
function
getHeaderText()
{
return
"BloggsCal Header\n"
;
}
function
getApptEncoder()
{
return
new
BloggsApptEncoder();
}
function
getFooterText()
{
return
"BloggsCal Footer\n"
;
}
}
class
MegaCommsManager
extends
CommsManager {
function
getHeaderText()
{
return
"MegaCal Header\n"
;
}
function
getApptEncoder()
{
return
new
MegaApptEncoder();
}
function
getFooterText()
{
return
"MegaCal Footer\n"
;
}
}
|
在这个时候,如果有新的数据格式,只需要添加一个创造类的子类即可,例如此时想获取MegaCal对应的解码器直接通过MegaCommsManager::getApptEncoder()获取即可.
完了么?我只能说,这个实例还没有完事.
新需求又来了,那就是不仅需要和A公司交流预约数据(Appointment),还需要交流待办事宜(Ttd)、联系人(Contact)等数据,同样的这些数据交流的格式也是BloggsCal和MegaCal,这个时候,我们可以直接在对应解码器的子类中添加处理事宜(TtD)和联系人(Contact)的方法,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
// 抽象工厂模式
abstract
class
CommsManager {
abstract
function
getHeaderText();
abstract
function
getApptEncoder();
abstract
function
getTtdEncoder();
abstract
function
getContactEncoder();
abstract
function
getFooterText();
}
class
BloggsCommsManager
extends
CommsManager {
function
getHeaderText()
{
return
"BloggsCal Header\n"
;
}
function
getApptEncoder()
{
return
new
BloggsApptEncoder();
}
function
getTtdEncoder()
{
return
new
BloggsTtdEncoder();
}
function
getContactEncoder()
{
return
new
BloggsContactEncoder();
}
function
getFooterText()
{
return
"BloggsCal Footer\n"
;
}
}
class
MegaCommsManager
extends
CommsManager {
function
getHeaderText()
{
return
"MegaCal Header\n"
;
}
function
getApptEncoder()
{
return
new
MegaApptEncoder();
}
function
getTtdEncoder()
{
return
new
MegaTtdEncoder();
}
function
getContactEncoder()
{
return
new
MegaContactEncoder();
}
function
getFooterText()
{
return
"MegaCal Footer\n"
;
}
}
//当然需要添加对应的TtdEncoder抽象类和ContactEncoder抽象类,以及他们的子类。
|
好啦,到这里就算是差不多结束了工厂模式的变形过程,我们可以来简单归纳下这个变形过程中的核心,如下:
1.将系统和实现的细节分离开,我们可在示例中移除或者添加任意数目的编码格式而不会影响系统.
2.对系统中功能相关的元素强制进行组合,因此,通过使用BloggsCommsManager,可以确定只使用与BloggsCal有关的类.
3.添加新产品时将会令人苦恼,因为不仅需要创建新产品的具体实现,而且为了支持它,我们必须修改抽象创建者和它的每个具体实现.
当然,我们可以创建一个标志参数来决定返回什么对象的单一的make()方法,而不用给每个工厂创建独立的方法,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
abstract
class
CommsManager {
const
APPT = 1;
const
TTD = 2;
const
CONTACT = 3;
abstract
function
getHeaderText();
abstract
function
make (
$flag_init
);
abstract
function
getFooterText();
}
class
BloggsCommsManager
extends
CommsManager {
function
getHeaderText()
{
return
"BloggsCal Header\n"
;
}
function
make(
$flag_init
)
{
switch
(
$flag_init
) {
case
self::APPT:
return
new
BloggsApptEncoder();
case
self::TTD:
return
new
BloggsTtdEncoder();
case
self::CONTACT:
return
new
BloggsContactEncoder();
}
}
function
getFooterText()
{
return
"BloggsCal Header\n"
;
}
}
|
此时,如果还需要添加交流其它的数据,此时只需在抽象创造者中添加一个新的flag_init标识,并在子创造者中的make方法中添加一个条件,相比来说比原先的更加容易扩展,只需修改少数地方即可.
最后,来简单总结下:
简单工厂:适用于生成数量少,功能简单的产品(BloggApptEncoder和MegaApptEncoder) 。
工厂模式:适用于生成数量多,功能复杂的产品(多个产品树[BloggCal,MegaCal]、单个产品族[apptEncoder]),相比简单工厂来说:业务更复杂,功能更多,但是产品族还是单个.
抽象工厂:适用于生成多个产品族、多个产品树的情景(产品族[appt,ttd,contact],产品树[Bloggcal,megaCal])。相比于工厂模式,更容易扩展添加新的产品族 。
好啦,本次记录就到这里了.
希望本文所述对大家PHP程序设计有所帮助.
原文链接:https://blog.csdn.net/luyaran/article/details/82877315 。
最后此篇关于PHP设计模式之工厂模式(Factory)入门与应用详解的文章就讲到这里了,如果你想了解更多关于PHP设计模式之工厂模式(Factory)入门与应用详解的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
对此感到疯狂,真的缺少一些东西。 我有webpack 4.6.0,webpack-cli ^ 2.1.2,所以是最新的。 在文档(https://webpack.js.org/concepts/mod
object Host "os.google.com" { import "windows" address = "linux.google.com" groups = ["linux"] } obj
每当我安装我的应用程序时,我都可以将数据库从 Assets 文件夹复制到 /data/data/packagename/databases/ .到此为止,应用程序工作得很好。 但 10 或 15 秒后
我在 cc 模式缓冲区中使用 hideshow.el 来折叠我不查看的文件部分。 如果能够在 XML 文档中做到这一点就好了。我使用 emacs 22.2.1 和内置的 sgml-mode 进行 xm
已结束。此问题不符合 Stack Overflow guidelines .它目前不接受答案。 我们不允许提出有关书籍、工具、软件库等方面的建议的问题。您可以编辑问题,以便用事实和引用来回答它。 关闭
根据java: public Scanner useDelimiter(String pattern) Sets this scanner's delimiting pattern to a patt
我读过一些关于 PRG 模式以及它如何防止用户重新提交表单的文章。比如this post有一张不错的图: 我能理解为什么在收到 2xx 后用户刷新页面时不会发生表单提交。但我仍然想知道: (1) 如果
看看下面的图片,您可能会清楚地看到这一点。 那么如何在带有其他一些 View 的简单屏幕中实现没有任何弹出/对话框/模式的微调器日期选择器? 我在整个网络上进行了谷歌搜索,但没有找到与之相关的任何合适
我不知道该怎么做,我一直遇到问题。 以下是代码: rows = int(input()) for i in range(1,rows): for j in range(1,i+1):
我想为重写创建一个正则表达式。 将所有请求重写为 index.php(不需要匹配),它不是以/api 开头,或者不是以('.html',或'.js'或'.css'或'.png'结束) 我的例子还是这样
MVC模式代表 Model-View-Controller(模型-视图-控制器) 模式 MVC模式用于应用程序的分层开发 Model(模型) - 模型代表一个存取数据的对象或 JAVA PO
我想为组织模式创建一个 RDF 模式世界。您可能知道,组织模式文档基于层次结构大纲,其中标题是主要的分组实体。 * March auxiliary :PROPERTIES: :HLEVEL: 1 :E
我正在编写一个可以从文件中读取 JSON 数据的软件。该文件包含“person”——一个值为对象数组的对象。我打算使用 JSON 模式验证库来验证内容,而不是自己编写代码。符合代表以下数据的 JSON
假设我有 4 张 table 人 公司 团体 和 账单 现在bills/persons和bills/companys和bills/groups之间是多对多的关系。 我看到了 4 种可能的 sql 模式
假设您有这样的文档: doc1: id:1 text: ... references: Journal1, 2013, pag 123 references: Journal2, 2014,
我有这个架构。它检查评论,目前工作正常。 var schema = { id: '', type: 'object', additionalProperties: false, pro
这可能很简单,但有人可以解释为什么以下模式匹配不明智吗?它说其他规则,例如1, 0, _ 永远不会匹配。 let matchTest(n : int) = let ran = new Rand
我有以下选择序列作为 XML 模式的一部分。理想情况下,我想要一个序列: 来自 my:namespace 的元素必须严格解析。 来自任何其他命名空间的元素,不包括 ##targetNamespace和
我希望编写一个 json 模式来涵盖这个(简化的)示例 { "errorMessage": "", "nbRunningQueries": 0, "isError": Fals
首先,我是 f# 的新手,所以也许答案很明显,但我没有看到。所以我有一些带有 id 和值的元组。我知道我正在寻找的 id,我想从我传入的三个元组中选择正确的元组。我打算用两个 match 语句来做到这
我是一名优秀的程序员,十分优秀!