设计模式
设计模式(Design pattern)是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性。 毫无疑问,设计模式于己于他人于系统都是多赢的;设计模式使代码编制真正工程化;设计模式是软件工程的基石脉络,如同大厦的结构一样。而设计原则则是设计模式所遵循的规则,设计模式就是实现了这些原则,从而达到了代码复用、增加可维护性的目的。
常用设计模式
策略模式
策略模式是对象的行为模式,用意是对一组算法的封装。动态的选择需要的算法并使用。
策略模式是程序中涉及决策控制的一种模式。策略模式功能非常强大,因为这个设计模式本身的核心思想就是面向对象编程的多形性思想。
策略模式的三个角色
1.抽象策略角色
2.具体策略角色
3.环境角色(对抽象策略角色的引用)
实现步骤
1.定义抽象角色类(定义好各个实现的共同抽象方法)
2.定义具体策略类(具体实现父类的共同方法)
3.定义环境角色类(私有化申明抽象角色变量,重载构造方法,执行抽象方法)
就在编程领域之外,有许多例子是关于策略模式的。例如:
如果我需要在早晨从家里出发去上班,我可以有几个策略考虑:我可以乘坐地铁,乘坐公交车,走路或其它的途径。
每个策略可以得到相同的结果,但是使用了不同的资源。
策略模式的代码实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <?php
abstract class baseAgent { abstract function PrintPage(); }
class ieAgent extends baseAgent { function PrintPage() { return 'IE'; } }
class otherAgent extends baseAgent { function PrintPage() { return 'not IE'; } }
class Browser { public function call($object) { return $object->PrintPage(); } }
$bro = new Browser (); echo $bro->call(new ieAgent ());
?>
|
工厂模式
工厂模式是我们最常用的实例化对象模式,是用工厂方法代替new操作的一种模式。
使用工厂模式的好处是,如果你想要更改所实例化的类名等,则只需更改该工厂方法内容即可,不需逐一寻找代码中,具体实例化的地方(new 处)修改了。为系统结构提供灵活的动态扩展机制,减少了耦合。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| <?php
header('Content-Type:text/html;charset=utf-8');
interface people { public function say(); }
class man implements people { public function say() { echo '我是男人<br>'; } }
class women implements people { public function say() { echo '我是女人<br>'; } }
class SimpleFactoty { static function createMan() { return new man(); }
static function createWomen() { return new women(); } }
$man = SimpleFactoty::createMan(); $man->say(); $woman = SimpleFactoty::createWomen(); $woman->say();
|
单例模式
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
单例模式是一种常见的设计模式,在计算机系统中,线程池、缓存、日志对象、对话框、打印机、数据库操作、显卡的驱动程序常被设计成单例。
单例模式分3种:懒汉式单例、饿汉式单例、登记式单例。
单例模式有以下3个特点:
1.只能有一个实例。
2.必须自行创建这个实例。
3.必须给其他对象提供这一实例。
那么为什么要使用PHP单例模式?
PHP 一个主要应用场合就是应用程序与数据库打交道的场景,在一个应用中会存在大量的数据库操作,针对数据库句柄连接数据库的行为,使用单例模式可以避免大量的 new 操作。因为每一次 new 操作都会消耗系统和内存的资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <?php
class Single { private $name;
private function __construct() { }
static public $instance;
static public function getInstance() { if (!self::$instance) self::$instance = new self(); return self::$instance; }
public function setName($n) { $this->name = $n; }
public function getName() { return $this->name; } }
$oa = Single::getInstance(); $ob = Single::getInstance(); $oa->setName('hello world'); $ob->setName('good morning'); echo $oa->getName(); echo $ob->getName();
|
注册模式
注册模式,解决全局共享和交换对象。已经创建好的对象,挂在到某个全局可以使用的数组上,在需要使用的时候, 直接从该数组上获取即可。将对象注册到全局的树上。任何地方直接去访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php
class Register { protected static $objects;
function set($alias, $object) //将对象注册到全局的树上 { self::$objects[$alias] = $object; }
static function get($name) { return self::$objects[$name]; }
function _unset($alias) { unset(self::$objects[$alias]); } }
|
适配器模式
将各种截然不同的函数接口封装成统一的 API。 PHP 中的数据库操作有 MySQL,MySQLi,PDO 三种,可以用适配器模式统一成一致,使不同的数据库操作,统一成一样的 API。类似的场景还有 cache 适配器,可以将 memcache,redis,fifile,apc 等不同的缓存函数,统一成一致。 首先定义一个接口(有几个方法,以及相应的参数)。然后,有几种不同的情况,就写几个类实现该接口。将完成相似功能的函数,统一成一致的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13
| <?php
namespace App;
interface IDatabase { function connect($host, $user, $passwd, $dbname);
function query($sql);
function close(); }
|
MySQL
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?php
namespace App\Database;
use App\IDatabase;
class MySQL implements IDatabase { protected $conn;
function connect($host, $user, $passwd, $dbname) { $conn = mysql_connect($host, $user, $passwd); mysql_select_db($dbname, $conn); $this->conn = $conn; }
function query($sql) { $res = mysql_query($sql, $this->conn); return $res; }
function close() { mysql_close($this->conn); } }
|
MySQLi
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| <?php
namespace App\Database;
use App\IDatabase;
class MySQLi implements IDatabase { protected $conn;
function connect($host, $user, $passwd, $dbname) { $conn = mysqli_connect($host, $user, $passwd, $dbname); $this->conn = $conn; }
function query($sql) { return mysqli_query($this->conn, $sql); }
function close() { mysqli_close($this->conn); } }
|
观察者模式
- 观察者模式(Observer),当一个对象状态发生变化时,依赖它的对象全部会收到通知,并自动更新。
- 事件发生后,要执行一连串更新操作。传统的编程方式,就是在事件的代码之后直接加入处理的逻辑。当更新的逻辑增多之后,代码会变得难以维护。这种方式是耦合的,侵入式的,增加新的逻辑需要修改事件的主体代码。
- 观察者模式实现了低耦合,非侵入式的通知与更新机制。
定义一个事件触发抽象类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| <?php
require_once 'Loader.php';
abstract class EventGenerator { private $observers = array();
function addObserver(Observer $observer) { $this->observers[] = $observer; }
function notify() { foreach ($this->observers as $observer) { $observer->update(); } } }
|
定义一个观察者接口
1 2 3 4 5 6 7 8 9 10
| <?php
require_once 'Loader.php';
interface Observer { function update(); }
|
实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <?php
require 'Loader.php';
class Event extends EventGenerator { function trigger() { echo "Event<br>"; } }
class Observer1 implements Observer { function update() { echo "逻辑1<br>"; } }
class Observer2 implements Observer { function update() { echo "逻辑2<br>"; } }
$event = new Event(); $event->addObserver(new Observer1()); $event->addObserver(new Observer2()); $event->trigger(); $event->notify();
|
六大设计原则
单一职责原则(Single Responsibility Principle - SRP)
一个类,只有一个引起它变化的原因。应该只有一个职责。每一个职责都是变化的一个轴线,如果一个类有一个以上的职责,这些职责就耦合在了一起。这会导致脆弱的设计。当一个职责发生变化时,可能会影响其它的职责。另外,多个职责耦合在一起,会影响复用性。例如:要实现逻辑和界面的分离。
开放封闭原则(Open Closed Principle - OCP)
软件实体应该是可扩展,而不可修改的。也就是说,对扩展是开放的,而对修改是封闭的。 对扩展开放,意味着有新的需求或变化时,可以对现有代码进行扩展,以适应新的情况。 对修改封闭,意味着类一旦设计完成,就可以独立完成其工作,而不要对类进行任何修改。 封装变化,是实现开放封闭原则的重要手段,对于经常发生变化的状态一般将其封装为一个抽象。 拒绝滥用抽象,只将经常变化的部分进行抽象,这种经验可以从设计模式的学习与应用中获得。
里氏替换原则(Liskov Substitution Principle - LSP)
里氏替换原则通俗的来讲就是:子类可以扩展父类的功能,但不能改变父类原有的功能。它包含以下 4 层含义
- 子类可以实现父类的抽象方法,但不能覆盖父类的非抽象方法。
- 子类中可以增加自己特有的方法。
- 当子类的方法重载父类的方法时,方法的前置条件(即方法的形参)要比父类方法的输入参数更宽松。
- 当子类的方法实现父类的抽象方法时,方法的后置条件(即方法的返回值)要比父类更严格
最少知识原则(Least Knowledge Principle - LKP)
最少知识原则又叫迪米特法则。核心思想是:低耦合、高内聚 一个实体应当尽量少的与其他实体之间发生相互作 用,使得系统功能模块相对独立。也就是说一个软件实体应当尽可能少的与其他实体发生相互作用。这样,当一个模块修改时,就会尽量少的影响其他的模块,扩展会相对容易,这是对软件实体之间通信的限制,它要求限制软件实体之间通信的宽度和深度。
接口隔离原则(Interface Segregation Principle - ISP)
接口隔离原则的含义是:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。 采用接口隔离原则对接口进行约束时,要注意以下几点:
- 接口尽量小,但是要有限度。对接口进行细化可以提高程序设计灵活性是不挣的事实,但是如果过小,则
会造成接口数量过多,使设计复杂化。所以一定要适度。 - 为依赖接口的类定制服务,只暴露给调用的类它需要的方法,它不需要的方法则隐藏起来。只有专注地为
一个模块提供定制服务,才能建立最小的依赖关系。 - 提高内聚,减少对外交互。使接口用最少的方法去完成最多的事情。
依赖倒置原则(Dependence Inversion Principle - DIP)
依赖倒置原则的核心思想是面向接口编程,不应该面向实现类编程。 在实际编程中,要做到下面 3 点:
- 低层模块尽量都要有抽象类或接口,或者两者都有。
- 变量的声明类型尽量是抽象类或接口。
- 使用继承时遵循里氏替换原则。