标签 设计模式 下的文章

备忘录模式,顾名思义,记录某种数据,在需要的时候释放出来。在游戏中,存档,读档就是备忘录模式。被Boss打死后复活,数据回复到打Boss之前,也是设计模式中的备忘录模式。在但是在游戏中,角色类的功能不能带有存储旧状态数据和恢复旧状态数据的方法。把存储和读取的细节封装到一个新类中。职责分离。每个类超过一个功能,就需要考虑拆分了。这也是单一原则的体现。
备忘录模式:在不破坏封装的前提下,捕获一个对象的内部属性数据,将这个状态保存在另一个类中,以便以后的恢复数据需求。
场景:EA知名游戏模拟人生The Sims,存档和读档。以PHP为代码环境来模拟场景。

<?php
//管理‘档案器的类’的类
class Archives{
    private $memento;
    public function get(){
        return $this->memento;
    }
    public function set($mementoObj){
        return $this->memento = $mementoObj;
    }
}
class Memento{
    private $mementoData;
    public function __construct($data){
        $this->mementoData = $data;
    }
    public function get(){
        return $this->mementoData;
    }
}
class GameRole{
    private $money;
    private $name;
    public function setMoney($money){
        $this->money = $money;
    }
    public function setName($name){
        $this->name = $name;
    }
    public function getMoney(){
        return $this->money;
    }
    public function getName(){
        return $this->name;
    }
    public function setMemento(){
        $data['money'] = $this->money;
        $data['name'] = $this->name;
        return new Memento($data);
    }
    public function getMemento($mementoObj){
        $mementoData = $mementoObj->get();;
        $this->money = $mementoData['money'];
        $this->name = $mementoData['name'];
    }
    public function display(){
        echo '玩家' . $this->name . '家有家庭资产' . $this->money . '元<br>';
    }
}
//客户端/接口
//游戏开始,小明家有100元
$playObj = new GameRole();
$playObj->setName('小明');
$playObj->setMoney(100);
$playObj->display();
//存档
$archivesObj = new Archives();
$archivesObj->set($playObj->setMemento());
//小明家做生意亏损了90元
$playObj->setMoney(10);
$playObj->display();
//读档
$playObj->getMemento($archivesObj->get());
$playObj->display();

优点:彻底封装了存档和读档的细节实现,完全不对外公开。备忘录类只有玩家类能够操作。对客户端和顶层外网接口完全细节封装和数据封闭。

适配器模式,尽管是一种常见的设计模式,但是有点亡羊补牢的感觉。不是首选的设计模式。适配器模式是连接两个类的中间件,当一个类想要调用某一个类的接口时,发现尽管这个类的接口可以实现想要的功能,但是却不能用。比如因为格式的问题等等,这时候需要一个中间件来充当转换器,这就是适配器模式。
适配器模式:适配器模式(有时候也称包装样式或者包装)将一个类的接口适配成用户所期待的。一个适配允许通常因为接口不兼容而不能在一起工作的类工作在一起,做法是将类自己的接口包裹在一个已存在的类中。(本段摘自百度百科(因为想找个不是我的白话文的定义-.-))。
实际生活中,比如我们买美版、欧版的电子产品,人家很多国家时110V电压,而我们国家时220V电压,所以需要一个电源适配器,来转换电压以便能够再国内的电网环境中安全的使用。
下面的例子是一个假设,由于历史原因或者开发不规范的原因,时有些定义并不一致。比如已经离职的同事A定义了商品字符串为id|num^id|num,而另一个同事B在开发时使用的是id@num#id@num,使得第三个同事C在开发时不能直接调用A和B同事的写好的现成类的方法,需要写一个类做为中间件来转换它。使的他们兼容。开放 - 封闭原则告诉我们,不要去修改人家的类,而是以扩展的方式去改变它。因此适配器模式诞生了。以PHP为代码环境:

<?php
//id|num^id|num
class ColleagueA{
    public static function getString(){
        //读取mysql略
        return '1|1^2|1';
    }
    public static function setString($str){
        echo '写进mysql:' . $str;
    }
}
//id@num#id@num
class ColleagueB{
    public static function getString(){
        //读取mysql略
        return '1@1#2@1';
    }
    public static function setString($str){
        echo '写进mysql:' . $str;
    }
}
//适配器
class Adaptation{
    public static function changeAToB($str){
        $data = array();
        $arr = explode('^', $str);
        foreach($arr as $a){
            $data[] = explode('|', $a);
        }
        $arr = $data;
        $data = '';
        foreach($arr as $a){
            $data[] = implode('@', $a);
        }
        return implode('#', $data);
    }
    public static function changeBToA($str){
        $data = array();
        $arr = explode('#', $str);
        foreach($arr as $a){
            $data[] = explode('@', $a);
        }
        $arr = $data;
        $data = '';
        foreach($arr as $a){
            $data[] = implode('|', $a);
        }
        return implode('^', $data);
    }
}
//客户端/接口
$stringFromA = ColleagueA::getString();
$stringFromAdaptation = Adaptation::changeAToB($stringFromA);
ColleagueB::setString($stringFromAdaptation);
echo '<br>';
$stringFromB = ColleagueB::getString();
$stringFromAdaptation = Adaptation::changeBToA($stringFromB);
ColleagueA::setString($stringFromAdaptation);

Ps:严格执行开发规范,开发前该抽象的抽象,该封装的封装,比到最后没办法了用这个适配器模式强的多。
适配器模式,一种亡羊补牢的模式,食之无味,弃之可惜。

状态模式是根据状态来执行不同的功能,通常以switch和if-ifelse来逻辑判断。面向对象设计,它的目的就是希望代码能够根据责任、功能来进行分解,不再是一大长串。状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂的时候,把状态的判断转移到表示不同状态的一系列类当中,把复杂的判断逻辑简化。
状态模式:当一个对象内在的状态改变时允许改变他的行为,这个对象看起来像是改变了其类。
当一个对象运行时,该执行什么方法,是取决于它的状态的时候,我们不用臃肿的条件判断语句,而是使用状态模式。
场景:人的行为,早上吃早饭,然后走路,上班,吃午饭,上班,走路,回家吃晚饭,睡觉。用条件控制来实现,if(time()==8点){ 吃早饭 }else if()....看看GoF的状态模式如何实现人的行为。以PHP为代码环境。

<?php
class Person{
    private $state;
    private $time;
    public function __construct(){
        $this->state = new Breakfast();
    }
    public function getTime(){
        return $this->time;
    }
    public function setTime($time){
        $this->time = $time;
    }
    public function getState(){
        return $this->state;
    }
    public function setState($state){
        $this->state = $state;
    }
    public function behavior(){
        $this->state->behavior($this);
    }
}
class Breakfast{
    public function behavior($personObj){
        if($personObj->getTime() < 8){
            echo '吃早餐<br>';
        }else{
            $personObj->setState(new Walk());
            $personObj->behavior();
        }
    }
}
class Walk{
    public function behavior($personObj){
        if($personObj->getTime() < 9 || ($personObj->getTime() > 18 && $personObj->getTime() < 19)){
            echo '走路<br>';
        }else{
            if($personObj->getTime() > 9 && $personObj->getTime() < 18){
                $personObj->setState(new Work());
                $personObj->behavior();
            }else{
                $personObj->setState(new Dinner());
                $personObj->behavior();
            }
        }
    }
}
class Work{
    public function behavior($personObj){
        if($personObj->getTime() < 12 || ($personObj->getTime() > 13 && $personObj->getTime() < 18)){
            echo '工作<br>';
        }else{
            if($personObj->getTime() < 13){
                $personObj->setState(new Lunch());
                $personObj->behavior();
            }else{
                $personObj->setState(new Walk());
                $personObj->behavior();
            }
        }
    }
}
class Lunch{
    public function behavior($personObj){
        if($personObj->getTime() < 13){
            echo '吃午餐<br>';
        }else{
            $personObj->setState(new work());
            $personObj->behavior();
        }
    }
}
class Dinner{
    public function behavior($personObj){
        if($personObj->getTime() < 20){
            echo '吃晚餐<br>';
        }else{
            $personObj->setState(new Sleep());
            $personObj->behavior();
        }
    }
}
class Sleep{
    public function behavior($personObj){
        echo '睡觉<br>';
        exit;
    }
}
//客户端/接口
$personObj = new Person();
//时间表
$timeList = array(7, 8.5, 10, 12.5, 15, 18.5, 19.5);
foreach($timeList as $time){
    $personObj->setTime($time);
    $personObj->behavior();
}

抽象工厂模式,是工厂方法模式的演变,而工厂方法模式,是简单工厂模式的进化。抛弃了应用的条件控制语句,无论是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();
?>