【问题标题】:PHP Composer, good design pattern for including suggested modules with namespacesPHP Composer,很好的设计模式,用于包含带有命名空间的建议模块
【发布时间】:2013-03-03 01:30:27
【问题描述】:

我正在编写一些代码(数据库抽象层,将在我的其他代码模块中使用),我想将其作为独立模块发布,可以包含在 composer 的项目中。我想在作曲家定义中包含一些可以提高我的模块性能的建议模块,但这不是必需的。

我遇到的问题是如何以一种完全可怕的方式做到这一点。

因此,在这个特定示例中,模块在 namespace Intahwebz\DB; 中声明,然后尝试包含可选模块 Intahwebz\Log\Log,进而尝试使用可选模块 Monolog

我目前拥有的是模块代码ConnectionWrapper.php

namespace Intahwebz\DB;

use Intahwebz\Log\Log;


if(trait_exists("Intahwebz\Log\Log") == false){
    require_once("Log.php");
}


class ConnectionWrapper{

    use Log;

    function __construct(){
        $this->initLog();

        // Rest of constructor code.

        $this->log->trace("ConnectionWrapper has been constructed.");
    }   

    // Lots of other functions here.
}


?>  

然后在 Log.php 中检查 Monolog 是否可用,如果可用,则包含它,否则定义一个真正轻量级的记录器。

<?php

namespace Intahwebz\Log;

if (class_exists('Monolog\Logger') &&
    class_exists('Monolog\Handler\StreamHandler')) {

require_once "MonologWrapper.php";

}
else{

    class Logger{
        public function debug($message, array $context = array()){
            echo $message."\n";
        }
        public function log($level, $message, array $context = array()){
            echo $message."\n";
        }
        public function info($message, array $context = array()){
            echo $message."\n";
        }
        public function notice($message, array $context = array()){
            echo $message."\n";
        }
        public function warning($message, array $context = array()){
            echo $message."\n";
        }
        public function error($message, array $context = array()){
            echo $message."\n";
        }
        public function critical($message, array $context = array()){
            echo $message."\n";
        }
        public function alert($message, array $context = array()){
            echo $message."\n";
        }
        public function emergency($message, array $context = array()){
            echo $message."\n";
        }
    }

    trait Log{

        var $log;
        function initLog(){
            $this->log = new Logger(__CLASS__);
        }
    }
}

如果 Monolog 可用,我们通过包含 MonologWrapper.php 来使用它

<?php

namespace Intahwebz\Log;

use Monolog\Logger;
use Monolog\Handler\StreamHandler;

trait Log{

    var $log;

    function    initLog(){
        $this->log = new Logger(__CLASS__);
        //Todo - get log handler from config file automagically.
        $this->log->pushHandler(new StreamHandler(PATH_TO_ROOT.'var/log/Admin.log', Logger::WARNING));
    }
}


?>

这样做的问题是:

1) 它非常难看,并且每个建议的模块都需要额外的文件。

2) 它不允许人们在不重写代码的情况下切换到独白以外的其他记录器。

3) 它有重复的类/特征定义,仅由 if 语句分隔,这完全混淆了我的 IDE。

我知道 Symfony 等人解决这个问题的方法是通过服务层。但是,如果不强制依赖包含比引入可选模块应该复杂得多,我看不出如何使用该设计模式。

谁能描述一个体面的设计模式来包含可选模块,或者用其他兼容模块替换它们?还是这种类型的东西只能在实际框架中很好地定义?

【问题讨论】:

    标签: php namespaces composer-php


    【解决方案1】:

    依赖注入:)

    例如代替

    function    initLog(){
        $this->log = new Logger(__CLASS__);
        // ..
    }
    

    使用

    function setLogger(Logger $logger) {
        $this->log = $logger;
        // ...
    }
    

    等等。这种硬编码的依赖太多了,我就不一一列举了。

    class MonologLoggingConnectionWrapperWrapper extends ConnectionWrapper {
        public function __construct (ConnectionWrapper $wrapper, Logger $logger) {
            // ..
        }
    }
    

    现在只有当有人试图实例化这个类时才需要 Monolog,然后很明显,缺少一些东西。如果您愿意,可以在类定义之前向文件添加测试,这应该(在这种情况下)抛出异常或触发错误。

    (类名源于ConnectionWrapper并不是真正的包装器,而是它自己的一个类:包装器必须扩展被包装的类,或者实现相应的接口,因此它是完全可交换的)

    值得一提:避免条件类或函数定义:)一个类名,一个类(不是更多),当我尝试实例化一个类时,它要么存在,要么不存在,但不是“有时”。

    【讨论】:

    • 感谢您的回答。我几乎明白了,除了有人如何使用他们自己喜欢的记录器而不是 Monolog?他们是否必须创建自己的“OtherLoggingConnectionWrapperWrapper”类?
    • “值得一提:避免条件类或函数定义:)”是的。我用“TODO - 重构这个废话”来提交它。
    • 非常离题:根据我的经验,“TODO”标志从未被阅读过:D
    猜你喜欢
    • 2015-11-21
    • 2020-07-22
    • 2016-05-17
    • 2018-06-02
    • 2013-05-07
    • 2019-12-04
    • 2021-03-20
    • 1970-01-01
    • 2010-10-30
    相关资源
    最近更新 更多