设计模式之适配器模式 adapter
最近看了《PHP设计模式》,觉得对自己帮助很大,所有想把该书提到的一些主要设计模式拿出来和大家分享,其中加入自己的理解,希望大家支持。
本系列文章着重介绍设计模式的思想,为了便于读者们的理解,一个设计模式的介绍开始到最后分 三步走:
-
问题与解决方案
从最浅显的需求入手讲解,更好的理解思想以及什么时候使用该设计模式。
-
UML
一目了然的看清晰框架(我也是第一次画图,不合理的地方,欢迎指正。)
-
代码示例
对于程序员来说,看代码可能比看枯燥的文字更加理解得快,所有我会用PHP语言写一个完整的简单的实例。
好了,进入正题,这一次我主要写的是 -- 适配器模式
首先,我说说我理解的需要使用适配器模式的假设:
插线板相信大家都用过,我觉得,它就是我们生活中最好的适配器写照。当然我是说的它的转换插口功能。比如家里墙壁上边的插座都是二孔插座,现在买回一个三孔电器,必须得要三孔插座才行。
遇到此情况,马上想到三种解决方法:
1.换墙壁上的插座
2.换买回来的电器三孔插口
3.使用插件板,把二孔插座转换成三孔插座
想想,哪种解决方案最优,最完美。要是你,你会怎么选择?其实在程序中,也有这样子的抉择。
一,问题与解决方案
在项目最初的代码库中,名为errorObject的对象能够处理所有的错误消息和代码。最初的编程人员并不认为自己的代码会产生任何错误,因此他们设计系统将errorObject对象的错误消息直接输出至控制台。
下面示例会生成一个“404:Not Found”错误。我们假定错误消息的内容和代码可能变化,但是以文本的方式以及记录格式始终一样。
1 //传统错误处理类 2 class errorObject{ 3 private $_error; 4 5 public function __construct($error){ 6 $this->_error = $error; 7 } 8 9 public function getError(){ 10 return $this->_error; 11 } 12 } 13 14 //将错误信息输出到控制台文件 15 class logToConsole{ 16 private $_errorObject; 17 18 public function __construct($errorObject){ 19 $this->_errorObject = $errorObject; 20 } 21 22 public function write(){ 23 $erroMsg = $this->_errorObject->getError(); 24 echo $erroMsg; 25 file_put_contents('error.log', $erroMsg); 26 } 27 } 28 29 //创建一个错误对象 30 $error = new errorObject('404: Not Found'); 31 //控制台写入日志文件 32 $log = new logToConsole($error); 33 $log->write();
在这个场景中,项目中加入了新的网络管理员。建议的最佳做法是安装用于监控软件的网络日志。网络管理员选择的软件包要求将错误日志记录至一个多列的CSV文件。具体的CSV格式要求第一列是错误值代码,第二天应当是错误文本。
选择的新软件包也可使用errorObject类,遗憾的是,版本不同。新的软件包logToCVS类如下:
1 //检测日志软件要使用 csv格式的错误日志 错误号:解释内容 2 class logToCSV{ 3 const CSV_FILE_PATH = 'error.csv'; 4 5 public function __construct($errorObject){ 6 $this->_errorObject = $errorObject; 7 } 8 9 //csv 格式 错误号:解释内容 10 public function write(){ 11 $erroMsg = $this->_errorObject->getErrorNumber(); 12 $erroMsg .= ','; 13 $erroMsg .= $this->_errorObject->getErrorText(); 14 echo $erroMsg; 15 file_put_contents(self::CSV_FILE_PATH, $erroMsg); 16 } 17 18 }
针对这个问题,我们可以采用下面两种解决方案:
1.更改现有代码库的errorObject类。
2.创建一个Adapter(适配器)对象。
考虑到保持errorObject原来标准型的需求,因此创建一个Adapter对象是最佳的解决方案。
新创建的适配器对象中必须存在现有的errorObject的功能性。此外,getErrorNum()和getErrorText()公共方法也必须有。在传统的errorObject类中,getError()方法用户获取错误消息。Adpater(适配器)对象应当利用该方法从父类中获取错误消息,然后再转化输出供给二个新公共方法使用。
1 //最好的解决方法是,创建一个 csv 的适配器类来处理 2 class logToCSVAdapter extends errorObject{ 3 private $_errorNumber; 4 private $_errorText; 5 6 public function __construct($error){ 7 parent::__construct($error); 8 $parts = explode(':', $this->getError()); 9 $this->_errorNumber = $parts[0]; 10 $this->_errorText = $parts[1]; 11 } 12 13 public function getErrorNumber(){ 14 return $this->_errorNumber; 15 } 16 17 public function getErrorText(){ 18 return $this->_errorText; 19 } 20 }
最后,为了实现这个适配器,必须通过使用该适配器替代原来的errorObject来更新代码。这样,logToCSV类就能够接受被适配器的类(而不是原来的errorObject类),从而使原来的代码能够像logToCSV类期望那样运行。
1 //适配器调用代码(实际上就是用子类替代父类,在子类中一系列处理方法) 2 $errorAdapter = new logToCSVAdapter('404:Not Found'); 3 $log = new logToCSV($errorAdapter); 4 $log->write();
二,UML
三,代码示例
1 <?php 2 //适配器模式 3 //例1,错误日志处理 4 5 //传统错误处理类 6 class errorObject{ 7 private $_error; 8 9 public function __construct($error){ 10 $this->_error = $error; 11 } 12 13 public function getError(){ 14 return $this->_error; 15 } 16 } 17 18 //将错误信息输出到控制台文件 19 class logToConsole{ 20 private $_errorObject; 21 22 public function __construct($errorObject){ 23 $this->_errorObject = $errorObject; 24 } 25 26 public function write(){ 27 $erroMsg = $this->_errorObject->getError(); 28 echo $erroMsg; 29 file_put_contents('error.log', $erroMsg); 30 } 31 } 32 33 //检测日志软件要使用 csv格式的错误日志 错误号:解释内容 34 class logToCSV{ 35 const CSV_FILE_PATH = 'error.csv'; 36 37 public function __construct($errorObject){ 38 $this->_errorObject = $errorObject; 39 } 40 41 //csv 格式 错误号:解释内容 42 public function write(){ 43 $erroMsg = $this->_errorObject->getErrorNumber(); 44 $erroMsg .= ','; 45 $erroMsg .= $this->_errorObject->getErrorText(); 46 echo $erroMsg; 47 file_put_contents(self::CSV_FILE_PATH, $erroMsg); 48 } 49 50 } 51 52 //最好的解决方法是,创建一个 csv 的适配器类来处理 53 class logToCSVAdapter extends errorObject{ 54 private $_errorNumber; 55 private $_errorText; 56 57 public function __construct($error){ 58 parent::__construct($error); 59 $parts = explode(':', $this->getError()); 60 $this->_errorNumber = $parts[0]; 61 $this->_errorText = $parts[1]; 62 } 63 64 public function getErrorNumber(){ 65 return $this->_errorNumber; 66 } 67 68 public function getErrorText(){ 69 return $this->_errorText; 70 } 71 } 72 73 //创建一个错误对象 74 $error = new errorObject('404: Not Found'); 75 //控制台写入日志文件 76 $log = new logToConsole($error); 77 $log->write(); 78 79 //适配器调用代码(实际上就是用子类替代父类,在子类中一系列处理方法) 80 $errorAdapter = new logToCSVAdapter('404:Not Found'); 81 $log = new logToCSV($errorAdapter); 82 $log->write(); 83 84
适配器模式大致思想就是这样,代码很简单,也很容易理解。不懂的童鞋可以多看几遍代码,可能更容易理解。下一篇写 -- 建造者模式。