文档章节

From Apprentice To Artisan 翻译 20

zgldh
 zgldh
发布于 2015/03/19 23:28
字数 1613
阅读 2104
收藏 0

上一篇

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本身做任何修改。传统的依赖关系链已经被反转了,代码变得更灵活,更加无惧变化!


回到目录

© 著作权归作者所有

zgldh

zgldh

粉丝 102
博文 36
码字总数 47009
作品 2
高级程序员
私信 提问
加载中

评论(5)

OSC_IXECXD
OSC_IXECXD
我夜那么认为 应该多读几遍!
Cbping
Cbping
受益匪浅
guoguowd
guoguowd
看了两遍,收获颇丰。还得再来几遍。
郑方方
郑方方
21百看不腻
沱长
沱长
完了,还得在来一遍
From Apprentice To Artisan 翻译 16

上一篇 Single Responsibility Principle 单一职责原则 Introduction 介绍 The "SOLID" design principles, articulated by Robert "Uncle Bob" Martin, are five principles that provide ......

zgldh
2015/03/17
2.6K
0
From Apprentice To Artisan 翻译 10

上一篇 Application Structure 应用结构 Introduction 介绍 Where does this class belong? This question is extremely common when building applications on a framework. Many develope......

zgldh
2014/12/29
12.4K
1
From Apprentice To Artisan 翻译 04

上一篇 Reflect Resolution 反射解决方案 One of the most powerful features of the Laravel container is its ability to automatically resolve dependencies via reflection. Reflection......

zgldh
2014/10/08
3.4K
0
From Apprentice To Artisan 翻译 03

上一篇 The IoC Container 控制反转容器 Basic Binding 基础绑定 Now that we've learned about dependency injection, let's explore inversion of control containers.IoC containers make......

zgldh
2014/09/08
6.3K
0
From Apprentice To Artisan 翻译 17

上一篇 Open Closed Principle 开放封闭原则 Introduction 介绍 Over the life time of an application, more time is spent adding to the existing codebase rather than constantly addi......

zgldh
2015/03/18
2.1K
0

没有更多内容

加载失败,请刷新页面

加载更多

nginx主备模式笔记

(1)两台服务器 192.168.17.129 和 192.168.17.131 (2)在两台服务器安装 keepalived 安装 keepalived (1)使用 yum 命令进行安装 yum install keepalived –y (2)安装之后,在 etc 里面...

行者终成事
今天
4
0
004-Docker镜像

Docker镜像 一个通用的私有仓库,可以提升效率 Docker镜像构建分为两种,一种是手动构建,一种是Dockerfile(自动构建) 基于centos镜像构建手动制作nginx镜像 docker run --name testdocker -...

伟大源于勇敢的开始
今天
5
0
OSChina 周一乱弹 —— 我就加班,不去世不休息

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @巴拉迪维 :《For Forever》90后那些小鲜肉歌手中,好像只有花花的歌能吸引我,这小家伙对音乐的感觉真是天才一般!#今日歌曲推荐# 《For F...

小小编辑
今天
9
1
【领会要领】web前端-轻量级框架应用(jQuery基础)

作者 | Jeskson 来源 | 达达前端小酒馆 jquery的安装和语法,jquery的多种选择器,dom操作和jquery事件。 jQuery框架,简介,优势,安装,语法,jQuery选择器,id选择器,类选择器,标记选择...

达达前端小酒馆
今天
6
0
MySQL 常用命令

无须死记硬背,直接 copy 就好。 1. 查看目前 mysql 用户 select user,host,password from mysql.user; 2. 修改 root 密码(使用内置函数修改) set password for root@localhost=password('y......

HuaiAnGG
昨天
6
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部