js常用设计模式实现二工厂模式和抽象工厂模式

创建型模式创建型模式是对一个类的实例化过程进行了抽象,把对象的创建和对象的使用进行了分离上一篇介绍了下单例模式,这一篇介绍一下工厂模式和抽象工厂模式,为什么把这两个放在一起说,因为我个人认为他们两个是有关联的,简单工厂模式被我看作是工厂模式的简洁版,然后工厂模式被我看作是抽象工厂模式的简洁版 简单工厂定义:简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。比如你去专门卖鼠标的地方你可以买各种各样的鼠标 最通俗的理解就是封装,简单带过一下 function mouse(color,long){ var o = new Object(); o.color= color; o.long= long; o.Explain= function(){ console.log(this.color,this.long); } return o; } var mouseA = mouse('黑色',3);工厂模式定义:工厂方法模式中,工厂父类负责定义创建产品对象的公共接口,而工厂子类则负责生成具体的产品对象,这样做的目的是将产品类的实例化操作延迟到工厂子类中完成,即通过工厂子类来确定究竟应该实例化哪一个具体产品类。比如去旁边还有个专门卖键盘的和耳机的然后他和卖鼠标的组成了一个门店 你要进如这个门店然后买鼠标或者键盘或者耳机 你可以把这个工厂模式理解成是 进化版的简单工厂模式 shop= function(val) { if(typeof this === "object") { //看看有没有new 没有new的话new一个 var s = new this[val](); return s; } else { return new shop(val); } } shop.prototype = { mouse: function() { console.log("买了个鼠标") }, keyboard: function() { console.log("买了个键盘") }, headset: function() { console.log("买了个耳机") } } bigShop = new shop(); mouse = new bigshop.mouse();抽象工厂模式定义:抽象工厂模式是指当有多个抽象角色时,使用的一种工厂模式。抽象工厂模式可以向客户端提供一个接口,使客户端在不必指定产品的具体的情况下,创建多个产品族中的产品对象。前边的两个都是直接生产实例的,开始以为抽象工厂也是但是后来发现抽象工厂更像是生产工厂,其实抽象工厂其实是实现子类继承父类的方法。 ...

July 11, 2019 · 2 min · jiezi

Java设计模式工厂模式

一、简单工厂定义简单工厂其实并不属于23种GOF设计模式之一,该模式是工厂方法模式的弱化(或者说是工厂方法模式的一种特例),因为简单,所以称为简单工厂模式(Simple Factory Pattern),也叫做静态工厂模式。虽然不是"标准"的设计模式(更像是一种编程习惯),但在实际项目中,采用该方法的案例还是比较多的。 简单工厂模式没有严格的定义,我们姑且使用以下描述: 提供一个创建对象实例的功能,而无须关心其具体实现。被创建实例的类型可以是接口、抽象类,也可以是具体的类关键点具体的工厂类创建产品的工厂方法 静态方法(静态工厂名字的由来)通常会有一个"类型"参数(还有一种是可以提供多个静态工厂方法并通过不同方法名区别要创建的产品)根据参数,利用if或者switch创建产品并返回实现抽象产品类(接口或抽象类)public interface Product { void doSomething(); void doAnything();}具体产品类public class ConcreteProductA implements Product { @Override public void doSomething() { System.out.println("ConcreteProductA doSomething"); } @Override public void doAnything() { System.out.println("ConcreteProductA doAnything"); }}public class ConcreteProductB implements Product { @Override public void doSomething() { System.out.println("ConcreteProductB doSomething"); } @Override public void doAnything() { System.out.println("ConcreteProductB doAnything"); }}工厂类public class Creator { public static Product createProduct(String type) { Product product = null; switch (type) { case "A": product = new ConcreteProductA(); break; case "B": product = new ConcreteProductB(); break; } return product; }}客户端代码public class Client { public static void main(String[] args) { Product productA = Creator.createProduct("A"); productA.doSomething(); productA.doAnything(); Product productB = Creator.createProduct("B"); productB.doSomething(); productB.doAnything(); }}优点简单解耦 ...

June 15, 2019 · 3 min · jiezi

设计模式工厂模式

工厂模式分为三种:简单工厂模式、工厂模式、抽象工厂模式从实现上看,代码复杂度依次上升简单工厂模式:采用switch语句根据传入的参数不同返回不同的对象,缺点是必须得传参,传参有问题会导致调用不成功,且后期扩展不方便,如果要加一个对象需要改动原来的代码工厂模式:将每个对象写作不同的方法,不用传参,且后期扩展方便,直接加方法就行抽象工厂模式:将方法加上了static修饰,使用的时候直接调用,不用实例化,更方便

May 12, 2019 · 1 min · jiezi

设计模式-工厂模式的演变过程

阅读须知提前了解设计模式的 六大原则,它们代码演化的 目标 和 驱动力。初始阶段需求:将网站的报错信息通过 Email 发送给管理员。需求背景:网站报500类错误时,管理员和开发人员并不能实时知道,等查看日志时或用户打电话过来返回问题时,有可能已经造成了极大的不良影响。so,开发一个实时通知功能,有问题早发现早治疗,岂不美哉?内心想法:这简单,写个异常处理器,配置到系统中进行监听,渲染时走 Email 类发出去。一顿过程化编程操作猛如虎…// Email 类class Email { // 发送 public function send(){}}// 错误异常处理器class ErrorHandler { // 渲染异常和错误 protected function renderException() { $prepare[‘host’] = ‘smtp.qq.com’; $prepare[‘port’] = 587; $prepare[’encryption’] = ’tls’; $prepare[‘username’] = ‘QQ 号’; $prepare[‘password’] = ‘授权码’; // … 艰辛复杂的对象初始化工作 $Mailer = new Mail($prepare); // 设置发送信息 $Mailer->setFrom(’noreply@domain.com’) ->setTo(‘admin@domain.com’) ->setSubject(‘报错了!’) ->setTextBody(‘具体的报错内容’); // 嗖~,搞定! $Mailer->send(); }}Gof: 嗯…这段代码,很头疼,违反了…很多原则啊。ErrorHandler 作为客户端,想发送邮件出去,却参与了邮件发送对象的初始化工作,身为客户端很累的,不符合单一职责原则。ErrorHandler 也知道了邮件发送对象的创建方法,但是它知道这个干吗? 也不符合迪米特原则。但是,如果他们的产品人员提到需求“止步于此”的话,这样写也行。如果开发人员“积极进取”的话,代码可以多往下走一个阶段,因为…太过程化编程了!第二阶段需求:有人退订单了或下单了长时间没有付款的客户,邮件通知到客服。需求背景:让客服跟进一下异常订单的情况,知己知彼,百战不殆嘛。内心想法:这简单,写个订单监听器,配置到系统中进行监听,需要的时候走 EMail 类发出去,已经搞过报错信息通知,这个简单。又在另一处一顿过程化编程操作猛如虎…// Email 类class Email { // 发送 public function send(){}}// 订单监听器class OrderHandler { // 通知 protected function notifly() { $prepare[‘host’] = ‘smtp.qq.com’; $prepare[‘port’] = 587; $prepare[’encryption’] = ’tls’; $prepare[‘username’] = ‘QQ 号’; $prepare[‘password’] = ‘授权码’; // … 艰辛复杂的对象初始化工作 $Mailer = new Email($prepare); // 设置发送信息 $Mailer->setFrom(’noreply@domain.com’) ->setTo(‘service@domain.com’) ->setSubject(‘有异常订单!’) ->setTextBody(‘具体的订单信息’); // 发送,搞定! $Mailer->send(); }}Gof:这就看不过过去了,可复用性太差了,更换个邮件配置还得改多处。需要优化一下了。内心想法: OK,封装一下,封装到一处。// EMail 类class Email { private static $_instance = null; private function __construct(){} private function __clone(){} // 获取对象 public static function getInstance() { if( self::$_instance == null ){ $prepare[‘host’] = ‘smtp.qq.com’; $prepare[‘port’] = 587; $prepare[’encryption’] = ’tls’; $prepare[‘username’] = ‘QQ 号’; $prepare[‘password’] = ‘授权码’; // … 艰辛复杂的对象初始化工作 self::$_instance = new self($prepare); } return self::$_instance; } // 设置消息体 public function initMessage($title, $content) { // 设置消息 $this->setFrom(’noreply@domain.com’) ->setTo(‘service@domain.com’) ->setSubject($title) ->setTextBody($content); } // 嗖~ public function send(){}}// 订单监听器class OrderHandler { // 通知 protected function notifly() { $Mailer = Email::getInstance(); // 设置发送信息 $Mailer->initMessage(‘有异常订单!’, ‘具体的订单信息’); // 嗖~ $Mailer->send(); }}// 错误异常处理器class ErrorHandler { // 渲染异常和错误 protected function renderException() { $Mailer = Email::getInstance(); // 设置发送信息 $Mailer->initMessage(‘报错了!’, ‘具体的报错内容’); // 嗖~ $Mailer->send(); }}Gof:嗯,孺子可教也。ErrorHandler、OrderHandler 作为客户端,不再关心邮件对象的创建过程,直接拿来就用,符合了单一职责原则和迪米特原则。还将 Email 类做成了单例模式,节省了内存空间,提高了性能,不错!第三阶段需求:切换发送通道,通过钉钉群消息发送,关闭原来的 Email 发送通道。需求背景:邮件通知只通知了相应几个管理员,当有人员变化是还需要改收信人配置,最主要的是邮件提醒也不及时啊,有的人还懒的刷邮件。最近公司启用了钉钉,直接走钉钉群自定义消息,人员变动直接屏蔽在外部,增删群成员就行,消息收取方便了,谁看了过了也能知道,报错信息也从共有知识变成了公共知识,岂不美哉?内心想法:处世多年的经验告诉我 Email 邮件通知类不能删除,万一哪天要再加上 Email 发送功能呢?说不定要通知的人不是内部人员没有钉钉账号呢,或要求钉钉消息和邮件同时发送呢, 删除了还得重写。坚决不能删除。之前邮件类优化了一版,这次钉钉通知类直接一步到位,将实例化过程封装在自己内部。// 钉钉通知类class DingDing { private static $_instance = null; private function __construct(){} private function __clone(){} // 获取对象 public static function getInstance() { if( self::$_instance == null ){ $prepare[‘url’] = ‘https://oapi.dingtalk.com/robot/send?access_token='; $prepare[’token’] = ‘123456abcdefg’; // … 艰辛复杂的对象初始化工作 self::$_instance = new self($prepare); } return self::$_instance; } // 设置消息体 public function initMessage($title, $content) { // 设置消息 $this->contentBody([ “msgtype” => “text”, “text” => [ “content” => “{$title}\n {$content}”, ], ]); } // 发送消息 public function sendMsg(){}}// 错误异常处理器class ErrorHandler { // 渲染异常和错误 protected function renderException() { // 通过配置获取使用的消息通道 $channel = ‘dingding’; switch ($channel) { case “dingding”: $DingDing = DingDing::getInstance(); // 设置消息 $DingDing->initMessage(‘报错了!’, ‘具体的报错内容’); // 发送,搞定! $DingDing ->sendMsg(); break; // case “other”: //… case “email”: default: $Mailer = Email::getInstance(); // 设置发送人 $Mailer->initMessage(‘报错了!’, ‘具体的报错内容’); // 发送,搞定! $Mailer->send(); } }}Gof:《从0到1》告诉我们,0到1很难,1到n却很简单,需求也是这样。有2个通知类型很快就会有多个通知类,到时候 renderException() 将会很臃肿。而且,身为 高层模块 的 ErrorHandler 异常处理器类直接依赖的 底层模块 的 Email邮件通知类 和 DingDing钉钉通知类,也违背了 依赖倒置原则 。每次新增、修改通知类时都需要修改 ErrorHandler 类,也不符合 开发-封闭原则。ErrorHandler 类知道了所有的 消息通知类,但是它其实只要消息通知的功能而已,违背了 迪米特原则。得改!内心想法:通过 依赖倒置原则 我们将 依赖细节(类、对象)改为 依赖抽象(抽象类、接口),对消息通知类进行抽象出 消息通知接口。抽象出接口后,就可以通过 实例化参数 或 方法参数 加上 接口类型 来限制只传入我们需要的对象。不至于开发人员传递对象错误到运行时才报错的状况出现。// 通知接口interface INotify{ // 获取实例 public function getInstance(); // 准备消息体 public function initMessage($title, $content); // 发送消息 public function send();}// 钉钉类class DingDing implements INotify { private $title; private $content; // 获取实例 public function getInstance(){return new self();} // 准备消息体 public function initMessage($title, $content){ $this->title = $title; $this->content = $content; } // 发送消息 public function send(){ echo $this->title. $this->content; }}// EMail 类class Email implements INotify { private $title; private $content; // 获取实例 public function getInstance(){return new self();} // 准备消息体 public function initMessage($title, $content){ $this->title = $title; $this->content = $content; } // 发送消息 public function send(){ echo $this->title. $this->content; }}// 错误异常处理器class ErrorHandler { // 消息通知对象 private $notify; // 初始化时限定传入符合 INotify 接口的类 public function __construct(INotify $notifyObj) { $this->notify = $notifyObj; } // 渲染异常和错误 public function renderException() { // 初始化消息体 $this->notify->initMessage(‘有报错了!’, ‘具体的报错信息’); // 发送消息 $this->notify->send(); }}// 订单监听器class OrderHandler { // 消息通知对象 private $notify; // 初始化时限定传入符合 INotify 接口的类 public function __construct(INotify $notifyObj) { $this->notify = $notifyObj; } // 通知 public function notify() { // 初始化消息体 $this->notify->initMessage(‘有异常订单!’, ‘具体的订单信息’); // 发送消息 $this->notify->send(); }}客户端代码# 错误异常处理器客户端// 通过配置获取使用的消息通道$channelError = ‘dingding’;switch ($channelError) { case “dingding”: $MessageNotify = DingDing::getInstance(); break; case “other1”: //… case “email”: default: $MessageNotify = Email::getInstance();}$ErrorHandler = new ErrorHandler($MessageNotify);$ErrorHandler->renderException();# 订单监听器客户端// 通过配置获取使用的消息通道$channelOrder = ’email’;switch ($channelOrder) { case “dingding”: $MessageNotify = DingDing::getInstance(); break; case “other1”: //… case “email”: default: $MessageNotify = Email::getInstance();}$OrderHandler = new OrderHandler($MessageNotify);$OrderHandler->notify();Gof:高层模块的 ErrorHandler 异常处理器依赖了抽象的 INotify 接口,符合了 依赖倒置原则。当有新的的消息通知需求时直接实现 INotify 接口,并通过 初始化参数 传入即可,不用再修改 ErrorHandler 类,也符合了 开发-封闭原则。通过 初始化参数 传入具体的消息通知对象,ErrorHandler 也不用再关心具体有多少种通知方式,具体用的哪种通知方式,也符合了 迪米特原则。但是还有以下不足,需要进步抽象优化。具体创建哪个消息通知对象的处理出现了重复,需要整合到一处,方便修改和复用。消息通知对象的创建过程放到了消息通知类的内部,多少有点违背了 单一职责原则。如果创建过程很“复杂”,强依赖了外部环境,例如依赖别的类的实例,或直接从具体的数据源(例如:DB,Redis)中读取配置等,将不利于以后的测试和功能迭代,应该将创建过程提到类外部,必要的参数通过初始化参数形式传入。// 通知消息工厂class NotifyFactory{ // 创建通知消息对象 public static function create($channel) { switch ($channel) { case “dingding”: $MessageNotify = DingDing::getInstance(); break; case “other1”: //… case “email”: default: $MessageNotify = Email::getInstance(); } return $MessageNotify; }}# 错误异常处理器客户端// 通过配置获取使用的消息通道$channelError = ‘dingding’;$MessageNotify = NotifyFactory::create($channelError);$ErrorHandler = new ErrorHandler($MessageNotify);$ErrorHandler->renderException();# 订单监听器客户端// 通过配置获取使用的消息通道$channelOrder = ’email’;$MessageNotify = NotifyFactory::create($channelOrder);$OrderHandler = new OrderHandler($MessageNotify);$OrderHandler->notify();等等灯等灯: 现在已经达成了 简单工厂模式。Gof: 嗯不错,消息通知类对外部的依赖被提到了类的外部,这样的好处多啊:方便对类进行自动化测试,例如 DingDing 原来初始化时从 DB 中获取配置进行初始化。单独测试 DingDing 类时,就必须要连上DB数据库,现在需要将配置通过初始化参数传入接口,参数值想从哪取就从哪取。外部依赖变更时,只需要对 NotifyFactory 类进行修改,修改影响范围变小了。不过,还有一个问题,那就是每次新增消息通知类时都需要修改 NotifyFactory 消息通知工厂的代码,往里添加 case 判断,不符合 开发-封闭原则。内心想法:我们可以进一步抽象,将对代码的修改调整为对类的增删上。// 通知消息工厂接口interface IFactory{ // 创建通知消息对象 public function create();}// 钉钉工厂类class DingDingFactory implements IFactory { public function create() { return DingDing::getInstance(); }}// Email工厂类class EmailFactory implements IFactory { public function create() { return Email::getInstance(); }}// Sms 工厂类class SmsFactory implements IFactory { public function create() { return Sms::getInstance(); }}// Sms 类class Sms implements INotify { private $title; private $content; // 获取实例 public function getInstance(){return new self();} // 准备消息体 public function initMessage($title, $content){ $this->title = $title; $this->content = $content; } // 发送消息 public function send(){ echo $this->title. $this->content; }}# 错误异常处理器客户端// 通过配置获取消息通知工厂类名$classError = ‘DingDingFactory’;// $classError = ‘SmsFactory’; 新增预备的短信通知通道$MessageNotify = (new $classError())->create();$ErrorHandler = new ErrorHandler($MessageNotify);$ErrorHandler->renderException();# 订单监听器客户端// 通过配置获取消息通知工厂类名$channelOrder = ‘EmailFactory’;$MessageNotify = (new $channelOrder())->create();$OrderHandler = new OrderHandler($MessageNotify);$OrderHandler->notify();等等灯等灯: 现在已经达成了 工厂方法模式。Gof: 当新增消息通知类时,已不需要修改任何已有代码,只需要新增一个 通知工厂类 和 一个消息通知类 即可。完美!!!此时启用消息通知类的变更被限定在配置文件或数据库数据配置变化上,切换消息通知通道并不需要修改程序代码。第四阶段需求:发送通知时记录一下日志,方便日后查询与统计。需求背景:有了即时通知,但想后期查询或统计怎么办,记录一下日志吧。异常订单比较重要,记录到 MySQL 中,查询异常报错字段比较多,记录的 Elasticsearch 中。内心想法:日志类和消息通知类很像嘛,直接搞成工厂方法模式,哈哈。// 日志接口interface ILog{ // 获取实例 public function getInstance(); // 写日志 public function write();}// Mysql Log 类class MysqlLog implements ILog {}// EalsticsearchLog 类class EalsticsearchLog implements ILog {}// 日志工厂接口interface ILogFactory{ // 创建日志记录对象 public function create();}// Mysql 日志工厂类class MysqlLogFactory implements ILogFactory { public function create() { return MysqlLog::getInstance(); }}// Ealsticsearch 日志工厂类class EalsticsearchLogFactory implements ILogFactory { public function create() { return EalsticsearchLog::getInstance(); }}# 错误异常处理器客户端// 通过配置获取消息通知工厂类名$classError = ‘DingDingFactory’;$MessageNotify = (new $classError())->create();// 通过配置获取日志记录工厂类名$logClassError = ‘EalsticsearchLogFactory’;$Log = (new $logClassError)->create();$ErrorHandler = new ErrorHandler($MessageNotify, $Log);$ErrorHandler->renderException();# 订单监听器客户端// 通过配置获取消息通知工厂类名$channelOrder = ‘EmailFactory’;$MessageNotify = (new $channelOrder())->create();// 通过配置获取日志记录工厂类名$logClassError = ‘MysqlLogFactory’;$Log = (new $logClassError)->create();$OrderHandler = new OrderHandler($MessageNotify, $Log);$OrderHandler->notify();Gof: 新增一个工厂模式并非这么简单就能解决问题的。 目前需求是 消息通知 并 记录日志 ,两者已经是 组合 关系,将这种组合关系下放到客户进行创建那么就不符合 单一职责原则。客户端还知道有2个工厂,2个工厂生产的东西必须搭配在一起才能实现消息通知并记录日志的功能,不符合 迪米特原则。类似买苹果笔记本,有不同的配置,简单那2个配置举例子,cpu 分为 i7 和 i5, 屏幕分为 13 寸 和 15 寸。普通消费者买笔记本会说:“我要个玩游戏爽的笔记本”,店员应该直接给出 i7 + 15 寸配置的机型。如果一个人也是玩游戏,过来直接说:“要个 i7 + 15 寸配置的机型”,那这个人一听就是程序员(知道的太多了!不符合单一职责原则和 迪米特原则)。// 通知消息工厂接口interface IFactory{ // 创建通知消息对象 public function createNotify(); // 创建日志记录对象 public function createLog();}// 异常报错工厂类class ErrorFactory implements IFactory { public function createNotify() { return DingDing::getInstance(); } public function createLog() { return EalsticsearchLog::getInstance(); }}// 异常订单工厂类class OrderFactory implements IFactory { public function createNotify() { return Email::getInstance(); } public function createLog() { return MysqlLog::getInstance(); }}# 错误异常处理器客户端// 通过配置获取异常错误工厂类名$classError = ‘ErrorFactory’;$Factory = new $classError();$ErrorHandler = new ErrorHandler($Factory->createNotify(), $Factory->createLog());$ErrorHandler->renderException();# 订单监听器客户端// 通过配置获取异常订单工厂类名$classError = ‘OrderFactory’;$Factory = new $classError();;$OrderHandler = new OrderHandler($Factory->createNotify(), $Factory->createLog());$OrderHandler->notify();等等灯等灯: 现在已经达成了 抽象工厂方法模式 。 ...

April 6, 2019 · 5 min · jiezi

PHP面试常考之设计模式——工厂模式

你好,是我琉忆,PHP程序员面试笔试系列图书的作者。本周(2019.3.11至3.15)的一三五更新的文章如下:周一:PHP面试常考之设计模式——工厂模式周三:PHP面试常考之设计模式——建造者模式周五:PHP面试常考之设计模式——策略模式今天这篇文章主要讲解的是PHP面试常考的设计模式之工厂模式。工厂模式其实可以划分为:简单工厂模式、工厂方法模式、抽象工厂模式等。具体它们有什么区别,用途有哪些呢?以下我将进行讲解。自己整理了一篇“设计模式需要遵守的5大原则”的文章,关注公众号:“琉忆编程库”,回复:“原则”,我发给你。以下内容如需转载,请注明作者和出处。1、简单工厂模式介绍:简单工厂模式又称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。在简单工厂模式中,可以根据参数的不同返回不同类的实例。简单工厂模式专门定义一个类来负责创建其他类的实例,被创建的实例通常都具有共同的父类。UML图:说明:CashFactory类:负责创建具体产品的实例CashSuper类:抽象产品类,定义产品子类的公共接口CreateCashAccept 类:具体产品类,实现Product父类的接口功能,也可添加自定义的功能简单工厂模式最大的优点在于实现对象的创建和对象的使用分离,将对象的创建交给专门的工厂类负责,但是其最大的缺点在于工厂类不够灵活,增加新的具体产品需要修改工厂类的判断逻辑代码,而且产品较多时,工厂方法代码将会非常复杂。实现示例:<?php //简单工厂模式class Cat{ function __construct() { echo “I am Cat class <br>”; }}class Dog{ function __construct() { echo “I am Dog class <br>”; }}class Factory{ public static function CreateAnimal($name){ if ($name == ‘cat’) { return new Cat(); } elseif ($name == ‘dog’) { return new Dog(); } }}$cat = Factory::CreateAnimal(‘cat’);$dog = Factory::CreateAnimal(‘dog’);2、工厂方法模式介绍:工厂方法模式通过定义一个抽象的核心工厂类,并定义创建产品对象的接口,创建具体产品实例的工作延迟到其工厂子类去完成。这样做的好处是核心类只关注工厂类的接口定义,而具体的产品实例交给具体的工厂子类去创建。当系统需要新增一个产品是,无需修改现有系统代码,只需要添加一个具体产品类和其对应的工厂子类,是系统的扩展性变得很好,符合面向对象编程的开闭原则;UML图:说明:Product:抽象产品类ConcreteProduct:具体产品类Factory:抽象工厂类ConcreteFactory:具体工厂类工厂方法模式是简单工厂模式的进一步抽象和推广。由于使用了面向对象的多态性,工厂方法模式保持了简单工厂模式的优点,而且克服了它的缺点。在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给子类去做。这个核心类仅仅负责给出具体工厂必须实现的接口,而不负责产品类被实例化这种细节,这使得工厂方法模式可以允许系统在不修改工厂角色的情况下引进新产品。实现示例:<?php interface Animal{ public function run(); public function say();}class Cat implements Animal{ public function run(){ echo “I ran slowly <br>”; } public function say(){ echo “I am Cat class <br>”; }}class Dog implements Animal{ public function run(){ echo “I’m running fast <br>”; } public function say(){ echo “I am Dog class <br>”; }}abstract class Factory{ abstract static function createAnimal();}class CatFactory extends Factory{ public static function createAnimal() { return new Cat(); }}class DogFactory extends Factory{ public static function createAnimal() { return new Dog(); }}$cat = CatFactory::createAnimal();$cat->say();$cat->run();$dog = DogFactory::createAnimal();$dog->say();$dog->run();自己整理了一篇“设计模式需要遵守的5大原则”的文章,关注公众号:“琉忆编程库”,回复:“原则”,我发给你。3、抽象工厂模式介绍:抽象工厂模式提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。抽象工厂模式又称为Kit模式,属于对象创建型模式。此模式是对工厂方法模式的进一步扩展。在工厂方法模式中,一个具体的工厂负责生产一类具体的产品,即一对一的关系,但是,如果需要一个具体的工厂生产多种产品对象,那么就需要用到抽象工厂模式了。为了便于理解此模式,这里介绍两个概念:产品等级结构:产品等级结构即产品的继承结构,如一个抽象类是电视机,其子类有海尔电视机、海信电视机、TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产品等级结构,抽象电视机是父类,而具体品牌的电视机是其子类。产品族 :在抽象工厂模式中,产品族是指由同一个工厂生产的,位于不同产品等级结构中的一组产品,如海尔电器工厂生产的海尔电视机、海尔电冰箱,海尔电视机位于电视机产品等级结构中,海尔电冰箱位于电冰箱产品等级结构中。UML类图:说明:具体类图的功能可以看UML图的说明实现示例:<?php interface TV{ public function open(); public function use();}class HaierTv implements TV{ public function open() { echo “Open Haier TV <br>”; } public function use() { echo “I’m watching TV <br>”; }}interface PC{ public function work(); public function play();}class LenovoPc implements PC{ public function work() { echo “I’m working on a Lenovo computer <br>”; } public function play() { echo “Lenovo computers can be used to play games <br>”; }}abstract class Factory{ abstract public static function createPc(); abstract public static function createTv();}class ProductFactory extends Factory{ public static function createTV() { return new HaierTv(); } public static function createPc() { return new LenovoPc(); }}$newTv = ProductFactory::createTV();$newTv->open();$newTv->use();$newPc = ProductFactory::createPc();$newPc->work();$newPc->play();自己编写的《PHP程序员面试笔试宝典》和《PHP程序员面试笔试真题解析》书籍,已在各大电商平台销售,两本可以帮助你更快更好的拿到offer的书。更多PHP相关的面试知识、考题可以关注公众号获取:琉忆编程库对本文有什么问题或建议都可以进行留言,我将不断完善追求极致,感谢你们的支持。 ...

March 11, 2019 · 2 min · jiezi

PHP设计模式范例 — DesignPatternsPHP(1)创建型设计模式

【搬运于GitHub开源项目DesignPatternsPHP】项目地址:戳我1、创建型设计模式在软件工程中,创建型设计模式承担着对象创建的职责,尝试创建适合程序上下文的对象,对象创建设计模式的产生是由于软件工程设计的问题,具体说是向设计中增加复杂度,创建型设计模式解决了程序设计中对象创建的问题。1.1 抽象工厂1.1.1 目的创建一系列相关或依赖的对象,而不指定它们的具体类。通常创建的类都实现相同的接口。抽象工厂的客户端并不关心这些对象是如何创建的,它只知道它们是如何组合在一起的。1.1.2 UML图1.1.3 代码你可以在 GitHub 上查看代码Parser.php<?phpnamespace DesignPatterns\Creational\AbstractFactory;interface Parser{ public function parse(string $input): array;}CsvParser.php<?phpnamespace DesignPatterns\Creational\AbstractFactory;class CsvParser implements Parser{ const OPTION_CONTAINS_HEADER = true; const OPTION_CONTAINS_NO_HEADER = false; /** * @var bool / private $skipHeaderLine; public function __construct(bool $skipHeaderLine) { $this->skipHeaderLine = $skipHeaderLine; } public function parse(string $input): array { $headerWasParsed = false; $parsedLines = []; foreach (explode(PHP_EOL, $input) as $line) { if (!$headerWasParsed && $this->skipHeaderLine === self::OPTION_CONTAINS_HEADER) { $headerWasParsed = true; continue; } $parsedLines[] = str_getcsv($line); } return $parsedLines; }}JsonParser.php<?phpnamespace DesignPatterns\Creational\AbstractFactory;class JsonParser implements Parser{ public function parse(string $input): array { return json_decode($input, true); }}ParserFactory.php<?phpnamespace DesignPatterns\Creational\AbstractFactory;class ParserFactory{ public function createCsvParser(bool $skipHeaderLine): CsvParser { return new CsvParser($skipHeaderLine); } public function createJsonParser(): JsonParser { return new JsonParser(); }}1.2 生成器模式1.2.1 目的生成器的目的是将复杂对象的创建过程(流程)进行抽象,生成器表现为接口的形式。在特定的情况下,比如如果生成器对将要创建的对象有足够多的了解,那么代表生成器的接口 interface 可以是一个抽象类(也就是说可以有一定的具体实现,就像众所周知的适配器模式)。如果对象有复杂的继承树,理论上创建对象的生成器也同样具有复杂的继承树。提示:生成器通常具有流畅的接口,推荐阅读关于 PHPUnit 的 mock 生成器获取更好的理解。1.2.2 例子PHPUnit: Mock 生成器1.2.3 UML图1.2.4 代码你可以在 GitHub 上找到这些代码Director.php<?phpnamespace DesignPatterns\Creational\Builder;use DesignPatterns\Creational\Builder\Parts\Vehicle;/* * Director is part of the builder pattern. It knows the interface of the builder * and builds a complex object with the help of the builder * * You can also inject many builders instead of one to build more complex objects /class Director{ public function build(BuilderInterface $builder): Vehicle { $builder->createVehicle(); $builder->addDoors(); $builder->addEngine(); $builder->addWheel(); return $builder->getVehicle(); }}BuilderInterface.php<?phpnamespace DesignPatterns\Creational\Builder;use DesignPatterns\Creational\Builder\Parts\Vehicle;interface BuilderInterface{ public function createVehicle(); public function addWheel(); public function addEngine(); public function addDoors(); public function getVehicle(): Vehicle;}TruckBuilder.php<?phpnamespace DesignPatterns\Creational\Builder;use DesignPatterns\Creational\Builder\Parts\Vehicle;class TruckBuilder implements BuilderInterface{ /* * @var Parts\Truck / private $truck; public function addDoors() { $this->truck->setPart(‘rightDoor’, new Parts\Door()); $this->truck->setPart(’leftDoor’, new Parts\Door()); } public function addEngine() { $this->truck->setPart(’truckEngine’, new Parts\Engine()); } public function addWheel() { $this->truck->setPart(‘wheel1’, new Parts\Wheel()); $this->truck->setPart(‘wheel2’, new Parts\Wheel()); $this->truck->setPart(‘wheel3’, new Parts\Wheel()); $this->truck->setPart(‘wheel4’, new Parts\Wheel()); $this->truck->setPart(‘wheel5’, new Parts\Wheel()); $this->truck->setPart(‘wheel6’, new Parts\Wheel()); } public function createVehicle() { $this->truck = new Parts\Truck(); } public function getVehicle(): Vehicle { return $this->truck; }}CarBuilder.php<?phpnamespace DesignPatterns\Creational\Builder;use DesignPatterns\Creational\Builder\Parts\Vehicle;class CarBuilder implements BuilderInterface{ /* * @var Parts\Car / private $car; public function addDoors() { $this->car->setPart(‘rightDoor’, new Parts\Door()); $this->car->setPart(’leftDoor’, new Parts\Door()); $this->car->setPart(’trunkLid’, new Parts\Door()); } public function addEngine() { $this->car->setPart(’engine’, new Parts\Engine()); } public function addWheel() { $this->car->setPart(‘wheelLF’, new Parts\Wheel()); $this->car->setPart(‘wheelRF’, new Parts\Wheel()); $this->car->setPart(‘wheelLR’, new Parts\Wheel()); $this->car->setPart(‘wheelRR’, new Parts\Wheel()); } public function createVehicle() { $this->car = new Parts\Car(); } public function getVehicle(): Vehicle { return $this->car; }}Parts/Vehicle.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;abstract class Vehicle{ /* * @var object[] / private $data = []; /* * @param string $key * @param object $value / public function setPart($key, $value) { $this->data[$key] = $value; }}Parts/Truck.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Truck extends Vehicle{}Parts/Car.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Engine{}Parts/Engine.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Engine{}Parts/Wheel.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Wheel{}Parts/Door.php<?phpnamespace DesignPatterns\Creational\Builder\Parts;class Door{}1.3 工厂方法1.3.1 目的SimpleFactory的优点是您可以子类化它来实现创建对象的不同方法。对于简单的情况,这个抽象类可能只是一个接口。这个模式是一个 “真正” 的设计模式,因为它遵循了依赖反转原则 Dependency Inversion Principle 众所周知这个 “D” 代表了真正的面向对象程序设计。它意味着工厂方法类依赖于类的抽象,而不是具体将被创建的类,这是工厂方法模式与简单工厂模式和静态工厂模式最重要的区别。1.3.2 UML图1.3.3 代码你可以在 GitHub 上找到这些代码Logger.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;interface Logger{ public function log(string $message);}StdoutLogger.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class StdoutLogger implements Logger{ public function log(string $message) { echo $message; }}FileLogger.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class FileLogger implements Logger{ /* * @var string / private $filePath; public function __construct(string $filePath) { $this->filePath = $filePath; } public function log(string $message) { file_put_contents($this->filePath, $message . PHP_EOL, FILE_APPEND); }}LoggerFactory.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;interface LoggerFactory{ public function createLogger(): Logger;}StdoutLoggerFactory.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class StdoutLoggerFactory implements LoggerFactory{ public function createLogger(): Logger { return new StdoutLogger(); }}FileLoggerFactory.php<?phpnamespace DesignPatterns\Creational\FactoryMethod;class FileLoggerFactory implements LoggerFactory{ /* * @var string / private $filePath; public function __construct(string $filePath) { $this->filePath = $filePath; } public function createLogger(): Logger { return new FileLogger($this->filePath); }}1.4 多例多例模式已经被考虑列入到反模式中!请使用依赖注入获得更好的代码可测试性和可控性!1.4.1 目的使类仅有一个命名的对象的集合可供使用,像单例模式但是有多个实例。1.4.2 例子2 个数据库连接,比如,一个连接MySQL,另一个连接SQLite多个日志记录器(一个记录调试信息,另一个记录错误信息)1.4.3 UML 图1.4.4 代码你可以在 GitHub 上找到这些代码Multiton.php<?phpnamespace DesignPatterns\Creational\Multiton;final class Multiton{ const INSTANCE_1 = ‘1’; const INSTANCE_2 = ‘2’; /* * @var Multiton[] / private static $instances = []; /* * this is private to prevent from creating arbitrary instances / private function __construct() { } public static function getInstance(string $instanceName): Multiton { if (!isset(self::$instances[$instanceName])) { self::$instances[$instanceName] = new self(); } return self::$instances[$instanceName]; } /* * prevent instance from being cloned / private function __clone() { } /* * prevent instance from being unserialized / private function __wakeup() { }}1.5 对象池1.5.1 目的对象池设计模式 是创建型设计模式,它会对新创建的对象应用一系列的初始化操作,让对象保持立即可使用的状态 - 一个存放对象的 “池子” - 而不是对对象进行一次性的的使用(创建并使用,完成之后立即销毁)。对象池的使用者会对对象池发起请求,以期望获取一个对象,并使用获取到的对象进行一系列操作,当使用者对对象的使用完成之后,使用者会将由对象池的对象创建工厂创建的对象返回给对象池,而不是用完之后销毁获取到的对象。对象池在某些情况下会带来重要的性能提升,比如耗费资源的对象初始化操作,实例化类的代价很高,但每次实例化的数量较少的情况下。对象池中将被创建的对象会在真正被使用时被提前创建,避免在使用时让使用者浪费对象创建所需的大量时间(比如在对象某些操作需要访问网络资源的情况下)从池子中取得对象的时间是可预测的,但新建一个实例所需的时间是不确定。总之,对象池会为你节省宝贵的程序执行时间,比如像数据库连接,socket连接,大量耗费资源的代表数字资源的对象,像字体或者位图。不过,在特定情况下,简单的对象创建池(没有请求外部的资源,仅仅将自身保存在内存中)或许并不会提升效率和性能,这时候,就需要使用者酌情考虑了。1.5.2 UML图1.5.3 代码你可以在 GitHub 上找到这些代码WorkerPool.php<?phpnamespace DesignPatterns\Creational\Pool;class WorkerPool implements \Countable{ /* * @var StringReverseWorker[] / private $occupiedWorkers = []; /* * @var StringReverseWorker[] / private $freeWorkers = []; public function get(): StringReverseWorker { if (count($this->freeWorkers) == 0) { $worker = new StringReverseWorker(); } else { $worker = array_pop($this->freeWorkers); } $this->occupiedWorkers[spl_object_hash($worker)] = $worker; return $worker; } public function dispose(StringReverseWorker $worker) { $key = spl_object_hash($worker); if (isset($this->occupiedWorkers[$key])) { unset($this->occupiedWorkers[$key]); $this->freeWorkers[$key] = $worker; } } public function count(): int { return count($this->occupiedWorkers) + count($this->freeWorkers); }}StringReverseWorker.php<?phpnamespace DesignPatterns\Creational\Pool;class StringReverseWorker{ /* * @var \DateTime / private $createdAt; public function __construct() { $this->createdAt = new \DateTime(); } public function run(string $text) { return strrev($text); }}1.6 原型模式1.6.1 目的通过创建一个原型对象,然后复制原型对象来避免通过标准的方式创建大量的对象产生的开销(new Foo())。1.6.2 例子大量的数据对象(比如通过ORM获取1,000,000行数据库记录然后创建每一条记录对应的对象实体)1.6.3 UML图1.6.4 代码你可以在 GitHub 上找到这些代码BookPrototype.php<?phpnamespace DesignPatterns\Creational\Prototype;abstract class BookPrototype{ /* * @var string / protected $title; /* * @var string / protected $category; abstract public function __clone(); public function getTitle(): string { return $this->title; } public function setTitle($title) { $this->title = $title; }}BarBookPrototype.php<?phpnamespace DesignPatterns\Creational\Prototype;class BarBookPrototype extends BookPrototype{ /* * @var string / protected $category = ‘Bar’; public function __clone() { }}FooBookPrototype.php<?phpnamespace DesignPatterns\Creational\Prototype;class FooBookPrototype extends BookPrototype{ /* * @var string / protected $category = ‘Foo’; public function __clone() { }}1.7 简单工厂1.7.1 目的它与静态工厂不同,因为它不是静态的。因此,可以有多个参数化的工厂,可以子类化它,也可以模拟它。它总是比静态工厂更受欢迎!1.7.2 UML图1.7.3 代码你可以在 GitHub 上找到这些代码SimpleFactory.php<?phpnamespace DesignPatterns\Creational\SimpleFactory;class SimpleFactory{ public function createBicycle(): Bicycle { return new Bicycle(); }}Bicycle.php<?phpnamespace DesignPatterns\Creational\SimpleFactory;class Bicycle{ public function driveTo(string $destination) { }}1.7.4 使用 $factory = new SimpleFactory(); $bicycle = $factory->createBicycle(); $bicycle->driveTo(‘Paris’);1.8 单例模式1.8.1 目标使应用中只存在一个对象的实例,并且使这个单实例负责所有对该对象的调用。1.8.2 例子数据库连接器日志记录器 (可能有多个实例,比如有多个日志文件因为不同的目的记录不同到的日志)应用锁文件 (理论上整个应用只有一个锁文件)1.8.3 UML图1.8.4 代码你可以在 GitHub 上找到这些代码Singleton.php<?phpnamespace DesignPatterns\Creational\Singleton;final class Singleton{ /* * @var Singleton / private static $instance; /* * gets the instance via lazy initialization (created on first usage) / public static function getInstance(): Singleton { if (null === static::$instance) { static::$instance = new static(); } return static::$instance; } /* * is not allowed to call from outside to prevent from creating multiple instances, * to use the singleton, you have to obtain the instance from Singleton::getInstance() instead / private function __construct() { } /* * prevent the instance from being cloned (which would create a second instance of it) / private function __clone() { } /* * prevent from being unserialized (which would create a second instance of it) / private function _wakeup() { }}1.9 静态工厂1.9.1 目的和抽象工厂类似,静态工厂模式用来创建一系列互相关联或依赖的对象,和抽象工厂模式不同的是静态工厂模式只用一个静态方法就解决了所有类型的对象创建,通常被命名为 Factory 或者 Generators1.9.2 例子Zend Framework: zend_cache 后端或 _Frontend 使用工厂方法创建缓存后端和前端1.9.3 UML图1.9.4 代码你可以在 GitHub 上找到这些代码StaticFactory.php<?phpnamespace DesignPatterns\Creational\StaticFactory;/* * Note1: Remember, static means global state which is evil because it can’t be mocked for tests * Note2: Cannot be subclassed or mock-upped or have multiple different instances. /final class StaticFactory{ /* * @param string $type * * @return Formatter */ public static function factory(string $type): Formatter { if ($type == ’number’) { return new FormatNumber(); } elseif ($type == ‘string’) { return new FormatString(); } throw new \InvalidArgumentException(‘Unknown format given’); }}Formatter.php<?phpnamespace DesignPatterns\Creational\StaticFactory;interface Formatter{ public function format(string $input): string;}FormatString.php<?phpnamespace DesignPatterns\Creational\StaticFactory;class FormatString implements Formatter{ public function format(string $input): string { return $input; }}FormatNumber.php<?phpnamespace DesignPatterns\Creational\StaticFactory;class FormatNumber implements Formatter{ public function format(string $input): string { return number_format($input); }} ...

January 18, 2019 · 6 min · jiezi