PHP 设计模式 - 行为型 - 模板方法模式(Template Method)

1. 模式定义

模板方法模式又叫模板模式,该模式在一个方法中定义一个算法的骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法中的某些步骤

  • 主要的方法定义为 final,防止子类修改算法骨架
  • 子类必须实现的方法定义为 abstract
  • 普通的方法(无 finalabstract 修饰)则称之为钩子(hook)

模板方法模式是基于继承的代码复用技术,模板方法模式的结构和用法也是面向对象设计的核心之一。

2. UML类图

image.png

3. 示例代码

Journey.php 父类:算法的骨架

<?php

namespace DesignPattern\Behavioral\TemplateMethod;

abstract class Journey
{
    /**
     * 该方法是父类和子类提供的公共服务
     * 注意到方法前加了final,意味着子类不能重写该方法
     */
    final public function takeATrip()
    {
        $this->buyAFlight();
        $this->takePlane();
        $this->enjoyVacation();
        $this->buyGift();
        $this->takePlane();
    }

    /**
     * 该方法必须被子类实现, 这是模板方法模式的核心特性
     */
    abstract protected function enjoyVacation();

    /**
     * 这个方法也是算法的一部分,但是是可选的,只有在需要的时候才去重写它
     */
    protected function buyGift()
    {
    }

    /**
     * 子类不能访问该方法
     */
    private function buyAFlight()
    {
        echo "Buying a flight\n";
    }

    /**
     * 这也是个final方法
     */
    final protected function takePlane()
    {
        echo "Taking the plane\n";
    }
}

BeachJourney.php 子类

<?php

namespace DesignPattern\Behavioral\TemplateMethod;

/**
 * BeachJourney类(在海滩度假)
 */
class BeachJourney extends Journey
{
    protected function enjoyVacation()
    {
        echo "Swimming and sun-bathing\n";
    }
}

CityJourney 子类

<?php

namespace DesignPattern\Behavioral\TemplateMethod;


/**
 * CityJourney类(在城市中度假)
 */
class CityJourney extends Journey
{
    protected function enjoyVacation()
    {
        echo "Eat, drink, take photos and sleep\n";
    }
}

单元测试

<?php
namespace DesignPattern\Tests;


use DesignPattern\Behavioral\TemplateMethod\BeachJourney;
use DesignPattern\Behavioral\TemplateMethod\CityJourney;
use PHPUnit\Framework\TestCase;

/**
 * 模板方法测试
 */
class TemplateMethodTest extends TestCase
{

    public function testBeach()
    {
        $journey = new BeachJourney();
        $this->expectOutputRegex('#sun-bathing#');
        $journey->takeATrip();
    }

    public function testCity()
    {
        $journey = new CityJourney();
        $this->expectOutputRegex('#drink#');
        $journey->takeATrip();
    }

    /**
     * 在PHPUnit中如何测试抽象模板方法
     */
    public function testLasVegas()
    {
        $journey = $this->getMockForAbstractClass('DesignPattern\Behavioral\TemplateMethod\Journey');
        $journey->expects($this->once())
            ->method('enjoyVacation')
            ->will($this->returnCallback(array($this, 'mockUpVacation')));
        $this->expectOutputRegex('#Las Vegas#');
        $journey->takeATrip();
    }

    public function mockUpVacation()
    {
        echo "Fear and loathing in Las Vegas\n";
    }
}

参考文档:https://laravelacademy.org/post/3006
教程源码:https://github.com/SylviaYuan1995/DesignPatternDemo

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容