依赖注入vs抽象类vs继承vs单例


Dependency injection vs abstract class vs inheritance vs singleton

我有三个类:

  • 将进程信息记录到文件中的Logger
  • 验证输入数据的Checker
  • 对输入数据进行处理的CCD_ 3

我需要Checker和Operations中的Logger来输出(记录)过程。我需要操作中的检查器,以便检查数据的有效性。

class Logger {
}
class Checker {
  // inherit Logger ?
  // dependency-inject Logger ?
  // access Logger statically ?
  // compose Logger as singleton or create new object ?
}
class Operations {
  // inherit Logger ?
  // dependency-inject Logger ?
  // access Logger statically ?
  // compose Logger as singleton or create new object ?
}

应用程序将只创建一个操作类和检查器类的实例。问题是,在不需要或不创建新对象的情况下,Logger应该是什么类型的类,以及如何在Operations和Checker类中实现它?

如果要对接口进行编程,那么最灵活的选择是使用装饰器:

interface ILogger
{
    function log($msg);
}
class FileLogger implements ILogger{
    function log($msg)
    {
        file_put_contents('file.dat', $msg . "'n", FILE_APPEND);
    }
}

interface IChecker
{
    function check($var);
}
class Checker implements IChecker {
    function check($var)
    {
        //do some checking
        return true;
    }
}
//decorator
class LoggingChecker implements IChecker
{
    private $checker;
    private $logger;
    function __construct(IChecker $checker, ILogger $logger)
    {
        $this->checker = $checker;
        $this->logger = $logger;
    }
    function check($var)
    {
        $checkResult = $this->checker->check($var);
        $this->logger->log('check result is: ' . ($checkResult)? 'a success' : 'a failure');
        return $checkResult;
    }
}

interface IOperations
{
    function doOperation($var);
}
class Operations implements IOperations{
    private $checker;
    function __construct(IChecker $checker)
    {
        $this->checker = $checker;
    }
    function doOperation($var)
    {
        if($this->checker->check($var)){
            //do some operation
        }
     }
}
//composotion root
//create concrete logger, in this case a FileLogger instance, though you could latter create a SqlLogger or EmailLogger 
//and swap it without any changes to the other classes
$logger = new FileLogger();
//create concreate checker
$checker = new Checker();
//create decorated checker
$loggingChecker = new LoggingChecker($checker, $logger);
//Operations can now be instantiated with either the regular checker, or the decorated logging checker, and it will
//work just the same, with no knowledge of the existense of loging, or indeed any knowledge of how the checker functions, beyond
//the fact it has a method called check that excepts a single parameter
$operation = new Operations($checker); //no logging is done
//OR
$operation = new Operations($loggingChecker); //logging is done

注意,在上面的例子中,我只为IChecker创建了一个装饰器,用于bereivity。

您也可以为IOperations(例如LoggingOperations)创建一个以相同方式工作的装饰器——它取决于ILoggerIOperations的实例。您可以使用相同的具体ILogger实现($logger)和IOperations实现($operation)来实例化它。

使用此对象的类将对IOperations具有单一依赖性,并且可以用$operation$logginOperation实例化,并以相同的方式进行操作。

希望这个例子能让您了解接口编程的灵活性,以及decorator模式如何简化依赖类并帮助实施SRP

直接回答您的问题,记录器应该是什么样的类…

真正的单例的一个经典例子是日志服务。假设我们具有基于事件的日志记录服务:客户端对象请求该文本通过向日志记录服务发送消息来进行日志记录。其他对象通过监听将文本记录在某个地方(控制台、文件等)向日志记录服务发送这些日志记录请求并进行处理。首先,请注意,日志记录服务通过了单身人士:

请求者需要一个众所周知的对象来向其发送请求日志这意味着一个全局访问点。由于日志记录服务多个侦听器可以注册到的单个事件源只需要是一个实例。

更多信息请点击此处为什么我们应该考虑«;记录器»;类作为单例?

对于一个好的记录器,如果你的框架中没有,你应该看看这个klogger

这取决于是否有可能拥有具有不同属性的记录器对象?(例如,将日志写入其他文件夹,或根据其属性对要记录的消息执行不同的操作)如果是这样,则可以注入依赖项。

记录器是否以相同的方式执行,甚至不需要属性?(例如,它只是将消息写入相对于源文件夹的预定义文件中)。如果您甚至不需要使用singleton,那么它可以是一个具有静态方法的Utilities类。

最后,如果记录器需要初始化,并且只初始化一次,则可以使用singleton。