类继承和依赖注入的关系

Let’s face it: for good or bad, OOP has been actively drilling deep holes in the soil of PHP in the last few years, hence its ubiquitous presence now is anything but breaking news. Furthermore, while this steady incremental “objectification” in the language’s terrain is generally considered a beneficial shift towards new and more promising horizons, for those still rooted in the procedural paradigm, the process has been far from innocuous.

让我们面对现实吧:无论好坏,OOP在过去的几年中一直在PHP的土壤中积极钻探深Kong,因此,它无处不在的存在现在绝不是什么新闻。 此外,尽管通常认为在语言领域中这种不断增加的“客观化”是向新的和更有希望的视野的有益转变,但对于仍然植根于程序范式的人们来说,这一过程并非无害。

There’s a few solid arguments that sustain this mindset: first and foremost the nature of OOP is awkward and inherently complex, plagued with nuance that can take literally years to tame. Also, defining the APIs that OO components use for interacting with one another can be quite a burden at times, especially when it comes to designing systems whose backbone rests on facilities provided by large and tangled object graphs.

有一些可靠的论据支持这种思维方式:首先,最重要的是,OOP的本质是笨拙的,内在的复杂,充满细微差别,可能需要花费数年才能驯服。 而且,有时定义OO组件用于彼此交互的API可能会很繁重,尤其是在设计其主干基于大型且复杂的对象图提供的功能的系统时 。

From a programmer’s perspective, the process of designing easily consumable APIs while still keeping a decent level of decoupling between the involved classes is always a tricky trade-off, as the more expressive the dependencies are, the harder the work is that needs to be done behind the scenes for wiring up the collaborators in their proper sequence. This fact itself is a dilemma that has plagued the minds of developers from long ago, which is certainly harder to digest as time goes by. With applications becoming increasingly bloated, housing inside their boundaries a growing number of classes, the construction of clean, declarative APIs isn’t just a loose practice anymore that can eventually be pushed into the language’s mainstream from time to time. It’s simply a must.

从程序员的角度来看,在设计易于使用的API的同时仍保持相关类之间的良好去耦水平的过程始终是一个棘手的权衡,因为依赖项越富表现力,工作就越需要完成在幕后,以适当的顺序连接协作者。 这个事实本身就是一个困扰很久以前的开发人员思想的困境,随着时间的流逝,这无疑更加难以消化。 随着应用程序变得越来越膨胀,在其边界内容纳越来越多的类,构造清晰的声明式API不再只是一种宽松的做法,最终最终会不时地被推入该语言的主流。 这是必须的。

This raises a few interesting questions: how should a class be provided with its dependencies without cluttering its API, or even worse, without coupling it to the dependencies themselves? Just by appealing to the benefits of Dependency Injection? Through the reigns of an injected Service Locator?

这就提出了一些有趣的问题:如何为类提供其依赖项而又不会使其API混乱,甚至更糟,而又不将其与依赖项本身耦合? 只是通过吸引依赖注入的好处? 通过注入的服务定位器的统治?

For obvious reasons, there’s no a one size fits all solution to the problem, even when all of the aforementioned approaches have something in common. Yes, to some extent they all make use of some form of Inversion of Control, which not only allows you to decouple the dependencies from the class but also keeps a neat level of insulation between the dependences’ interfaces and the implementation, making mocking/testing a breeze.

出于明显的原因,即使上述所有方法都有共同点,也没有一种方法可以完全解决问题。 是的,它们在某种程度上都利用了某种形式的控制反转,这不仅使您可以将依赖项与类解耦,还可以使依赖项的接口与实现之间保持整洁的隔离,从而进行模拟/测试微风。

Evidently, the topic is a world away from being banal, and as such it deserves a close, in-depth analysis. In this two-part series I’ll be doing a quick roundup of some of the most common methodologies that can be employed for managing class dependencies in modern application development, ranging from using service locators and injectable factories, to sinking your teeth into plain vanilla Dependency Injection, and even implementing a naive Dependency Injection Container.

显然,该主题是一个远离平庸的世界,因此,它值得进行深入,深入的分析。 在这个由两部分组成的系列文章中,我将快速汇总一些可用于管理现代应用程序开发中的类依赖关系的最常用方法,从使用服务定位器和可注入工厂,到将您的牙齿陷入普通的香草依赖注入,甚至实现了幼稚的依赖注入容器。

(希望)消失的瘟疫–构造函数中的“新”运算符 (A (Hopefully) Vanishing Plague – “new” Operators in Constructors)

The mantra is somewhat old, sure, but its claim still rings loud and clear: “placing ‘new’ operators in constructors is just plain evil.” We all know that now, and even take for granted that we’ll never commit such a cardinal sin. But this was literally the default method used for years for a class to look up its dependencies before Dependency Injection reached the PHP world. For the sake of completeness, let’s recreate this clunky, old-fashioned scenario and remind ourselves why we should get away from such a harmful plague.

这种口头禅肯定有些陈旧,但是它的主张仍然响亮而清晰:“在构造函数中放置'new'运算符简直就是邪恶。” 我们现在都知道,甚至理所当然地认为,我们绝不会犯下这种根本性的罪行。 但这实际上是多年来使用的默认方法,用于类在Dependency Injection进入PHP世界之前查找其依赖项。 为了完整起见,让我们重新创建这种笨拙的老式场景,并提醒自己为什么要摆脱这种有害的瘟疫。

Say that we need to build a simple file storage module which must internally consume a serializer in order to read and write data to a specific file. The implementation of the serializer would look something like to this:

假设我们需要构建一个简单的文件存储模块,该模块必须在内部使用一个序列化程序才能将数据读写到特定文件中。 序列化器的实现如下所示:

<?php
namespace LibraryEncoder;
class Serializer implements Serializable
{
public function serialize($data) {
if (is_resource($data)) {
throw new InvalidArgumentException(
"PHP resources are not serializable.");
}
if (($data = serialize($data)) === false) {
throw new RuntimeException(
"Unable to serialize the supplied data.");
}
return $data;
}
public function unserialize($data) {
if (!is_string($data)|| empty($data)) {
throw new InvalidArgumentException(
"The data to be unserialized must be a non-empty string.");
}
if (($data = @unserialize($data)) === false) {
throw new RuntimeException("Unable to unserialize the supplied data.");
}
return $data;
}
}

The Serializer class is just a lightweight implementation of the native Serializable interface and exposes the typical serialize/unserialize duet to the outside world. With this contrived strategy class doing its thing as expected, the aforementioned file storage module could be sloppily coded as follows:

Serializer类只是本机Serializable接口的轻量级实现,将典型的序列化/非序列化二重奏向外界公开。 通过这种精心设计的策略类按预期方式运行,上述文件存储模块可以按如下所示进行草率编码:

<?php
namespace LibraryFile;
use LibraryEncoderSerializer;
class FileStorage
{
const DEFAULT_STORAGE_FILE = "data.dat";
protected $serializer;
protected $file;
public function __construct($file = self::DEFAULT_STORAGE_FILE) {
$this->setFile($file);
$this->serializer = new Serializer();
}
public function setFile($file) {
if (!is_file($file)) {
throw new InvalidArgumentException(
"The file $file does not exist.");
}
if (!is_readable($file) || !is_writable($file)) {
if (!chmod($file, 0644)) {
throw new InvalidArgumentException(
"The file $file is not readable or writable.");
}
}
$this->file = $file;
return $this;
}
public function read() {
try {
return $this->serializer->unserialize(
@file_get_contents($this->file));
}
catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
public function write($data) {
try {
return file_put_contents($this->file,
$this->serializer->serialize($data));
}
catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
}

The behavior of FileStorage boils down to just saving and pulling in data from a predefined target file. But the seemingly benign nature of the class is nothing but an illusion, as it blatantly instantiates the serializer in its constructor!

FileStorage的行为可以归结为只是从预定义的目标文件中保存和提取数据。 但是类的看似良性本质不过是一种幻想,因为它在其构造函数中公然实例化了序列化器!

This form of “dictatorial” dependency lookup not only introduces a strong coupling between the class and its dependency, but it condemns testability to a quick death. The following code shows how the artifacts ripple through to client code when using this approach:

这种“独裁”依赖查找的形式不仅在类及其依赖之间引入了强大的耦合,而且将可测试性谴责为快速死亡。 以下代码显示了使用这种方法时工件如何传递到客户端代码:

<?php
$fileStorage = new FileStorage();
$fileStorage->write("This is a sample string.");
echo $fileStorage->read();

Feel free to run the code and it’ll work like a charm, that’s for sure. But we know the coupling is there just beneath the surface, not to mention the fact that there’s not a single clue about if the FileStorage has an internal dependency on another component, or how this one was acquired. Definitively, this is pretty much like an antipattern which goes against the grain when it comes to defining clean, expressive class APIs.

可以随意运行代码,它会像魅力一样工作,这是肯定的。 但是我们知道耦合只是存在于表面之下,更不用说一个事实,即FileStorage是否对另一个组件具有内部依赖性,或者该组件是如何获得的。 绝对地,这很像是反模式,在定义干净的,具有表现力的类API时违反了规则。

There’re a few additional approaches that are worth looking into that can improve the way the two previous classes interoperate with each other. For instance, we could be a bit bolder and inject a factory straight into the internals of FileStorage and let it create a serializer object when necessary, thus making the class’ dependency a little more explicit.

还有一些值得研究的其他方法可以改善前两个类之间的互操作方式。 例如,我们可以大胆一些,直接将工厂注入FileStorage的内部,并在必要时让它创建一个序列化器对象,从而使类的依赖关系更加明确。

延迟通过注入工厂创建依赖项 (Deferring the Creation of Dependencies through an Injected Factory)

Factories, in any of their forms, are nifty elements that allow us to nicely distill object construction from application logic. While such ability is quite remarkable in itself, it can be taken a bit further when mixed with the goodness of Dependency Injection.

任何形式的工厂都是极好的元素,它们使我们能够从应用程序逻辑中很好地提取对象构造。 虽然这种能力本身非常出色,但是与依赖注入的优点混合使用时,可以进一步提高。

If you’re the curious sort like I am, you’ll be wondering how to exploit the benefits provided by a factory for enhancing how the earlier FileStorage class looks up its collaborator, getting rid of the infamous “new” operator in its constructor. At a very basic level, the lookup process could be reformulated through the following factory class:

如果您像我一样好奇,那么您会想知道如何利用工厂提供的好处来增强早期的FileStorage类如何查找其协作者,从而摆脱其构造函数中臭名昭著的“ new”运算符。 从根本上讲,可以通过以下工厂类重新构造查找过程:

<?php
namespace LibraryDependencyInjection;
interface SerializerFactoryInterface
{
public function getSerializer();
}
<?php
namespace LibraryDependencyInjection;
use LibraryEncoderSerializer;
class SerializerFactory implements SerializerFactoryInterface
{
public function getSerializer() [
static $serializer;
if ($serializer === null) {
$serializer = new Serializer;
}
return $serializer;
}
}

Having at hand a dynamic factory charged with the task of creating the serializer on demand, now the FileStorage class can be refactored to accept a factory implementer:

拥有一个动态工厂负责按需创建序列化器的任务之后,现在可以重构FileStorage类以接受工厂实现者:

<?php
namespace LibraryFile;
use LibraryDependencyInjectionSerializerFactoryInterface;
class FileStorage
{
const DEFAULT_STORAGE_FILE = "data.dat";
protected $factory;
protected $file;
public function __construct(SerializerFactoryInterface $factory, $file = self::DEFAULT_STORAGE_FILE) {
$this->setFile($file);
$this->factory = $factory;
}
public function setFile($file) {
if (!is_file($file)) {
throw new InvalidArgumentException(
"The file $file does not exist.");
}
if (!is_readable($file) || !is_writable($file)) {
if (!chmod($file, 0644)) {
throw new InvalidArgumentException(
"The file $file is not readable or writable.");
}
}
$this->file = $file;
return $this;
}
public function read() {
try {
return $this->factory->getSerializer()->unserialize(
@file_get_contents($this->file));
}
catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
public function write($data) {
try {
return file_put_contents($this->file,
$this->factory->getSerializer()->serialize($data));
}
catch (Exception $e) {
throw new Exception($e->getMessage());
}
}
}

The functionality of FileStorage remains pretty much the same, but the tight coupling with the serializer has been broken by injecting an implementer of the SerializerFactoryInterface. This simple twist turns the class into a flexible and testable creature, which exposes a more expressive API, as is now easier to see from the outer world what dependencies it needs. The code below shows the result of these enhancements from the perspective of the client code:

FileStorage的功能几乎相同,但是通过注入SerializerFactoryInterface的实现程序,与SerializerFactoryInterface的紧密耦合已被打破。 这种简单的转变将类变成了一个灵活且可测试的生物,它公开了更具表现力的API,现在可以从外部世界更容易地看出它需要什么依赖。 下面的代码从客户端代码的角度显示了这些增强的结果:

<?php
$fileStorage = new FileStorage(new SerializerFactory());
$fileStorage->write("This is a sample string.");
echo $fileStorage->read();

Let’s not fall into blind, pointless optimism, though. It’s fair to say the refactored implementation of the storage module looks more appealing; this approach makes it trivial to switch out different factory implementations at runtime. But in my opinion, at least in this case, it’s a masked violation of the Law of Demeter since the injected factory acts like an unnecessary mediator to the module’s real collaborator.

但是,我们不要陷入盲目,毫无意义的乐观之中。 可以公平地说,重构的存储模块实现看起来更具吸引力。 这种方法使得在运行时切换出不同的工厂实现变得很简单。 但是我认为,至少在这种情况下,这是对戴米特律法的掩盖违反,因为注入的工厂对于模块的实际协作者而言就像是不必要的调停者。

This doesn’t mean that injected factories are a bad thing at all. But in most cases they should be used only for creating dependencies on demand, especially when the dependencies’ life cycles are shorter than that of the client class using them.

这并不意味着注入工厂根本不是一件坏事。 但是在大多数情况下,它们仅应用于创建按需的依赖关系,尤其是当依赖关系的生命周期短于使用它们的客户端类的生命周期时。

结束语 (Closing Remarks)

Quite often unfairly overlooked in favor of more “seductive” topics, managing class dependencies is unquestionably a central point of object-oriented design and whose underlying logic certainly goes much deeper than dropping a few “new” operators in factories, or worse, in stingy constructors that hide those dependencies from the outside world so they can’t be properly decoupled at will or tested in isolation.

人们常常不偏不倚地忽略了更多的“诱人”主题,而管理类依赖无疑是面向对象设计的中心点,其基本逻辑肯定比在工厂中丢掉一些“新”运算符要深得多,或者更糟的是,构造函数将那些依赖项隐藏在外部世界中,这样就不能随意地对其进行解耦或孤立地对其进行测试。

There’s no need to feel that all is lost, however, There exists a few additional approaches that can be utilized for handling in an efficient manner the way that classes are provided with their collaborators, including the use of the Service Locator pattern, raw Dependency Injection, and even appealing to the benefits of a Dependency Injection container. All these ones will be covered in detail in the follow up, so stay tuned!

不必感到一切都丢失了,但是,可以使用一些其他方法来有效地处理与合作者一起提供类的方式,包括使用服务定位器模式,原始依赖注入,甚至吸引了依赖注入容器的好处。 所有这些都将在后续内容中详细介绍,请继续关注!

Image via SvetlanaR / Shutterstock

图片来自SvetlanaR / Shutterstock

翻译自: https://www.sitepoint.com/managing-class-dependencies-1/

类继承和依赖注入的关系

类继承和依赖注入的关系_管理类依赖关系:依赖关系注入,服务定位符和工厂简介,第1部分...相关推荐

  1. mysql数据依赖关系_发现数据库对象的依赖关系

    SQL Server Management Studio中有一个很有意思的工具,可以查看某个对象的依赖和被依赖关系.如下图所示 假设,我们自己的程序也要实现这样的功能,那么该怎么做呢? 1. 首先,创 ...

  2. 简述机器指令与微指令之间的关系_技术动态 | 跨句多元关系抽取

    第一部分 概述 关系抽取简介 关系抽取是从自由文本中获取实体间所具有的语义关系.这种语义关系常以三元组 <E1,R,E2> 的形式表达,其中,E1 和E2 表示实体,R 表示实体间所具有的 ...

  3. 2013福建高职单招计算机类专业,2013福建高职单招_计算机类专业_知识试题(卷).doc...

    2013福建高职单招_计算机类专业_知识试题(卷).doc (13页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.9 积分  .. 计算机类专业基 ...

  4. 2020考研公共课_基础精讲课_管理类联考综合能力 联考逻辑(读书笔记)

    概念的内涵和外延 混淆概念=偷换概念 概念种类:集合概念和非集合概念,肯定概念和否定概念(又称负概念或矛盾概念) 概念关系:相容关系.包含关系.交叉关系.不相容关系(又称全异关系){分为:反对关系.矛 ...

  5. python关于类和对象说法正确的是_关于类和对象,下面说法正确的是?

    [判断题]温度越高,料液的粘度越小,扩散系数越大,可提高膜通量. [其它]我请武老师教我日语. [单选题]关于类和对象,下面说法正确的是? [连线题]已知系统顾客的平均到达强度为λ,服从泊松分布,顾客 ...

  6. python 分析人物关系_基于共现发现人物关系的python实现

    基于共现发现人物关系的python实现 1.共现关系 在文献计量学中,关键词的共词方法常用来确定该文献集所代表学科中各主题之间的关系.而在这里,我们需要通过分析一篇小说或剧本,来分析剧中各个角色之间的 ...

  7. 管理类联考笔试还是计算机考,【管理类联考】你那么努力,为何数学还是考不好?...

    数学绝对是众多MBA等管理类联考考生的一大痛点,更是文科生们的"噩梦",很多考生都认为在备考复习阶段花费了大量的时间精力来备战数学,然而联考时还是会失分,还是发挥的不好.由于管理类 ...

  8. core控制器属性注入的用处_了解ASP.NET Core 依赖注入,看这篇就够了

    DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET  Core的DI实现以及对实例 ...

  9. core控制器属性注入的用处_理解 ASP.NET Core 依赖注入

    DI在.NET Core里面被提到了一个非常重要的位置,介绍一下.NET  Core的DI实现以及对实例生命周期的管理,在控制台以及Mvc下如何使用DI,以及如何把默认的Service Contain ...

最新文章

  1. 剑指offer: 斐波那契数列 python 实现
  2. spring框架使用Quartz执行定时任务实例详解
  3. AS3.0(3)-函数;类;对象
  4. 使用ssh做端口转发
  5. python数据动画_[转载]Maya使用Python获取动画每帧的rotation数据
  6. 一张图理清ASP.NET Core启动流程
  7. 前端设置画布的高度_【后期修图】ps画布设置详解
  8. html 13 背景
  9. java矩阵相乘泛型_21.8 实例学习:泛型矩阵类
  10. airpin linux电脑,AirPinPcSender
  11. UVA 11137 Ingenuous Cubrency (背包水题)
  12. 微信公众号原主体已注销 如何办理账号迁移?
  13. 配备透明触摸屏 看3D全息投影概念手机
  14. java sencha_sencha编译出错
  15. 2021年美国大学生数学建模竞赛(题目详细介绍)
  16. 吃饭理论,抓沙理论,杯子理论——结构化学习,实现N+1,时间管理
  17. 《程序员修炼之道》给所有毕业生的18条建言
  18. BAT云战争新动向:收编“旧军”,占山为王
  19. Windows 注册表操作 reg 命令详解
  20. Echarts 页面多图自适应的解决办法

热门文章

  1. 决策树系列之一决策树的入门教程
  2. 到底什么是 路由器(router)、交换机(switch)
  3. 标注 画框 转写,亲亲,你做错了没?
  4. matlab 稀疏随机矩阵,Matlab 稀疏矩阵函数
  5. 微信小程序实现打开并下载服务器上面的pdf文件到手机
  6. 42、使用mmrotate中k3det进行旋转目标检测,并进行mnn部署和ncnn部署
  7. 3dmax中的切角chamfer能用出什么花来吗?
  8. html导出excel合并单元格,JS导出EXCEL,动态设置单元格格式,合并单元格(横向或纵向)等操作...
  9. 4个工具,个个都是精品!修复图片视频画质超好用
  10. Java基础面试题简单总结