题外话:前几天到一家公司面试php职位,来面试我的是一位java工程师,囧。随后他随便问了几个php和java共有的名词,要我解释。当时我就渣了。其中一个就是让我描述设计模式都有哪些。虽然我很久之前隐约学习过,奈何时间久远,脑子里早就自动缓存失效。最近整理印象笔记,把这个问题在cnblogs上简单记录一下。
首先,推荐一本书《设计模式》,本人已在网上找到电子版资料,还没开始阅读,欢迎阅读过的小伙伴前来发表阅读感想!
网上很多资料都是从五个设计模式做的介绍,我的笔记同样如此。包括‘工厂模式’、‘单例模式/单元素模式’、‘观察者模式’、‘命令链模式‘、‘策略模式’ ,下面我们开始一一介绍。
一、工厂模式
概念理解:工厂模式是一种具有创建对象的某种方法的类。我们可以使用工厂类直接创建对象,new的过程由工厂完成。如果我们需要修改所创建的对象类型,只需要更改工厂即可,调用工厂的所有代码都会自动更改。
优点:工厂模式的优点在于创建对象。就是把创建对象的过程封装起来,每次调用工厂的某个方法就自动创建了对象。
设计思想:根据不同参数生成不同类实例。
工厂模式划分:根据抽象程度的不同,可以分为 简单工厂模式、工厂方法模式和抽象工厂模式。下面看一下demo.
*简单工厂模式
<?php /** * 简单工厂模式又叫静态工厂方法模式,理解为可以通过一个静态方法创建对象 * */ interface animal{ function eat(); } class herbivore implements animal{ function eat(){ echo "吃草<br>"; } } class carnivore implements animal{ function eat(){ echo "吃肉<br>"; } } class omnivores implements animal{ function eat(){ echo "杂食<br>"; } } class Demo{ static function createHerbivore(){ return new herbivore; } static function createCarnivore(){ return new carnivore; } static function createOmnivores(){ return new omnivores; } } $herbivore = Demo::createHerbivore(); $herbivore->eat(); $carnivore = Demo::createCarnivore(); $carnivore->eat(); $omnivores = Demo::createOmnivores(); $omnivores->eat();
*工厂方法模式
<?php /** * 工厂模式 定义一个创建对象的接口,让子类决定哪个类实例化,解决简单工厂模式中封闭开发原则 * */ interface animal{ function eat(); } class herbivore implements animal{ function eat(){ echo "吃草<br>"; } } class carnivore implements animal{ function eat(){ echo "吃肉<br>"; } } class omnivores implements animal{ function eat(){ echo "杂食<br>"; } } //将对象的创建抽象成一个接口 interface createAnimal{ function create(); } class FactoryHerbivore implements createAnimal{ function create(){ return new herbivore; } } class FactoryCarnivore implements createAnimal{ function create(){ return new carnivore; } } class FactoryOmnivores implements createAnimal{ function create(){ return new omnivores; } } class Demo{ function test(){ $Factory = new FactoryHerbivore(); $herbivore = $Factory->create(); $herbivore->eat(); $Factory = new FactoryCarnivore(); $carnivore = $Factory->create(); $carnivore->eat(); $Factory = new FactoryOmnivores(); $omnivores = $Factory->create(); $omnivores->eat(); } } $demo = new Demo(); $demo->test();
*抽象工厂方法
<?php /** * 抽象工厂 提供一个创建一系列相关或相互依赖对象的接口 * 与工厂方法的区别,这里定义了一系列接口,工厂方法只有一个 * * */ interface animal{ function eat(); } class yesHerbivore implements animal{ function eat(){ echo "吃草<br>"; } } class noHerbivore implements animal{ function eat(){ echo "不吃草<br>"; } } class yesCarnivore implements animal{ function eat(){ echo "吃肉<br>"; } } class noCarnivore implements animal{ function eat(){ echo "不吃肉<br>"; } } class yesOmnivores implements animal{ function eat(){ echo "杂食<br>"; } } class noOmnivores implements animal{ function eat(){ echo "不杂食<br>"; } } //本质区别在这里,将对象的创建抽象成一个接口 interface createAnimal{ function createYes(); function createNo(); } class FactoryHerbivore implements createAnimal{ function createYes(){ return new yesHerbivore(); } function createNo(){ return new noHerbivore(); } } class FactoryCarnivore implements createAnimal{ function createYes(){ return new yesCarnivore(); } function createNo(){ return new noCarnivore(); } } class FactoryOmnivores implements createAnimal{ function createYes(){ return new yesOmnivores(); } function createNo(){ return new noOmnivores(); } } class Demo{ function test(){ $Factory = new FactoryHerbivore(); $herbivore = $Factory->createYes(); $herbivore->eat(); $herbivore = $Factory->createNo(); $herbivore->eat(); $Factory = new FactoryCarnivore(); $carnivore = $Factory->createYes(); $carnivore->eat(); $carnivore = $Factory->createNo(); $carnivore->eat(); $Factory = new FactoryOmnivores(); $omnivores = $Factory->createYes(); $omnivores->eat(); $omnivores = $Factory->createNo(); $omnivores->eat(); } } $demo = new Demo(); $demo->test();
啊咧,为什么我觉得工厂方法模式和抽象工厂方法模式一样呢。。。
适用范围:
简单工厂模式:工厂类负责创建的对象较少,客户只知道传入工厂类的参数,对于如何创建对象不关心。
工厂方法模式:当一个类不知道它所必须创建对象的类或一个类希望由子类来指定它所创建的对象时,当类将创建对象的职责委托给多个帮助子类中得某一个,并且你希望将哪一个帮助子类是代理者这一信息局部化的时候,可以使用工厂方法模式。
抽象工厂模式:一个系统不应当依赖于产品类实例何如被创建,组合和表达的细节,这对于所有形态的工厂模式都是重要的。这个系统有多于一个的产品族,而系统只消费其 中某一产品族。同属于同一个产品族的产品是在一起使用的,这一约束必须在系统的设计中体现出来。系统提供一个产品类的库,所有的产品以同样的接口出现,从 而使客户端不依赖于实现。
无论是简单工厂模式、工厂模式还是抽象工厂模式,它们本质上都是将不变的部分提取出来,将可变的部分留作接口,以达到最大程度上的复用。究竟用哪种设计模式更适合,这要根据具体的业务需求来决定。
再简单一些理解一下,在Thinkphp框架中的Db.class.php就是工厂类,可以操作mysql,oracle等各数据库,以中间层的姿态,屏蔽掉具体的实现,让程序员不改动原来的查询代码。
总之,工厂模式主要是为创建对象提供过渡接口,以便将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的。
推荐阅读:
二、单例/单元素模式
概念理解:单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。我们最常见的应该就是数据库的操作,我们只需要实例化一次,就不需要每次都去new,这样极大的降低了资源的消耗。
其他情况,比如线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序等也都被设计成单例。
单例的特点:只有一个实例,必须自行创建,必须给其他对象提供这一实例。
使用优点:比如在大量数据库操作的场景下,使用单例模式避免频繁大量的new操作,节约了大量的内存和系统的资源。
单例模式的划分:懒汉式单例、饿汉式单例、登记式单例。
<?php class Mysql{ //该属性用来保存实例 private static $conn; //构造函数为private,防止创建对象 private function __construct(){ $this->conn = mysql_connect(‘localhost‘,‘root‘,‘‘); } //创建一个用来实例化对象的方法 public static function getInstance(){ if(!(self::$conn instanceof self)){ self::$conn = new self; } return self::$conn; } //防止对象被复制 public function __clone(){ trigger_error(‘Clone is not allowed !‘); } } //只能这样取得实例,不能new 和 clone $mysql = Mysql::getInstance();
推荐阅读:
三、观察者模式
理解:观察者和主题(被观察者)都是用接口。观察者利用主题的接口向主题注册,主题利用观察者的接口通知观察者。观察者和主题之间的关系并非通过继承产生,而是在运行时利用组合的方式产生。
下面我们通过代码来了解观察者模式的具体实现。
//抽象主题 abstract class Subject{ private $observers = array();//观察者队列 public function Attach(Observer $observer) { //观察者对象向主题注册 array_push($this->observers, $observer); } public function Detach(Observer $observer) { //观察者对象从主题注销 遍历查找 foreach ($this->observers as $k=>$v) { if($v==$observer) { unset($this->observers[$k]); } } } function Notify(){ //主题利用观察者的接口通知观察者 以遍历的方式 foreach ($this->observers as $v) { $v->Update(); } } } //抽象观察者 abstract class Observer{ public abstract function Update();//更新操作 } //具体主题 class concreteSubject extends Subject { public $subject_state; } //具体观察者 class ConcreteObserver extends Observer { private $name; private $observerState; public $subject; public function __construct(ConcreteObserver $_sub,$_name) { $this->subject = $_sub; $this->name = $_name; } public function Update() { $this->observerState = $this->subject->subject_state; echo "观察者".$this->name."的新状态是:".$this->observerState."<br/>"; } }
*实现代码* $_s = new concreteSubject();//具体主题对象 $testA = new ConcreteObserver($_s, "小A");//具体观察者对象 $testB = new ConcreteObserver($_s, "小B");//具体观察者对象 $_s->Attach($testA);//将观察者对象通过主题对象注册 $_s->Attach($testB); $_s->subject_state ="状态修改通知"; $_s->Notify();//通知发送
由上我们总结如下:
抽象主题:注册观察者对象,并定义了通知的接口规则
抽象观察者:定义了接到通知后所做的操作接口规则
具体主题:实现抽象主题的接口,接到状态改变则通知观察者
具体观察者:实现具体方法
观察者模式的使用场景:
一个抽象模型有两方面,一方面依赖于另一方面,当对一个对象的改变需要同时改变其他对象,而不需要知道具体有多少对象带改变,松耦合。
缺点:有可能会引起意外的更新。
推荐阅读:
PHP设计模式之观察者模式(Observer)详细介绍和代码实例
四、命令链模式
概念:命令链模式以松散耦合主题为基础,发送消息、命令和请求,或通过一组处理程序发送任意内容,每个处理程序都会自行判断自己能否处理请求,如果可以,该请求被处理,进程停止。您可以为系统添加或移除处理程序而不影响其他处理程序。
使用场景:可以使用在用户登录注册时处理不同角色用户的业务逻辑与变量值。
如下代码:
interface ICommand { function onCommand($name,$args); } class UserCommand implements ICommand { public function onCommand($name,$args) { if($name !=‘addUser‘) return false; echo "UserCommand handling ‘addUser‘ <br>"; return true; } } class MailCommand implements ICommand { public function onCommand($name,$args) { if($name !=‘mail‘) return false; echo "UserCommand handling ‘mail‘ <br>"; return true; } } class CommandChain { private $_commands = array(); public function addCommand($cmd){ $this->_commands[] = $cmd; } public function runCommand($name,$args){ foreach ($this->_commands as $cmd){ if($cmd->onCommand($name,$args)) return; } } } $test = new CommandChain(); $test->addCommand(new UserCommand()); $test->addCommand(new MailCommand()); $test->runCommand(‘addUser‘, null); $test->runCommand(‘mail‘, null);
推荐阅读:
五、策略模式
概念:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,此模式让算法的变化独立于使用算法的客户,从而让程序更灵活,具有更好的扩展性和维护性。
设计原则:
找出应用中可能需要变化之处,把它们独立出来,不要和不需要的代码混在一起。针对接口编程,不针对实现编程。多用组合,少用继承。
下面看demo
abstract class Person{ abstract function show(); } class minority extends Person { public function show(){ echo "未成年,二折优惠<br>"; } } class adult extends Person { public function show(){ echo "成年,五折优惠<br>"; } } class elder extends Person { public function show(){ echo "老年,免费<br>"; } } class Check { static function getResult($person){ $person = new $person; $person->show(); } } Check::getResult(‘minority‘); Check::getResult(‘adult‘); Check::getResult(‘elder‘);
适用场景:
1.多个类之区别在于表现行为不同,可以使用策略模式,在操作时动态选择要执行的策略。
2.需要在不同情况使用不同的策略,或者策略还可能在未来用其他方式实现。
3.对客户隐藏具体策略的实现细节,彼此完全独立。
推荐阅读: