🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
### **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: * 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. We should extract this responsibility into another class, such as an ``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: ~~~ <!-- 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. 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.