2014年4月

抽象工厂模式,是工厂方法模式的演变,而工厂方法模式,是简单工厂模式的进化。抛弃了应用的条件控制语句,无论是switch还是if-ifelse。是设计模式的一种。
抽线工厂模式来自于方法模式和简单工厂模式的进化与整合,其实,我已经要疯了,23种设计模式,现在已经出现了三种工厂模式。
抽象工厂模式:提供一个创建一系列相关的、相互依赖的对象接口,而无需指定他们的具体类。
对于面向过程的编程,以及套在class里的面向对象的编程,修改起来是大批量的,是非常丑陋的。我常常告诫自己,编程是一门艺术,每个程序员都是艺术家,写出优美的,有艺术感的代码,并且是高效的,低成本的,这就是编程之美!
场景:原本是mysql,现在要换成oracle。以PHP为代码环境。现在有两张表,一个是用户表user,一个是公司表company。

<?php
class Db{
    private static $dbName = 'mysql';
    public static function createUserDbObj(){
        $className = self::$dbName . 'UserDbModel';
        return new $className();
    }
    public static function createCompanyDbObj(){
        $className = self::$dbName . 'CompanyDbModel';
        return new $className();
    }
}
interface User{
    public function get();
    public function set();
}
class mysqlUserDbModel implements User{
    public function get(){
        echo '从Mysql中查找用户记录<br>';
    }
    public function set(){
        echo '从Mysql中添加用户记录<br>';
    }
}
class oracleUserDbModel implements User{
    public function get(){
        echo '从Oracle中查找用户记录<br>';
    }
    public function set(){
        echo '从Oracle中添加用户记录<br>';
    }
}
interface Company{
    public function get();
    public function set();
}
class mysqlCompanyDbModel implements User{
    public function get(){
        echo '从Mysql中查找公司记录<br>';
    }
    public function set(){
        echo '从Mysql中添加公司记录<br>';
    }
}
class oracleCompanyDbModel implements User{
    public function get(){
        echo '从Oracle中查找公司记录<br>';
    }
    public function set(){
        echo '从Oracle中添加公司记录<br>';
    }
}
//客户端/接口
$userDbObj = Db::createUserDbObj();
$companyDbObj = Db::createCompanyDbObj();
$userDbObj->get();
$userDbObj->set();
$companyDbObj->get();
$companyDbObj->set();

现在代码的方式,是把选择数据库给写死到程序中了(Db类), 我们可以以更加灵活的方式,比如:
1、配置

config.php
<?php
define('DB_NAME', 'mysql');

2、文件

$f = fopen('config', 'r');
$config = '';
while(!feof($f)){
    $config .= fgets($f);
    $config .= ' ';
}
//----------------我是分割线-----------
$config = file_get_contents('config');
//-------------------------------------------
//$config = 'dbtype:mysql|username:root|password:root';
$config = explode('|', $config);
foreach($config as $k=>$c){
    $data = explode(':', $c);
    unset($config[$k]);
    $config[$data[0]] = $data[1];
}
print_r($config);

个人认为:这种方式的数据库应用代码设计,仍旧是很繁琐的,每增加一张表,需要增加各个类型的数据库类各一个。所以,本例仅仅是为了演示说明抽象工厂模式。

观察者模式,又叫做订阅-发布模式。当一个对象的改变需要同时改变多个对象的时候,可以使用法不这模式。设计模式中的观察者模式,就是为了解除类之间的耦合,使双方都依赖于抽象而不是依赖于具体。在实际生活中,比如我们更换了手机号,需要通知大家的时候,我们就是主题,或者通知者,而需要通知的人就是观察者列表,一条短信的群发告诉大家,就是观察者模式的应用。

<?php
//主题者、通知者抽象类。
abstract class Subject{
	private $observerList = array();
	public function add(&$obj, $action){
		$this->observerList[] = array('obj'=>$obj, 'action'=>$action);
	}
	public function notice(){
		foreach($this->observerList as $observer){
			$obj = $observer['obj'];
			$action = $observer['action'];
			$obj->$action;
		}
	}
}
//具体的通知者
class I extends Subject{
	private $status;
	public function getStatus(){
		return $this->status;
	}
	public function setStatus($status){
		$this->status = $status;
	}
}
//观者者抽象类
abstract class Observer{
	public function sendSms(){
	
	}
}
//具体的观察者
class ConcreteObserver extends Observer{
	private $name;
	private $status;
	private $objSubject;
	public function __construct($name, $objSubject){
		$this->name = $name;
		$this->objSubject = $objSubject;
	}
	public function sendSms(){
		$status = $this->objSubject->getStatus();
		echo '观察者'.$this->name.'收到的状态是'.$status.'<br>';
	}
}
//客户端/接口
$i = new I();
$i->setStatus('更换手机号码了。');
$friend1 = new ConcreteObserver('小明', $i);
$friend2 = new ConcreteObserver('小红', $i);
$friend3 = new ConcreteObserver('小黄', $i);
$i->add($friend1, 'sendSms');
$i->add($friend2, 'sendSms');
$i->add($friend3, 'sendSms');
$i->notice();
?>

建造者模式,也叫生成器模式。是设计模式的一种。某个复杂算法类,在方法调用上是顺序稳定的,但是具体属性不同,此时可以使用建造者模式。
建造者模式:一个复杂的对象,我们把它的构造和它的表示分离,可以实现同样的构造,而产生多种不同的表示,这种设计模式我们把它叫做建造者模式,也被成为生成器模式。顾名思义,在一个厂房中批量生成。
在定义和开发时,必须要满足:1、我们开发的类,允许被它的对象有多种不同的表示。2、当创建复杂对象的算法,应该独立于该对象的组成部分和该对象的装配方式。
在建造者模式这一的设计模式种,第一个类builder是各种创建方法的抽象接口。ConcreteBuilder调用Builder的接口来装配。提供对外的接口。ProductA是A产品类,调用ConcreteBuilder实现了具体的产品A的实现方法,也就是需要被构造的那个复杂的对象。Director就是我们的向导类,根据客户的需求生成产品A、产品B、产品C。
场景:麦当劳,汉堡和批萨,收银员就是向导类。以PHP为代码环境。

<?php
//麦当劳,抽象接口类
interface McDonald{
    public function yuanLiao();
    public function nieXingZhuang();
    public function jiaRe();
}
//汉堡,就是产品A类
class Hamburger implements McDonald{
    public function yuanLiao(){
        echo '采购原料:面+肉+生菜+酱';
        $this->separate();
    }
    public function nieXingZhuang(){
        echo '捏成蓬松的圆球形状';
        $this->separate();
    }
    public function jiaRe(){
        echo '加热10分种';
        $this->separate();
    }
    private function separate(){
        echo '<br>';
    }
}
//薯条,就是产品B类
class FrenchFries implements McDonald{
    public function yuanLiao(){
        echo '采购原料:土豆';
        $this->separate();
    }
    public function nieXingZhuang(){
        echo '切成细长条';
        $this->separate();
    }
    public function jiaRe(){
        echo '加热15分种';
        $this->separate();
    }
    private function separate(){
        echo '<br>';
    }
}
//收银员,就是向导类
class Cashier{
    public function createProduct($productObj){
        $productObj->yuanLiao();
        $productObj->nieXingZhuang();
        $productObj->jiaRe();
        return '制作完成,可以上桌了';
    }
}
//客户端/接口
$cashier = new Cashier();
$cashier->createProduct(new Hamburger());
$cashier->createProduct(new FrenchFries());
?>

外观模式其实非常容易用到,是对迪米特法则的一种应用:降低类的耦合度,添加中间件。也是对依赖倒转原则的完美体现:针对接口的编程。
外观模式:再次针对某个接口封装一个高层类,实现一个高层接口,按某种算法或使用方式整合底层接口类,使得底层的接口更加容易使用,也降低了底层接口和客户端的耦合度。
场景:调用数据库。以PHP为代码环境,以Mysql为数据库环境。

<?php
class MysqlDB{
    private $conn;
    public function __construct($host, $username, $password, $dbName){
        $this->conn($host, $username, $password);
        $this->selectDb($dbName);
    }
    private function conn($host, $username, $password){
        $this->conn = mysql_connect($host, $username, $password);
    }
    private function selectDb($dbName){
        mysql_select_db($dbName, $this->conn);
    }
    public function query($sql){
        return mysql_query($sql);
    }
    public function fetchArray($queryResult){
        return mysql_fetch_array($queryResult);
    }
	public function fetchAssoc($queryResult){
        return mysql_fetch_assoc($queryResutl);
    }
}
class Facade{
    private $mysqlObj;
	public function __construct($host, $username, $password, $dbName){
        $this->mysqlObj = new MysqlDB($host, $username, $password, $dbName);
    }
    public function get($tableName){
        $sql = 'SELECT * FROM ' . $tableName;
        $queryResult = $this->mysqlObj->query($sql);
        $fetchArr = $this->myqlObj->fetchAssoc($queryResult);
		return $fetchArr
    }
}
//客户端/接口
$obj = new Facade('localhost', 'root', 'root', 'db_name');
$list = $obj->get('user_info');
?>

常见的使用场景:
1、开发的初期阶段,有意识的建立中间件,将不同的两层分离,在层与层之间建立外观。
2、在开发阶段,某个类会根据需求的不断变更等原因使类变得更加复杂而庞大,增加一个外观类,使的使用者和这个庞大负责的类耦合降低。
3、历史遗留问题。需要用到遗留的复杂逻辑的类,直接调用是不好的,所以需要一个中间件(外观模式的外观类)来调用这个复杂类,而使用者调用外观类即可。
可以理解为,外观模式的外观类,是一个入口,使用者调用外观类,外观类调用底层的类。

面向对象的特性之一:封装。不需要知道具体如何实现的细节,只需要调用某个类的方法,得到预期的结果。尽可能少的使用public,降低成员的访问权限。可以更好降低类与类之间的耦合度。程序设计时,修改一个越弱耦合的类,对系统造成的影响就会越小,耦合度越低,越利于复用。这就是迪米特法则的根本思想。
依赖接口而不是依赖实现,在弱耦合、低权限的基础上,完全不需要关心接口的实现细节,这也就是依赖倒转原则。面向对象的原则和面向对象的特性是不对立的。
迪米特法则:如果两个类,不需要直接进行两个类之间的通信,那么,这两个类就不应该直接发生作用和求情,如果一个类在特定条件下需要调用另一个类,那么,可以通过第三个类来实现,转发这个调用。
是不是又用点像代理模式?代理模式是针对对象的,代理类实例化真实类,调用真实类的方法。而迪米特法则是一个类调用另一个类,然后这个另一个类再调用另另一个类。