设计模式是一套被反复使用,代码设计经验的总结,它的主要作用是提升代码的复用性、扩展性和可靠性,本文主要介绍常见的几种设计模式:工厂模式、单例模式、观察者模式、策略模式。
1.工厂模式:
工厂模式是创建型设计模式,它的实现方式是:定义抽象接口,根据产品类具体实现接口,通过工厂类管理具体产品的对象创建;体现的原则是面向接口编程,而不是面向实现编程,这样可以提高代码的扩展性。下面举例分析。
1.1.不使用工厂模式
class Cal
{
public function calculate($a, $b, $operator)
{
switch ($operator) {
case '+':
$result = $a + $b;
break;
case '-':
$result = $a - $b;
break;
case '*':
$result = $a * $b;
break;
default:
$result = $a + $b;
}
return $result;
}
}
这种方式看起来没有问题,但是程序的扩展性并不好:假如要加很多个其他计算功能(开方、乘方、对数、三角函数等),则只能在case中继续堆砌代码,这么做的坏处是所有计算类都高度耦合在一起,如果新加的若干个计算类中有一处代码出现问题,则其他计算类都无法使用,同时程序的执行效率也大大降低。
2.1.使用工厂模式
为了解决上述问题,降低各个具体类(以计算类为例)的耦合性,提升程序的可扩展性,工厂模式就应运而生,示例代码如下:
//1.计算类的抽象接口
interface CalC
{
public function getTwoVal($a, $b);
}
//2.面向接口编程,通过实现接口来扩展具体的功能类
class AddC implements CalC
{
public function getTwoVal($a, $b)
{
return $a + $b;
}
}
class SubC implements CalC
{
public function getTwoVal($a, $b)
{
return $a - $b;
}
}
class MultiplyC implements CalC
{
public function getTwoVal($a, $b)
{
return $a * $b;
}
}
//3.工厂类管理具体计算类的创建
class Factory
{
public static function getObj($operator)
{
switch ($operator) {
case '+':
$cal = new AddC();
break;
case '-':
$cal = new SubC();
break;
case '*':
$cal = new MultiplyC();
break;
default:
$cal = new AddC();
}
return $cal;
}
}
//使用
$add = Factory::getObj('+');
$res = $add->getTwoVal(5, 2);
echo $res;
通过上述代码可以看到,各个具体计算类的只有自己的相关代码而不包含其他代码,计算类间的耦合性大大降低,利于整个计算功能的扩展。
2.单例模式:
单例模式应该是最简单的设计模式了,同时也非常常见,它的核心思想是只实例化一个对象供全局使用,从而减少内存开销、节省系统资源。
代码示例:
//2.单例模式,三私一公两静态
class Db
{
private static $instance;
private function __construct()
{
//内部初始化
}
private function __clone()
{
//禁止克隆
}
//开放单一入口
public static function getDb()
{
//判断实例是否已存在
if (!(self::$instance instanceof self)) {
self::$instance = new Db();
}
return self::$instance;
}
}
//测试单例
$db = Db::getDb();
$db1 = Db::getDb();
$res = ($db === $db1);
var_dump($res);
3.观察者模式:
观察者模式属于行为型设计模式,常用于存在一对多关系时:当一个对象发生变更操作时,会自动通知其依赖对象。具体实现方式:将一个类设置为可被观察(实现接口),接着它可以注册观察者类(实现观察者类接口),当前类发生操作变化时可通知观察者类。从而实现观察和被观察者业务逻辑的松散耦合。
代码示例
interface Observer
{
//观察者的方法
function onChanges($sender, $args);
}
interface Observable
{
//注册观察者
function addObserver($observer);
}
class UserList implements Observable
{
private $observers;
//可观察的类,添加其观察者
public function addObserver($observer)
{
$this->observers[] = $observer;
}
//执行方法,并通知观察者
public function addCustomer($name)
{
foreach ($this->observers as $observer) {
$observer->onChanges($this, $name);//通知观察者,传递参数和发送人信息
}
}
}
class UserListLogger implements Observer
{
//观察者接受参数,并执行操作
function onChanges($sender, $args)
{
echo "add {$args} to user_list" . PHP_EOL;
}
}
//使用
$logger = new UserListLogger();//观察类
$userList = new UserList();//可观察类
$userList->addObserver($logger);//添加观察类
$userList->addCustomer('Bryan');//执行动作
观察者的使用场景很多,比如用户注册后发送邮件通知管理员和用户,我们可以将注册逻辑写到可观察类函数中,将发送邮件写到观察者类函数中,这样的好处是将发送邮件逻辑和注册逻辑实现松散耦合,注册逻辑的修改不会影响发送邮件逻辑。再比如,用户下单购买商品时,可以在下单成功后通知其观察者,各个观察者分别实现购买记录写入日志、发送短信、送兑换券积分等业务逻辑。
4.策略模式
策略模式是行为型设计模式,它可以将一个类的行为方法在运行时进行更改,避免大量使用if-else语句带来的复杂度以及维护成本。
代码示例
interface CalStrategy
{
function getVal($a, $b);
}
class AddStrategy implements CalStrategy
{
function getVal($a, $b)
{
return $a + $b;
}
}
class SubStrategy implements CalStrategy
{
function getVal($a, $b)
{
return $a - $b;
}
}
class MultiplyStrategy implements CalStrategy
{
function getVal($a, $b)
{
return $a * $b;
}
}
class CalContext
{
private $strategy;
//初始化策略
public function __construct(CalStrategy $strategy)
{
$this->strategy = $strategy;
}
//用于策略变更
public function setStrategy(CalStrategy $strategy)
{
$this->strategy = $strategy;
}
public function getVal($a, $b)
{
return $this->strategy->getVal($a, $b);
}
}
//使用
$calContext = new CalContext(new AddStrategy());//初始化策略
$addResult = $calContext->getVal(5, 10);
echo $addResult . PHP_EOL;
$calContext->setStrategy(new MultiplyStrategy());//变更策略
$mulResult = $calContext->getVal(5, 10);
echo $mulResult . PHP_EOL;
上述代码的例子仍然是工厂设计模式的例子-计算类。可以看到,工厂模式和策略模式都可以实现这种功能,区别在于:工厂模式是类的统一管理,关注的是实例的创建,属于创建型设计模式;而策略模式接受已经创建好的实例,实现不同的行为(变更策略),属于行为型设计模式。