搜索
首页后端开发php教程关于PHP中外观模式facade pattern的解析

关于PHP中外观模式facade pattern的解析

Jun 13, 2018 pm 02:35 PM
facadepatternphp外观模式

这篇文章主要介绍了详解PHP中的外观模式facade pattern的详细用法以及代码实例,对此有需求的朋友参考下吧。

关于facade这个词的翻译

facade这个词,原意指的是一个建筑物的表面、外观,在建筑学中被翻译为“立面”这个术语,国内对facade这个词的关注,可能更多要依赖于laravel的流行,似乎都一致把laravel里的facade翻译作“门面”。说实在的,当第一次看到翻译文档里提什么“门面”的时候,我想你跟我的内心一样:“这是在说什么玩意呢?你是在讲商店、店铺的门面吗?”直到现在,如果非得用中文说facade,非得用“门面”这个词,我的心里还是不自觉地会“咯噔”那么一下,我知道这里是有问题的。

facade到底翻译作啥好呢?倒是也有的人群干脆提倡不翻译,遇到它就直接英文单词拿过来,这也不是个长远办法,终归是要为了新入门的人铺平理解的道路才好。后来偶然看到台湾的学者,确切说是台湾的维基百科,将facade pattern译作“外观模式”,考虑到该模式的实际作用,方才感觉瞬间释然。即使laravel里的facade,严格上并不是facade pattern,很多人到现在依然在批评laravel在facade这个词语上的滥用和误导,但它终归也是在借用或模仿facade pattern,所以laravel里的facade,本文也认为同样翻译成“外观”比较好,当然,为了更好理解,可以是“服务外观”。即使如此,从私人角度,我更希望将其直呼为“服务定位器”、“服务代理”或者“服务别名”,实际上国外的很多人也是建议如此更名,只是Taylor在这件事上态度一反往常地强硬,所以也暂且不必强求。

通过下文,待实际了解了facade pattern具体是啥后,我想你会更好地理解为什么翻译为“外观模式”更贴切。

什么是facade pattern(“外观模式”的定义)

不论在现实世界还是编程世界,facade(外观)的目的就是给一个可能原本丑的、杂乱的东西,“披上”一个优美的、吸引人的外观、或者说面具,用中国的俗话就是:什么是外观?“人靠衣装马靠鞍”。基于此,facade pattern就是将一个或多个杂乱的、复杂的、不容易重构的class,添加上(或转换成)一个漂亮优雅的对接入口(interface),这样呢好让你更乐意、更方便地去操作它,从而间接地操作了背后的实际逻辑。

什么时候需要用facade pattern

facade pattern(“外观模式”)经常是用来给一个或多个子系统,来提供统一的入口界面(interface),或者说操作界面。
当你需要操作别人遗留下来的项目,或者说第三方的代码的时候。尤其是通常情况下,这些代码你不容易去重构它们,也没有提供测试(tests)。这个时候,你就可以创建一个facade(“外观”),去将原来的代码“包裹”起来,以此来简化或优化其使用场景。

说得再多,不如来几个例子直观:

示例一:在java中,通过facade操作计算机内部复杂的系统信息

假设我们有这么一些复杂的子系统逻辑:

class CPU {
 public void freeze() { ... }
 public void jump(long position) { ... }
 public void execute() { ... }
}
class Memory {
 public void load(long position, byte[] data) {
  ...
 }
}
class HardDrive {
 public byte[] read(long lba, int size) {
  ...
 }
}

为了更方便地操作它们,我们可以来创建一个外观类(facade):

class Computer {
 public void startComputer() {
  cpu.freeze();
  memory.load(BOOT_ADDRESS, hardDrive.read(BOOT_SECTOR, SECTOR_SIZE));
  cpu.jump(BOOT_ADDRESS);
  cpu.execute();
 }
}

然后我们的客户,就可以很方便地来这样调用了:

class You {
 public static void main(String[] args) {
  Computer facade = new Computer();
  facade.startComputer();
 }
}

示例二:一个糟糕的第三方邮件类

假设你不得不用下面这个看上去很糟糕的第三方邮件类,尤其是里面每个方法名你都得停留个好几秒才能看懂:

interface SendMailInterface
{
 public function setSendToEmailAddress($emailAddress);
 public function setSubjectName($subject);
 public function setTheEmailContents($body);
 public function setTheHeaders($headers);
 public function getTheHeaders();
 public function getTheHeadersText();
 public function sendTheEmailNow();
}
class SendMail implements SendMailInterface
{
 public $to, $subject, $body;
 public $headers = array();
 
 public function setSendToEmailAddress($emailAddress)
 {
  $this->to = $emailAddress;
 }
 public function setSubjectName($subject)
 {
  $this->subject = $subject;
 }
 public function setTheEmailContents($body)
 {
  $this->body = $body;
 }
 public function setTheHeaders($headers)
 {
  $this->headers = $headers;
 }
 public function getTheHeaders()
 {
  return $this->headers;
 }
 public function getTheHeadersText()
 {
  $headers = "";
  foreach ($this->getTheHeaders() as $header) {
   $headers .= $header . "\r\n";
  }
 }
 
 public function sendTheEmailNow()
 {
  mail($this->to, $this->subject, $this->body, $this->getTheHeadersText());
 }
}

这个时候你又不好直接改源码,没办法,来一个facade吧

class SendMailFacade
{
 private $sendMail;
 public function __construct(SendMailInterface $sendMail)
 {
  $this->sendMail = $sendMail;
 }
 public function setTo($to)
 {
  $this->sendMail->setSendToEmailAddress($to);
  return $this;
 }
 public function setSubject($subject)
 {
  $this->sendMail->setSubjectName($subject);
  return $this;
 }
 public function setBody($body)
 {
  $this->sendMail->setTheEmailContents($body);
  return $this;
 }
 public function setHeaders($headers)
 {
  $this->sendMail->setTheHeaders($headers);
  return $this;
 }
 public function send()
 {
  $this->sendMail->sendTheEmailNow();
 }
}

然后原来不加优化的终端调用可能是这样的:

$sendMail = new SendMail();
$sendMail->setSendToEmailAddress($to);
$sendMail->setSubjectName($subject);
$sendMail->setTheEmailContents($body);
$sendMail->setTheHeaders($headers);
$sendMail->sendTheEmailNow();

现在有了外观类,就可以这样了:

$sendMail  = new SendMail();
$sendMailFacade = new sendMailFacade($sendMail);
$sendMailFacade->setTo($to)->setSubject($subject)->setBody($body)->setHeaders($headers)->send();

示例三:完成一个商品交易的复杂流程

假设呢,一个商品交易环节需要有这么几步:

$productID = $_GET['productId']; 
$qtyCheck = new productQty();

 // 检查库存
if($qtyCheck->checkQty($productID) > 0) {
  
 // 添加商品到购物车
 $addToCart = new addToCart($productID);
  
 // 计算运费
 $shipping = new shippingCharge();
 $shipping->updateCharge();
  
 // 计算打折
 $discount = new discount();
 $discount->applyDiscount();
  
 $order = new order();
 $order->generateOrder();
}

可以看到,一个流程呢包含了很多步骤,涉及到了很多Object,一旦类似环节要用在多个地方,可能就会导致问题,所以可以先创建一个外观类:

class productOrderFacade {
 public $productID = '';  
 public function __construct($pID) {
  $this->productID = $pID;
 }
 public function generateOrder() {   
  if($this->qtyCheck()) {
   $this->addToCart();
   $this->calulateShipping();
   $this->applyDiscount();
   $this->placeOrder();
  }   
 }
 private function addToCart () {
  /* .. add product to cart .. */
 } 
 private function qtyCheck() {
  $qty = 'get product quantity from database';
  if($qty > 0) {
   return true;
  } else {
   return true;
  }
 }
  private function calulateShipping() {
  $shipping = new shippingCharge();
  $shipping->calculateCharge();
 }
 private function applyDiscount() {
  $discount = new discount();
  $discount->applyDiscount();
 }
 private function placeOrder() {
  $order = new order();
  $order->generateOrder();
 }
}

这样呢,我们的终端调用就可以两行解决:

$order = new productOrderFacade($productID);
$order->generateOrder();

示例四:往多个社交媒体同步消息的流程

// 发Twitter消息
class CodeTwit {
 function tweet($status, $url)
 {
 var_dump('Tweeted:'.$status.' from:'.$url);
 }
}
// 分享到Google plus上
class Googlize {
 function share($url)
 {
 var_dump('Shared on Google plus:'.$url);
 }
}
//分享到Reddit上
class Reddiator {
 function reddit($url, $title)
 {
 var_dump('Reddit! url:'.$url.' title:'.$title);
 }
}

如果每次我们写了一篇文章,想着转发到其他平台,都得分别去调用相应方法,这工作量就太大了,后期平台数量往往只增不减呢。这个时候借助于facade class:

class shareFacade {
 
 protected $twitter; 
 protected $google; 
 protected $reddit; 
 function __construct($twitterObj,$gooleObj,$redditObj)
 {
 $this->twitter = $twitterObj;
 $this->google = $gooleObj;
 $this->reddit = $redditObj;
 } 
 function share($url,$title,$status)
 {
 $this->twitter->tweet($status, $url);
 $this->google->share($url);
 $this->reddit->reddit($url, $title);
 }
}

这样终端调用就可以:

$shareObj = new shareFacade($twitterObj,$gooleObj,$redditObj);
$shareObj->share('//myBlog.com/post-awsome','My greatest post','Read my greatest post ever.');

facade pattern的优劣势

优势

能够使你的终端调用与背后的子系统逻辑解耦,这往往发生在你的controller里,就意味着你的controller可以有更少的依赖,controller关注的更少了,从而责任和逻辑也更明确了,同时也意味着你子系统里的逻辑更改,并不会影响到你的controller里终端调用。

劣势

虽然特别有用,但是一个常见的陷阱就是,过度使用这个模式,明明可能那个时候你并不需要,这个往往注意即可。当然也有人争论说,明明我原来的代码都能用,干嘛费这个劲,那么同样是房子,你是喜欢住在精致的屋子里呢,还是说有四面墙就行了呢?

感觉facade pattern与其他的设计模式似曾相识?

认真学过我们《Laravel底层核心技术实战揭秘》这一课程的同学,可能到这里就会尤其觉得这个facade pattern好像在哪里见过?可能你会脱口而出:“这货跟之前咱们学的decorator pattern有啥区别呢?为啥不直接说成修饰者模式呢?”

确实,在“包装”逻辑方面,它们确实类似,但是:

修饰者模式(Decorator)——用来给一个Object添加、包裹上新的行为、逻辑,而不需要改动原来的代码

外观模式(facade pattern)——用来给一个或多个复杂的子系统、或者第三方库,提供统一的入口,或者说统一的终端调用方式

还是有一定差别的~

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

学习laravel的模型事件的几种用法

关于PHP的Laravel框架中使用消息队列queue及异步队列的方法分析

Laravel 5框架的模型和控制器以及视图基础流程的学习

以上是关于PHP中外观模式facade pattern的解析的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
可以在PHP会话中存储哪些数据?可以在PHP会话中存储哪些数据?May 02, 2025 am 12:17 AM

phpsessionscanStorestrings,数字,数组和原始物。

您如何开始PHP会话?您如何开始PHP会话?May 02, 2025 am 12:16 AM

tostartaphpsession,usesesses_start()attheScript'Sbeginning.1)placeitbeforeanyOutputtosetThesessionCookie.2)useSessionsforuserDatalikeloginstatusorshoppingcarts.3)regenerateSessiveIdStopreventFentfixationAttacks.s.4)考虑使用AttActAcks.s.s.4)

什么是会话再生,如何提高安全性?什么是会话再生,如何提高安全性?May 02, 2025 am 12:15 AM

会话再生是指在用户进行敏感操作时生成新会话ID并使旧ID失效,以防会话固定攻击。实现步骤包括:1.检测敏感操作,2.生成新会话ID,3.销毁旧会话ID,4.更新用户端会话信息。

使用PHP会话时有哪些性能考虑?使用PHP会话时有哪些性能考虑?May 02, 2025 am 12:11 AM

PHP会话对应用性能有显着影响。优化方法包括:1.使用数据库存储会话数据,提升响应速度;2.减少会话数据使用,只存储必要信息;3.采用非阻塞会话处理器,提高并发能力;4.调整会话过期时间,平衡用户体验和服务器负担;5.使用持久会话,减少数据读写次数。

PHP会话与Cookie有何不同?PHP会话与Cookie有何不同?May 02, 2025 am 12:03 AM

PHPsessionsareserver-side,whilecookiesareclient-side.1)Sessionsstoredataontheserver,aremoresecure,andhandlelargerdata.2)Cookiesstoredataontheclient,arelesssecure,andlimitedinsize.Usesessionsforsensitivedataandcookiesfornon-sensitive,client-sidedata.

PHP如何识别用户的会话?PHP如何识别用户的会话?May 01, 2025 am 12:23 AM

phpientifiesauser'ssessionusessessionSessionCookiesAndSessionIds.1)whiwSession_start()被称为,phpgeneratesainiquesesesessionIdStoredInacookInAcookInamedInAcienamedphpsessidontheuser'sbrowser'sbrowser.2)thisIdAllowSphptptpptpptpptpptortoreTessessionDataAfromtheserverMtheserver。

确保PHP会议的一些最佳实践是什么?确保PHP会议的一些最佳实践是什么?May 01, 2025 am 12:22 AM

PHP会话的安全可以通过以下措施实现:1.使用session_regenerate_id()在用户登录或重要操作时重新生成会话ID。2.通过HTTPS协议加密传输会话ID。3.使用session_save_path()指定安全目录存储会话数据,并正确设置权限。

PHP会话文件默认存储在哪里?PHP会话文件默认存储在哪里?May 01, 2025 am 12:15 AM

phpsessionFilesArestoredIntheDirectorySpecifiedBysession.save_path,通常是/tmponunix-likesystemsorc:\ windows \ windows \ temponwindows.tocustomizethis:tocustomizEthis:1)useession_save_save_save_path_path()

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

SecLists

SecLists

SecLists是最终安全测试人员的伙伴。它是一个包含各种类型列表的集合,这些列表在安全评估过程中经常使用,都在一个地方。SecLists通过方便地提供安全测试人员可能需要的所有列表,帮助提高安全测试的效率和生产力。列表类型包括用户名、密码、URL、模糊测试有效载荷、敏感数据模式、Web shell等等。测试人员只需将此存储库拉到新的测试机上,他就可以访问到所需的每种类型的列表。

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具