博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
From Apprentice To Artisan 翻译 20
阅读量:7015 次
发布时间:2019-06-28

本文共 6460 字,大约阅读时间需要 21 分钟。

  hot3.png

Dependency Inversion Principle 依赖反转原则

Introduction 介绍

We have reached our final destination in our overview of the five SOLID design principles! The final principle is the Dependency Inversion principle, and it states that high-level code should not depend on low-level code. Instead, high-level code should depend on an abstraction layer that serves as a "middle-man" between the high and low-level code. A second aspect to the principle is that abstractions should not depend upon details, but rather details should depend upon abstractions. If this all sounds extremely confused, don't worry. We'll cover both aspects of this principle below.

在整个“坚实”原则概述的旅途中,我们到达最后一站了!最后的原则是依赖反转原则,它规定高等级的代码不应该依赖(迁就)低等级的代码。首先,高等级的代码应该依赖(遵从)着抽象层,抽象层就像是“中间人”一样,负责连接着高等级和低等级的代码。其次,抽象定义不应该依赖(迁就)着具体实现,但具体实现应该依赖(遵从)着抽象定义。如果这些东西让你极端困惑,别担心。接下来我们会将这两方面统统介绍给你。

Dependency Inversion Principle 依赖反转原则

This principle states that high-level code should not depend on low-level code, and that abstractions should not depend upon details.

该原则要求高等级代码不应该迁就低等级代码,抽象定义不应该迁就具体实现。

In Action 实践

If you have already read prior chapters of this book, you already have a good grasp of the Dependency Inversion principle! To illustrate the principle, let's consider the following class:

如果你已经读过了本书前面几个章节,你就已经很好掌握了依赖反转原则!为了说明本原则,让我们考虑下面这个类:

class Authenticator {        public function __construct(DatabaseConnection $db)        {            $this->db = $db;        }        public function findUser($id)        {            return $this->db->exec('select * from users where id = ?', array($id));        }        public function authenticate($credentials)        {            // Authenticate the user...        }    }

As you might have guessed, the Authenticator class is responsible for finding and authenticating users. Let's examine the constructor of this class. You will see that we are type-hinting a DatabaseConnection instance. So, we're tightly coupling our authenticator to the database, and essentially saying that users will always only be provided out of a relational SQL database. Furthermore, our high-level code (the Authenticator) is directly depending on low-level code (the DatabaseConnection).

你可能猜到了,Authenticator就是用来查找和验证用户的。继续研究它的构造函数。我们发现它使用了类型提示,要求传入一个DatabaseConnection对象,所以该验证类和数据库被紧密的联系在一起。而且基本上讲,这个数据库还只能是关系数据库。从而可知,我们的高级代码(Authenticator)直接的依赖着低级代码(DatabaseConnection)。

First, let's discuss "high-level" and "low-level" code. Low-level code implements basic operations like reading files from a disk, or interaction with a database. High-level code encapsulates complex logic and relies on the low-level code to function, but should not be directly coupled to it. Instead, the high-level code should depend on an abstraction that sits on top of the low-level code, such as an interface. Not only that, but the low-level code should also depend upon an abstraction. So, let's write an interface that we can use within our Authenticator:

首先我们来谈谈“高级代码”和“低级代码”。低级代码用于实现基本的操作,比如从磁盘读文件,操作数据库等。高级代码用于封装复杂的逻辑,它们依靠低级代码来达到功能目的,但不能直接和低级代码耦合在一起。取而代之的是高级代码应该依赖着低级代码的顶层抽象,比如接口。不仅如此,低级代码应当依赖着抽象。 所以我们来写个Authenticator可以用的接口:

interface UserProviderInterface {        public function find($id);        public function findByUsername($username);    }

Next let's inject an implementation of this interface into our Authenticator:

接下来我们将该接口注入到Authenticator里面:

class Authenticator {        public function __construct(UserProviderInterface $users, HasherInterface $hash)        {            $this->hash = $hash;            $this->users = $users;        }        public function findUser($id)        {            return $this->users->find($id);        }        public function authenticate($credentials)        {            $user = $this->users->findByUsername($credentials['username']);            return $this->hash->make($credentials['password']) == $user->password;        }    }

After making these changes, our Authenticator now relies on two high-level abstractions: UserProviderInterface and HasherInterface. We are free to inject any implementation of these interfaces into the Authenticator. For example, if our users are now stored in Redis, we can write a RedisUserProvider which implements the UserProviderInterface contract. The Authenticator is no longer depending directly on low-level storage operations.

做了这些小改动后,Authenticator现在依赖于两个高级抽象:UserProviderInterfaceHasherInterface。我们可以向Authenticator自由的注入这俩接口的任何实现类。比如,如果我们的用户存储在Redis里面,我们只需写一个RedisUserProvider来实现UserProviderInterface接口即可。Authenticator不再依赖着具体的低级别的存储操作了。

Furthermore, our low-level code now depends on the high-level UserProviderInterface abstraction, since it implements the interface itself:

此外,由于我们的低级别代码实现了UserProviderInterface接口,则我们说该低级代码依赖着这个接口。

class RedisUserProvider implements UserProviderInterface {        public function __construct(RedisConnection $redis)        {            $this->redis = $redis;        }        public function find($id)        {            $this->redis->get('users:'.$id);        }        public function findByUsername($username)        {            $id = $this->redis->get('user:id:'.$username);            return $this->find($id);        }    }

Inverted Thinking 反转的思维

Applying this principle inverts the way many developers design applications. Instead of coupling high-level code directly to low-level code in a "top-down" fashion, this principle states that both high and low-level code depend upon a high-level abstraction.

贯彻这一原则会反转好多开发者设计应用的方式。不再将高级代码直接和低级代码以“自上而下”的方式耦合在一起,这个原则提出无论高级还是低级代码都要依赖于一个高层次的抽象。

Before we "inverted" the dependencies of our Authenticator, it could not be used with anything other than a database storage system. If we changed storage system, our Authenticator would also have to be modified, in violation of the Open Closed principle. Again, as we have seen before, multiple principles usually stand or fall together.

在我们没有反转Authenticator的依赖之前,它除了使用数据库存储系统别无选择。如果我们改变了存储系统,Authenticator也需要被修改,这就违背了开放封闭原则。我们又一次看到,这些设计原则通常一荣俱荣一损俱损。

After forcing the Authenticator to depend upon an abstraction over the storage layer, we can use it with any storage system that implements our UserProviderInterface contract without any modifications to the Authenticator itself. The conventional dependency chain has been inverted and our code is much more flexible and welcoming of change!

通过强制让Authenticator依赖着一个存储抽象层,我们就可以使用任何实现了UserProviderInterface接口的存储系统,且不用对Authenticator本身做任何修改。传统的依赖关系链已经被反转了,代码变得更灵活,更加无惧变化!


转载于:https://my.oschina.net/zgldh/blog/389243

你可能感兴趣的文章
课程2:《黑马程序员_Java基础视频-深入浅出精华版》-视频列表-
查看>>
TP4056大电流1A使用注意事项
查看>>
java代理模式之静态代理
查看>>
ASP.NET MVC5+EF6+EasyUI 后台管理系统(80)-自由桌面
查看>>
Java常考面试题(四)
查看>>
前端学数据库之记录操作
查看>>
学习Javascript闭包(Closure)
查看>>
【Todo】git的fast forward & git命令学习 & no-ff
查看>>
如何远程关闭一个ASP.NET Core应用?
查看>>
共享内存mmap学习 及与 shmxxx操作的区别
查看>>
你性格那么软,总是经常改变想法
查看>>
Summary of Critical and Exploitable iOS Vulnerabilities in 2016
查看>>
NeHe OpenGL教程 第十七课:2D图像文字
查看>>
查看Linux系统版本与位数
查看>>
更换CentOS7的下载源为阿里云
查看>>
退出Android程序时清除所有activity的实现方法
查看>>
eclipse注释模板设置(未整理)
查看>>
Hibernate框架 主配置文件(Hibernate.cfg.xml)基本
查看>>
学习SpringMVC——从HelloWorld开始
查看>>
Say Hello to ConstraintLayout
查看>>