PHP中怎么实现工厂模式
今天就跟大家聊聊有关PHP中怎么实现工厂模式,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。
工厂模式的意思其实就是提供获取某个对象实例的一个接口,同时使调用代码避免确定实例化基类的步骤,实际上就是建立一个统一的类实例化的函数接口,完事统一调用,统一控制,它是PHP中常用的一种设计模式,一般会配合单例模式一起使用,来加载php类库中的类。来看一个简单的应用场景:
我们拥有一个Json类,String类,Xml类。
如果我们不使用工厂方式实例化这些类,则需要每一个类都需要new一遍,过程不可控,类多了,到处都是new的身影
引进工厂模式,通过工厂统一创建对象实例。
代码如下:
<?php //工厂模式提供获取某个对象实例的一个接口,同时使调用代码避免确定实例化基类的步骤 //字符串类 classString{ publicfunctionwrite(){} } //Json类 classJson{ publicfunctiongetJsonData(){} } //xml类 classXml{ publicfunctionbuildXml(){} } //工厂类 classFactory{ publicstaticfunctioncreate($class){ returnnew$class; } } Factory::create("Json");//获取Json对象
我们现在应该对于工厂模式有了一个大概的理解了,咱们接下来可以从字面上来理解一下。
工厂么,它就是生产产品的地方,它有原料,设备和产品,那么在PHP中,我们可以理解为,这个工厂模式可以通过一个工厂类(设备),来调用自身的静态方法(生产方式)来产生对象实例(产品),在上述实例中的Json类等,就相当于原料了。
理解了上面的一段话之后,我们就可以再深入的了解下这个工厂模式了。
我们来考虑以下场景,如果项目中,我们通过一个类创建对象,在快完成或者已经完成,要扩展功能的时候,发现原来的类的类名不是很合适或者发现类需要添加构造函数参数才能实现功能扩展,在这种情况下,大家就可以感受到“高内聚低耦合”的博大精深,我们可以尝试使用工厂模式解决这个问题。
还有就是最经典的数据库连接问题等等,都可以使用工厂模式来接觉问题,咱们也不废话,来看一下网上一个比较经典的案例:
interfaceTransport{ publicfunctiongo(); } classBusimplementsTransport{ publicfunctiongo(){ echo"bus每一站都要停"; } } classCarimplementsTransport{ publicfunctiongo(){ echo"car跑的飞快"; } } classBikeimplementsTransport{ publicfunctiongo(){ echo"bike比较慢"; } } classtransFactory{ publicstaticfunctionfactory($transport) { switch($transport){ case'bus': returnnewBus(); break; case'car': returnnewCar(); break; case'bike': returnnewBike(); break; } } } $transport=transFactory::factory('car'); $transport->go();
大家有了解过工厂模式应该都知道,工厂模式有三种,那就是一般工厂模式(静态工厂模式),工厂模式,还有就是抽象工厂模式,咱这里并未把所有的案例全部介绍完毕,不过嘞,咱们可以跟着网上的一个案例,来简单了解下工厂模式的三种变形的过程。
首先,我们来假设有个关于个人事务管理的项目,功能之一就是管理Appointment(预约)对象。我们的业务团队和A公司建立了关系,目前需要使用一个叫做BloggsCal格式来和他们交流预约相关的数据,但是业务部门提醒可能会有更多的数据格式,所以解码器可能会有多种,我们呢,为了避免在逻辑代码中使用过多的if else,可能就会需要使用工厂模式来将创造者和使用者分开。
那么,我们就需要两个类,一个类AppEncoder用于定义一个解码器,将A公司传来的数据解码;另外一个类CommsManager用于获取该解码器,就是调用AppEncoder类,用于与A公司进行通信。使用模式术语说,CommsManager就是创造者,AppEncoder就是产品(一个创造者、一个产品,将类的实例化和对象的使用分离开,这就是工厂模式的思想)。
咱们先来通过简单工厂模式实现上述任务场景,如下:
//产品类 classBloggsApptEncoder{ functionencode() { return"AppointmentdataencodedinBloggsCalformat\n"; } } //创造者类 classCommsManager{ functionstaticgetBloggsApptEncoder() { returnnewBloggsApptEncoder(); } }
大概明白了奥,好啦,现在又有新任务了,业务部门告诉我们需要新增一种数据格式MegCal,来完成数据交流,那么我们就需要新增对应的解码器类,然后直接在commsManager新增参数来标识需要实例化哪个解码器,如下:
classCommsManager{ constBLOGGS=1; constMEGA=2; private$mode; publicfunction__construct($mode) { $this->mode=$mode; } functiongetApptEncoder() { switch($this->mode){ case(self::MEGA): returnnewMegaApptEncoder(); default: returnnewBloggsApptEncoder(); } } }
上述两个案例综合起来就是简单工厂模式了,它符合现实中的情况,而且客户端免除了直接创建产品对象的责任,而仅仅负责“消费”产品(正如暴发户所为)。
接下来,我们从开闭原则上来分析下简单工厂模式,当新增一种数据格式的时候,只要符合抽象产品格式,那么只要通知工厂类知道就可以被使用了(即创建一个新的解码器类,继承抽象解码器ApptEncoder),那么对于产品部分来说,它是符合开闭原则的——对扩展开放、对修改关闭,但是对于工厂类不太理想,因为每增加一各格式,都要在工厂类中增加相应的商业逻辑和判断逻辑,这显自然是违背开闭原则的。
然而在实际应用中,很可能产品是一个多层次的树状结构,这时候由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,因此简单工厂模式只适用于业务简单的情况下或者具体产品很少增加的情况,而对于复杂的业务环境可能不太适应了,这个时候就应该由工厂方法模式来出场了。
又来新需求了,那就是每种格式的预约数据中,需要提供页眉和页脚来描述每次预约。
咱们先来用简单工厂模式来实现上述功能,如下:
//简单工厂模式 classCommsManager{ constBLOGGS=1; constMEGA=2; private=$mode; publicfunction__construct($mode) { $this->mode=$mode; } //生成解码器对应的页眉 publicfunctiongetHeaderText() { switch($this->mode){ case(self::MEGA): return"MegaCalheader\n"; default: return"BloggsCalheader\n"; } } //生成解码器 publicfunctiongetApptEncoder() { switch($this->mode){ case(self::MEGA): returnnewMegaApptEncoder(); default: returnnewBloggsApptEncoder();; } } }
从上述代码中,我们可以看到,相同的条件语句switch在不同的方法中出现了重复,而且如果添加新的数据格式,那么需要改动的类过多。所以需要对我们的结构进行修改,以求更容易扩展和维护。
我们可以使用创造者子类分别生成对应的产品,这样添加新的数据格式时,只需要添加一个创造者子类即可,方便扩展和维护,这也就是比简单工厂模式更复杂一点的工厂模式,如下:
//工厂模式 abstractclassCommsManager{ abstractfunctiongetHeaderText(); abstractfunctiongetApptEncoder(); abstractfunctiongetFooterText(); } classBloggsCommsManagerextendsCommsManager{ functiongetHeaderText() { return"BloggsCalHeader\n"; } functiongetApptEncoder() { returnnewBloggsApptEncoder(); } functiongetFooterText() { return"BloggsCalFooter\n"; } } classMegaCommsManagerextendsCommsManager{ functiongetHeaderText() { return"MegaCalHeader\n"; } functiongetApptEncoder() { returnnewMegaApptEncoder(); } functiongetFooterText() { return"MegaCalFooter\n"; } }
在这个时候,如果有新的数据格式,只需要添加一个创造类的子类即可,例如此时想获取MegaCal对应的解码器直接通过MegaCommsManager::getApptEncoder()
获取即可。
完了么?我只能说,这个实例还没有完事。
新需求又来了,那就是不仅需要和A公司交流预约数据(Appointment),还需要交流待办事宜(Ttd)、联系人(Contact)等数据,同样的这些数据交流的格式也是BloggsCal和MegaCal,这个时候,我们可以直接在对应解码器的子类中添加处理事宜(TtD)和联系人(Contact)的方法,如下:
//抽象工厂模式 abstractclassCommsManager{ abstractfunctiongetHeaderText(); abstractfunctiongetApptEncoder(); abstractfunctiongetTtdEncoder(); abstractfunctiongetContactEncoder(); abstractfunctiongetFooterText(); } classBloggsCommsManagerextendsCommsManager{ functiongetHeaderText() { return"BloggsCalHeader\n"; } functiongetApptEncoder() { returnnewBloggsApptEncoder(); } functiongetTtdEncoder() { returnnewBloggsTtdEncoder(); } functiongetContactEncoder() { returnnewBloggsContactEncoder(); } functiongetFooterText() { return"BloggsCalFooter\n"; } } classMegaCommsManagerextendsCommsManager{ functiongetHeaderText() { return"MegaCalHeader\n"; } functiongetApptEncoder() { returnnewMegaApptEncoder(); } functiongetTtdEncoder() { returnnewMegaTtdEncoder(); } functiongetContactEncoder() { returnnewMegaContactEncoder(); } functiongetFooterText() { return"MegaCalFooter\n"; } } //当然需要添加对应的TtdEncoder抽象类和ContactEncoder抽象类,以及他们的子类。
好啦,到这里就算是差不多结束了工厂模式的变形过程,我们可以来简单归纳下这个变形过程中的核心,如下:
1.将系统和实现的细节分离开,我们可在示例中移除或者添加任意数目的编码格式而不会影响系统。
2.对系统中功能相关的元素强制进行组合,因此,通过使用BloggsCommsManager,可以确定只使用与BloggsCal有关的类。
3.添加新产品时将会令人苦恼,因为不仅需要创建新产品的具体实现,而且为了支持它,我们必须修改抽象创建者和它的每个具体实现。
当然,我们可以创建一个标志参数来决定返回什么对象的单一的make()方法,而不用给每个工厂创建独立的方法,如下:
abstractclassCommsManager{ constAPPT=1; constTTD=2; constCONTACT=3; abstractfunctiongetHeaderText(); abstractfunctionmake($flag_init); abstractfunctiongetFooterText(); } classBloggsCommsManagerextendsCommsManager{ functiongetHeaderText() { return"BloggsCalHeader\n"; } functionmake($flag_init) { switch($flag_init){ caseself::APPT: returnnewBloggsApptEncoder(); caseself::TTD: returnnewBloggsTtdEncoder(); caseself::CONTACT: returnnewBloggsContactEncoder(); } } functiongetFooterText() { return"BloggsCalHeader\n"; } }
看完上述内容,你们对PHP中怎么实现工厂模式有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注恰卡编程网行业资讯频道,感谢大家的支持。
推荐阅读
-
php字符串增加1如何实现
php字符串增加1如何实现这篇“php字符串增加1如何实现”文章的...
-
php如何判断字符串是否有中文
-
php如何实现字符串去掉头尾
-
php字符串的组成是什么
php字符串的组成是什么这篇文章主要讲解了“php字符串的组成是什...
-
php如何让Swoole/Pool进程池实现Redis持久连接
php如何让Swoole/Pool进程池实现Redis持久连接本篇...
-
php字符串长度不一致如何解决
-
php时区不正确如何解决
-
php+fread()乱码如何解决
php+fread()乱码如何解决本篇内容介绍了“php+frea...
-
php explode报错如何解决
-
linux Centos如何安装PHP7
linuxCentos如何安装PHP7今天小编给大家分享一下li...