依赖注入vs抽象类vs继承vs singleton

时间:2016-01-11 15:36:24

标签: php oop

我有三个班级:

  • 将进程信息记录到文件的Logger
  • 验证输入数据的Checker
  • 对输入数据执行操作的Operations

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

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 ?
}

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

3 个答案:

答案 0 :(得分:1)

如果您正在编程接口,那么最灵活的选择是使用装饰器:

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创建了一个装饰器,以保证其真实性。

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

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

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

答案 1 :(得分:0)

直接回答你的问题,应该记录什么样的课程......

  

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

     

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

此处有更多信息Why we should consider the «Logger» class as a singleton?

对于一个不错的记录器,万一你的框架中没有一个,你应该看看这个klogger

答案 2 :(得分:0)

这取决于,是否有可能让logger对象具有不同的属性? (例如,将日志写入其他文件夹或根据其属性执行与要记录的消息不同的内容)如果是这样依赖注入的方式。

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

最后,如果您的记录器需要初始化并且只初始化一次,那么您可以使用单例。