文档章节

From Apprentice To Artisan 翻译 18

zgldh
 zgldh
发布于 2015/03/18 22:41
字数 1590
阅读 1451
收藏 0

上一篇

Liskov Substitution Principle 里氏替换原则

Introduction 介绍

Don't worry, the Liskov Substitution Principle is a lot easier to understand than it sounds. This principle states that you should be able to use any implementation of an abstraction in any place that accepts that abstraction. But, let's make this a little simpler. In plain English, the principle states that if a class uses an implementation of an interface, it must be able to use any implementation of that interface without requiring any modifications.

别担心,里氏替换原则读起来吓人学起来简单。该原则要求:一个抽象的任意一个实现,可以被用在任何需要该抽象的地方。读起来绕口,用普通人的话来解释一下。该原则规定:如果某处代码使用了一个接口的一个实现类,那么在这里也可以直接使用该接口的任何其他实现类,不用做出任何修改。

Liskov Substitution Principle 里氏替换原则

This principle states that objects should be replaceable with instances of their sub-types without altering the correctness of that program.

该原则规定对象应该可以被该对象子类的实例所替换,并且不会影响到程序的正确性。

In Action 实践

To illustrate this principle, let's continue to use our OrderProcessor example from the previous chapter. Take a look at this method:

为了说明该原则,我们继续编写上一章节的OrderProcessor。看下面的方法:

<!-- lang:php -->
public function process(Order $order)
{
    // Validate order...
    $this->orders->logOrder($order);
}

Note that after the Order is validated, we log the order using the OrderReporsitoryInterface implementation. Let's assume that when our order processing business was young, we stored all of our orders in CSV format on the file system. Our only OrderRepositoryInterface implementation was a CsvOrderRepository. Now, as our order rate grows, we want to use a relational database to store them. So, let's look at a possible implementation of our new repository:

注意当我们的Order通过了验证,就被OrderRepositoryInterface的实现对象存储起来了。假设当我们的业务刚起步时,我们将订单存储在CSV格式的文件系统中。我们的OrderRepositoryInterface的实现类是CsvOrderRepository。现在,随着我们订单增多,我们想用一个关系数据库来存储订单。那么我们来看看新的订单资料库类该怎么编写吧:

<!-- lang:php -->
class DatabaseOrderRepository implements OrderRepositoryInterface {
    protected $connection;
    public function connect($username, $password)
    {
        $this->connection = new DatabaseConnection($username, $password);
    }

    public function logOrder(Order $order)
    {
        $this->connection->run('insert into orders values (?, ?)', array(
            $order->id, $order->amount
        ));
    }
}

Now, let's examine how we would have to consume this implementation:

现在我们来研究如何使用这个实现类:

<!-- lang:php -->
public function process(Order $order)
{
    // Validate order...

    if($this->repository instanceof DatabaseOrderRepository)
    {
        $this->repository->connect('root', 'password');
    }
    $this->repository->logOrder($order);
}

Notice that we are forced to check if our OrderRepositoryInterface is a database implementation from within our consuming processor class. If it is, we must connect to the database. This may not seem like a problem in a very small application, but what if the OrderRepositoryInterface is consumed by dozens of other classes? We would be forced to implement this "bootstrap" code in every consumer. This will be a headache to maintain and is prone to bugs, and if we forgot to update a single consumer our application will break.

注意在这段代码中,我们必须在资料库外部检查OrderRepositoryInterface的实例对象是不是用数据库实现的。如果是的话,则必须先连接数据库。在很小的应用中这可能不算什么问题,但如果OrderRepositoryInterface被几十个类调用呢?我们可能就要把这段“启动”代码在每一个调用的地方复制一遍又一遍。这让人非常头疼难以维护,非常容易出错误。一旦我们忘了将所有调用的地方进行同步修改,那程序恐怕就会出问题。

The example above clearly breaks the Liskov Substitution Principle. We were unable to inject an implementation of our interface without changing the consumer to call the connect method. So, now that we have identified the problem, let's fix it. Here is our new DatabaseOrderRepository implementation:

很明显,上面的例子没有遵循里氏替换原则。如果不附加“启动”代码来调用connect方法,则这段代码就没法用。好了,我们已经找到问题所在,咱们修好他。下面就是新的DatabaseOrderRepository

<!-- lang:php -->
class DatabaseOrderRepository implements OrderRepositoryInterface {
    protected $connector;
    public function __construct(DatabaseConnector $connector)
    {
        $this->connector = $connector;
    }
    public function connect()
    {
        return $this->connector->bootConnection();
    }
    public function logOrder(Order $order)
    {
        $connection = $this->connect();
        $connection->run('insert into orders values (?, ?)', array(
            $order->id, $order->amount
        ));
    }
}

Now our DatabaseOrderRepository is managing the connection to the database, and we can remove our "bootstrap" code from the consuming OrderProcessor:

现在DatabaseOrderRepository掌管了数据库连接,我们可以把“启动”代码从OrderProcessor移除了:

<!-- lang:php -->
public function process(Order $order)
{
    // Validate order...
    
    $this->repository->logOrder($order);
}

With this modification, we can now use our CsvOrderRepository or DatabaseOrderRepository without modifying the OrderProcessor consumer. Our code adheres to the Liskov Substitution Principle! Take note that many of the architecture concepts we have discussed are related to knowledge. Specifically, the knowledge a class has of its "surrounding", such as the peripheral code and dependencies that help a class do its job. As you work towards a robust application architecture, limiting class knowledge will be a recurring and important theme.

这样一改,我们就可以想用CsvOrderRepository也行,想用DatabaseOrderRepository也行,不用改OrderProcessor一行代码。我们的代码终于实现了里氏替换原则!要注意,我们讨论过的许多架构概念都和知识相关。具体讲,知识就是一个类和它所具有的周边领域,比如用来帮助类完成任务的外围代码和依赖。当你要制作一个容错性强大的应用架构时,限制类的知识是一种常用且重要的手段。

Also note the consequence of violating the Liskov Substitution principle with regards to the other principle we have covered. By breaking this principle, the Open Closed principle must also be broken, as, if the consumer must check for instances of various child classes, this consumer must be changed each time there is a new child class.

还要注意如果不遵守里氏替换原则,那后果可能会影响到我们之前已经讨论过的其他原则。不遵守里氏替换原则,那么开放封闭原则一定也会被打破。因为,如果调用者必须检查实例属于哪个子类的,那一旦有个新的子类,调用者就得做出改变。(译者注:这就违背了对修改封闭的原则。)

Watch For Leaks 小心遗漏

You have probably noticed that this principle is closely related to the avoidance of "leaky abstractions", which were discussed in the previous chapter. Our database repository's leaky abstraction was our first clue that the Liskov Substitution Principle was being broken. Keep an eye out for those leaks!

你可能注意到这个原则和上一章节提到的“抽象的漏洞”密切相关。我们的数据库资料库的抽象漏洞就是没有遵守里氏替换原则的第一迹象。要留意那些漏洞!

下一篇

© 著作权归作者所有

共有 人打赏支持
zgldh
粉丝 95
博文 35
码字总数 46344
作品 2
高级程序员
私信 提问
From Apprentice To Artisan 翻译 20

上一篇 Dependency Inversion Principle 依赖反转原则 Introduction 介绍 We have reached our final destination in our overview of the five SOLID design principles! The final princip......

zgldh
2015/03/19
0
4
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
0
0
From Apprentice To Artisan 翻译 08

上一篇 As Organizer 作为管理工具 One of the keys to building a well architected Laravel application is learning to use service providers as an organizational tool. When you are ......

zgldh
2014/12/09
0
0
From Apprentice To Artisan 翻译 02

上一篇 Taking It Further 更进一步 Let's consider another example to solidify our understanding. Perhaps we want to notify customers of charges to their account. We'll define tw......

zgldh
2014/08/22
0
1
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
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Ubuntu常用操作

查看端口号 netstat -anp |grep 端口号 查看已使用端口情况 netstat -nultp(此处不用加端口号) netstat -anp |grep 82查看82端口的使用情况 查找被占用的端口: netstat -tln netstat -tl...

hc321
昨天
0
0
网站cdn的静态资源突然访问变的缓慢,问题排查流程

1.首先我查看了一下是否自己的网络问题,通过对比其他资源的访问速度和下载速度,确认不是 2.通过ping 和 tracert 判断cdn域名能否正常访问,(最后回想感觉这一步可以省略,因为每次最终能访...

小海bug
昨天
0
0
Mybatis 学习笔记四 MyBatis-Plus插件

Mybatis 学习笔记四 MyBatis-Plus插件 maven依赖 <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus</artifactId> <ve......

晨猫
昨天
2
0
小白带你认识netty(二)之netty服务端启动(下)

承接上一篇小白带你认识netty(二)之netty服务端启动(上),还剩下两步骤:3、注册Selector:将Channel注册到Selector上 和 4、端口的绑定:服务端端口的监听。 3、注册Selector:将Chann...

天空小小
昨天
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部