模板模式

模板模式

模板模式准备一个抽象类,将部分逻辑以具体方法以及具体构造形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

讲到模板模式,我不得不提的就是我们著名的PSR-3规范,PSR-3为我们提供了模板的接口



namespace Psr\Log;

/**
 * Describes a logger instance.
 *
 * The message MUST be a string or object implementing __toString().
 *
 * The message MAY contain placeholders in the form: {foo} where foo
 * will be replaced by the context data in key "foo".
 *
 * The context array can contain arbitrary data, the only assumption that
 * can be made by implementors is that if an Exception instance is given
 * to produce a stack trace, it MUST be in a key named "exception".
 *
 * See https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md
 * for the full interface specification.
 */
interface LoggerInterface
{
    /**
     * System is unusable.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function emergency($message, array $context = array());

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function alert($message, array $context = array());

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function critical($message, array $context = array());

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function error($message, array $context = array());

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function warning($message, array $context = array());

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function notice($message, array $context = array());

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function info($message, array $context = array());

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function debug($message, array $context = array());

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed $level
     * @param string $message
     * @param array $context
     * @return void
     */
    public function log($level, $message, array $context = array());
}

以上代码提供了记录各个级别日志的接口,下面我们使用模板模式做一个日志的实现。


/**
 * Describes log levels.
 */
class LogLevel
{
    const EMERGENCY = 'emergency';
    const ALERT     = 'alert';
    const CRITICAL  = 'critical';
    const ERROR     = 'error';
    const WARNING   = 'warning';
    const NOTICE    = 'notice';
    const INFO      = 'info';
    const DEBUG     = 'debug';
}

/**
 * Class ILogger
 * @datetime 2020/7/17 7:16 PM
 * @author roach
 * @email [email protected]
 */
abstract class ILogger
{
    /**
     * @param string $message
     * @param array  $context
     * @return string
     * @datetime 2020/7/17 7:17 PM
     * @author roach
     * @email [email protected]
     */
    public static function interpolate($message, array $context = array())
    {
        // build a replacement array with braces around the context keys
        $replace = array();
        foreach ($context as $key => $val) {
            // check that the value can be cast to string
            if (!is_array($val) && (!is_object($val) || method_exists($val, '__toString'))) {
                $replace['{' . $key . '}'] = $val;
            }
        }

        // interpolate replacement values into the message and return
        return strtr($message, $replace);
    }

    /**
     * System is unusable.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function emergency($message, array $context = array())
    {
        $this->log(LogLevel::EMERGENCY, $message, $context);
    }

    /**
     * Action must be taken immediately.
     *
     * Example: Entire website down, database unavailable, etc. This should
     * trigger the SMS alerts and wake you up.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function alert($message, array $context = array())
    {
        $this->log(LogLevel::ALERT, $message, $context);
    }

    /**
     * Critical conditions.
     *
     * Example: Application component unavailable, unexpected exception.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function critical($message, array $context = array())
    {
        $this->log(LogLevel::CRITICAL, $message, $context);
    }

    /**
     * Runtime errors that do not require immediate action but should typically
     * be logged and monitored.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function error($message, array $context = array())
    {
        $this->log(LogLevel::ERROR, $message, $context);
    }

    /**
     * Exceptional occurrences that are not errors.
     *
     * Example: Use of deprecated APIs, poor use of an API, undesirable things
     * that are not necessarily wrong.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function warning($message, array $context = array())
    {
        $this->log(LogLevel::WARNING, $message, $context);
    }

    /**
     * Normal but significant events.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function notice($message, array $context = array())
    {
        $this->log(LogLevel::NOTICE, $message, $context);
    }

    /**
     * Interesting events.
     *
     * Example: User logs in, SQL logs.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function info($message, array $context = array())
    {
        $this->log(LogLevel::INFO, $message, $context);
    }

    /**
     * Detailed debug information.
     *
     * @param string $message
     * @param array $context
     * @return void
     */
    public function debug($message, array $context = array())
    {
        $this->log(LogLevel::DEBUG, $message, $context);
    }

    /**
     * Logs with an arbitrary level.
     *
     * @param mixed $level
     * @param string $message
     * @param array $context
     * @return void
     */
    abstract public function log($level, $message, array $context = array());
}

/**
 * Class File
 * @datetime 2020/7/17 7:23 PM
 * @author roach
 * @email [email protected]
 */
class File extends ILogger
{
    /**
     * @var string
     * @datetime 2020/7/17 7:20 PM
     * @author roach
     * @email [email protected] 
     */
    protected $_fileName;

    /**
     * @param $fileName
     * @datetime 2020/7/17 7:21 PM
     * @author roach
     * @email [email protected]
     */
    public function setFileName($fileName)
    {
        $this->_fileName = $fileName;
    }

    /**
     * @param string $level
     * @param string $message
     * @param array  $context
     * @datetime 2020/7/17 7:23 PM
     * @author roach
     * @email [email protected]
     */
    public function log($level, $message, array $context = array())
    {
        $message = '{datetime} {level} {ip} {url} '.$message.PHP_EOL;
        $context['datetime'] = date('Y-m-d H:i:s');
        $context['level']    = $level;
        $context['ip']       = $_SERVER['REMOTE_ADDR'];
        $context['url']      = $_SERVER['REQUEST_URI'];
        $message = self::interpolate($message, $context);
        
        file_put_contents($this->_fileName, $message, FILE_APPEND | LOCK_EX);
    }
}

class Db extends ILogger
{
    /**
     * @var \PDO
     * @datetime 2020/7/17 7:24 PM
     * @author roach
     * @email [email protected] 
     */
    protected $_pdo;

    /**
     * @param PDO $pdo
     * @datetime 2020/7/17 7:24 PM
     * @author roach
     * @email [email protected]
     */
    public function setPdo(\PDO $pdo)
    {
        $this->_pdo = $pdo;
    }

    /**
     * @param mixed $level
     * @param string $message
     * @param array $context
     * @datetime 2020/7/17 7:28 PM
     * @author roach
     * @email [email protected]
     */
    public function log($level, $message, array $context = array())
    {
        $message = self::interpolate($message, $context);
        $stmt = $this->_pdo->prepare('INSERT INTO `roach_log`(`level`,`ip`,`url`,`msg`,`add_time`)VALUES(?,?,?,?,?)');
        $stmt->execute([
            $level,
            $_SERVER['REMOTE_ADDR'],
            $_SERVER['REQUEST_URI'],
            self::interpolate($message, $context),
            time()
        ]);
    }
}

分析以上代码,ILogger是抽象的模板类,实现了部分逻辑,但是log方法没有实现,交给子类实现,作者实现了FileDb子类,如果您感兴趣可以实现其他类,如RedisKafka等。

看过策略模式文章的同学也许有疑问了,这不是策略模式吗?把log方法封装了起来,从本质上来讲算是解决了不同算法的问题,但是严格上来讲,缺少了一个Context对象,不过不重要,只要我们的代码符合开闭原则,我们以设计模式六大原则来编写代码就可以了,严格按照模式来编写代码是没有意义的。

学习更多内容: https://404.360tryst.com

我的视频课程: https://edu.csdn.net/course/detail/9933

你可能感兴趣的:(PHP进阶教程)