【设计模式】之工厂模式、单例模式、观察者模式、策略模式(PHP)

设计模式是一套被反复使用,代码设计经验的总结,它的主要作用是提升代码的复用性扩展性可靠性,本文主要介绍常见的几种设计模式:工厂模式单例模式观察者模式策略模式

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;

上述代码的例子仍然是工厂设计模式的例子-计算类。可以看到,工厂模式和策略模式都可以实现这种功能,区别在于:工厂模式是类的统一管理,关注的是实例的创建,属于创建型设计模式;而策略模式接受已经创建好的实例,实现不同的行为(变更策略),属于行为型设计模式。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容