假设您正在编写一个“更大”的应用程序,并且您希望在类中记录某些错误。现在几乎每个班级都需要访问Logger。
一个简单的解决方案如下(PHP,但无所谓):
class SomeClass {
public function someMethod() {
// stuff ...
Logger::log("something happened");
// stuff ...
}
}
这种方法的问题在于它使得使用Logger的每个类都依赖于它而你不能简单地在没有Logger的其他项目中使用相同的类。它是紧密耦合的。而且,如果不更改SomeClass
的实现,则无法更改消息的记录方式。
上述方法的“小”升级如下:
class SomeClass {
private $logger;
public function __construct(/* stuff */) {
$this->logger = LoggerFactory::getLogger();
}
public function someMethod() {
// stuff ...
$this->logger->log("something happened");
// stuff ...
}
}
这并没有真正解决问题,因为SomeClass
现在只依赖于LoggerFactory
而且它与它紧密耦合。好消息是,LoggerFactory
现在可以返回Logger
类的不同实例。因此,如果您想更改消息的记录方式,则无需更改SomeClass
的实现。
另一种方法是使用依赖注入:
class SomeClass {
private $logger;
public function __construct(Logger $logger, /* stuff */) {
$this->logger = $logger;
}
public function someMethod() {
// stuff ...
$this->logger->log("something happened");
// stuff ...
}
}
现在这不会将SomeClass
与任何特定类联系起来。它只需要一个实现Logger
接口的类。但是我遇到的问题是,每当你创建一个对象时,你需要将Logger
实例传递给构造函数。
$object = new Position($this->logger, $x, $y);
我遇到的另一个问题是,记录器实际上并不属于Position
类$x
和$y
。这只是一个实用工具。
你如何最好地处理这种情况?在这种情况下,耦合和内聚之间的最佳折衷是什么?
答案 0 :(得分:1)
答案 1 :(得分:1)
您的方法是正确的,并且回答了控制原理的反转。 但是,如您所述,记录器对于应用程序来说是一个贯穿各领域的关注,与Position实体无关。换句话说:它纯粹是技术性的,我们不想知道它。
我在这里看到两个选项:
1)您通过某种类型的Manager类引用所有记录器,并使用类级别所需的记录器。必须为解决方案中需要记录功能的所有类都知道此LoggingManager。在使用外部Logging Frameworks时,我经常这样做(例如:Log4Net)。
2)或者您可以在运行时从更高的实体(服务)请求记录器,该实体将提供所需的记录器。使用依赖注入框架或面向方面编程风格的类配置将从类本身中删除记录器类型(或工厂)的选择。
不幸的是我不熟悉PHP,所以我无法为您提供代码示例。 但我确实在C#中找到了这个AOP的好例子:http://visualstudiomagazine.com/articles/2011/05/12/wccsp_aspect-oriented-programming.aspx