Single Responsibility Principle 单一职责原则
Introduction 介绍
The "SOLID" design principles, articulated by Robert "Uncle Bob" Martin, are five principles that provide a good foundation for sound application design. The five principles are:
罗伯特“鲍勃叔叔”马丁阐述了名为“坚实”的一些设计原则(译者注:看下面五个原则的首字母正是 SOLID)。这些都是制作完善的程序设计的优秀基础,一共有五个原则:
- The Single Responsibility Principle 单一职责原则
- The Open Closed Principle 开放封闭原则
- The Liskov Substitution Principle 里氏替换原则
- The Interface Segregation Principle 接口隔离原则
- The Dependency Inversion Principle 依赖反转原则
Let's explore each of these principles in more depth, and look at some code examples illustrating each principle. As we will see, each principle compliments the others, and if one principle falls, most, if not all, of the others do as well.
让我们深入探索一下,再看点代码样例来说明各个原则。我们将看到,每个原则之间都有联系。如果其中一个原则没有被遵循,那么其他大部分(可能不会是全部)的原则也会出问题。
In Action 实践
The Single Responsibility Principle states that a class should have one, and only one, reason to change. In other words, a class' scope and responsibility should be narrowly focused. As we have said before, ignorance is bliss when it comes to class responsibilities. A class should do its job, and should not be affected by changes to any of its dependencies.
单一职责原则规定一个类有且仅有一个理由使其改变。换句话说,一个类的功能边界和职责应当是十分狭窄且集中的。我们之前就提到过,在类的职责问题上,无知是福。一个类应当做它该做的事儿,并且不应当被它的依赖的任何变化所影响到。
Consider the following class:
考虑下列类:
<!-- lang: php -->
class OrderProcessor {
public function __construct(BillerInterface $biller)
{
$this->biller = $biller;
}
public function process(Order $order)
{
$recent = $this->getRecentOrderCount($order);
if($recent > 0)
{
throw new Exception('Duplicate order likely.');
}
$this->biller->bill($order->account->id, $order->amount);
DB::table('orders')->insert(array(
'account' => $order->account->id,
'amount' => $order->amount,
'created_at'=> Carbon::now()
));
}
protected function getRecentOrderCount(Order $order)
{
$timestamp = Carbon::now()->subMinutes(5);
return DB::table('orders')->where('account', $order->account->id)
->where('created_at', '>=', $timestamps)
->count();
}
}
What are the responsibilities of the class above? Obviously, its name implies that it is responsible for processing orders. But, based on the getRecentOrderCount
method, we can also see that it is responsible for examining an account's order history in the database in order to detect duplicated orders. This extra validation responsibility means that we must change our order processor when our data store changes, as well as when our order validation rules change.
上面这个类的职责是什么?很显然顾名思义,它是用来处理订单的。不过由于getRecentOrderCount
这个方法的存在,这个类就有了在数据库中审查某帐号订单历史来看有没有重复订单的职责。这个额外的验证职责意味着当我们的存储方式改变或当订单验证规则改变时,我们的这个订单处理器也要跟着改变。
We should extract this responsibility into another class, such as an OrderRepository
:
我们必须将这个职责抽离出来放到另外的类里面,比如放到OrderRepository
:
<!-- lang:php -->
class OrderRepository {
public function getRecentOrderCount(Account $account)
{
$timestamp = Carbon::now()->subMinutes(5);
return DB::table('orders')->where('account', $account->id)
->where('created_at', '>=', $timestamp)
->count();
}
public function logOrder(Order $order)
{
DB::table('orders')->insert(array(
'account' => $order->account->id,
'amount' => $order->amount,
'created_at'=> Carbon::now()
));
}
}
Then, we can inject our repository into the OrderProcessor
, alleviating it of the responsibility of researching an account's order history:
然后我们可以将我们的资料库(译者注:OrderRepository )注入到OrderProcessor
里,帮后者承担起对账户订单历史的处理责任:
<!-- lang:php -->
class OrderProcessor {
public function __construct(BillerInterface $biller, OrderRepository $orders)
{
$this->biller = $biller;
$this->orders = $orders;
}
public function process(Order $order)
{
$recent = $this->orders->getRecentOrderCount($order->account);
if($recent > 0)
{
throw new Exception('Duplicate order likely.');
}
$this->biller->bill($order->account->id, $order->amount);
$this->orders->logOrder($order);
}
}
Now that we have abstracted our order data gathering responsibilities, we no longer have to change our OrderProcessor
when the method of retrieving and logging orders changes. Our class responsibilities are more focused and narrow, providing for cleaner, more expressive code, and a more maintainable application.
现在我们提取出了收集订单数据的责任,当读取和写入订单的方式改变时,我们不再需要修改OrderProcessor
这个类了。我们的类的职责更加的专注和精确,这提供了一个更干净、更有表现力的代码,同时也是更容易维护的代码。
Keep in mind, the Single Responsibility Principle isn't just about less line of code, it's about writing classes that have a narrow responsibility, and a cohesive set of available methods, so make sure that all of the methods in a class are aligned with the overall responsibility of that class. After building a library of small, clear classes with well defined responsibilities, our code will be more decoupled, easier to test, and friendlier to change.
请记住,单一职责原则的关键不仅仅是让函数变短,而是写出职责更精确更高内聚的类,所以要确保类里面所有的方法都属于该类的职责之下的。在建立一个小巧、清晰且职责明确的类库以后,我们的代码会更加解耦,更容易测试,并且更易于更改。