SOLID 클래스의 원리 SOLID
SOLID
SOLID
는 기억하기 쉬운 약어입니다. Robert Martin이 명명한 5가지 가장 중요한 객체 지향 코딩 설계 원칙을 나타내는 Michael Feathers 작성SOLID
是Michael Feathers推荐的便于记忆的首字母简写,它代表了Robert Martin命名的最重要的五个面对对象编码设计原则
S: 单一职责原则 (SRP)
O: 开闭原则 (OCP)
L: 里氏替换原则 (LSP)
I: 接口隔离原则 (ISP)
D: 依赖倒置原则 (DIP)
1. 单一职责原则
Single Responsibility Principle (SRP)
正如在Clean Code所述,"修改一个类应该只为一个理由"。 人们总是易于用一堆方法塞满一个类,如同我们只能在飞机上 只能携带一个行李箱(把所有的东西都塞到箱子里)。这样做 的问题是:从概念上这样的类不是高内聚的,并且留下了很多 理由去修改它。将你需要修改类的次数降低到最小很重要。 这是因为,当有很多方法在类中时,修改其中一处,你很难知 晓在代码库中哪些依赖的模块会被影响到。
坏:
class UserSettings { private $user; public function __construct(User $user) { $this->user = $user; } public function changeSettings(array $settings): void { if ($this->verifyCredentials()) { // ... } } private function verifyCredentials(): bool { // ... } }
好:
class UserAuth { private $user; public function __construct(User $user) { $this->user = $user; } public function verifyCredentials(): bool { // ... } } class UserSettings { private $user; private $auth; public function __construct(User $user) { $this->user = $user; $this->auth = new UserAuth($user); } public function changeSettings(array $settings): void { if ($this->auth->verifyCredentials()) { // ... } } }
2. 开闭原则
Open/Closed Principle (OCP)
正如Bertrand Meyer所述,"软件的工件( classes, modules, functions
等) 应该对扩展开放,对修改关闭。" 然而这句话意味着什么呢?这个原则大体上表示你 应该允许在不改变已有代码的情况下增加新的功能
坏:
abstract class Adapter { protected $name; public function getName(): string { return $this->name; } } class AjaxAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = 'ajaxAdapter'; } } class NodeAdapter extends Adapter { public function __construct() { parent::__construct(); $this->name = 'nodeAdapter'; } } class HttpRequester { private $adapter; public function __construct(Adapter $adapter) { $this->adapter = $adapter; } public function fetch(string $url): Promise { $adapterName = $this->adapter->getName(); if ($adapterName === 'ajaxAdapter') { return $this->makeAjaxCall($url); } elseif ($adapterName === 'httpNodeAdapter') { return $this->makeHttpCall($url); } } private function makeAjaxCall(string $url): Promise { // request and return promise } private function makeHttpCall(string $url): Promise { // request and return promise } }
好:
interface Adapter { public function request(string $url): Promise; } class AjaxAdapter implements Adapter { public function request(string $url): Promise { // request and return promise } } class NodeAdapter implements Adapter { public function request(string $url): Promise { // request and return promise } } class HttpRequester { private $adapter; public function __construct(Adapter $adapter) { $this->adapter = $adapter; } public function fetch(string $url): Promise { return $this->adapter->request($url); } }
3. 里氏替换原则
Liskov Substitution Principle (LSP)
- S: 단일 책임 원칙(SRP)
< /li>
- O: 개방-폐쇄 원칙(OCP)🎜
- 🎜L: 리스코프 대체 원칙(LSP)🎜 li>
- 🎜I: 인터페이스 분리 원칙(ISP) 🎜
- 🎜D: 종속성 역전 원칙(DIP)🎜
단일 책임 원칙( SRP)
🎜🎜Clean Code에 명시된 것처럼 "클래스를 수정하는 이유는 한 가지여야 합니다." 비행기에 여행가방 하나만 가지고 다닐 수 있는 것처럼(모든 것을 여행가방에 담는다) 클래스에 여러 메서드를 채우는 것은 항상 쉽습니다. 문제는 개념적으로 그러한 클래스가 응집력이 높지 않고 수정해야 할 이유가 많다는 것입니다. 클래스를 수정해야 하는 횟수를 최소화하는 것이 중요합니다. 클래스에 여러 개의 메서드가 있는 경우 그 중 하나를 수정하면 코드 베이스의 어떤 종속 모듈이 영향을 받을지 알기 어렵기 때문입니다. 🎜🎜나쁨:🎜class Rectangle { protected $width = 0; protected $height = 0; public function setWidth(int $width): void { $this->width = $width; } public function setHeight(int $height): void { $this->height = $height; } public function getArea(): int { return $this->width * $this->height; } } class Square extends Rectangle { public function setWidth(int $width): void { $this->width = $this->height = $width; } public function setHeight(int $height): void { $this->width = $this->height = $height; } } function printArea(Rectangle $rectangle): void { $rectangle->setWidth(4); $rectangle->setHeight(5); // BAD: Will return 25 for Square. Should be 20. echo sprintf('%s has area %d.', get_class($rectangle), $rectangle->getArea()).PHP_EOL; } $rectangles = [new Rectangle(), new Square()]; foreach ($rectangles as $rectangle) { printArea($rectangle); }🎜좋음:🎜
interface Shape { public function getArea(): int; } class Rectangle implements Shape { private $width = 0; private $height = 0; public function __construct(int $width, int $height) { $this->width = $width; $this->height = $height; } public function getArea(): int { return $this->width * $this->height; } } class Square implements Shape { private $length = 0; public function __construct(int $length) { $this->length = $length; } public function getArea(): int { return $this->length ** 2; } } function printArea(Shape $shape): void { echo sprintf('%s has area %d.', get_class($shape), $shape->getArea()).PHP_EOL; } $shapes = [new Rectangle(4, 5), new Square(5)]; foreach ($shapes as $shape) { printArea($shape); }🎜🎜2. 개방/폐쇄 원칙🎜🎜🎜
개방/폐쇄 원칙(OCP)
🎜🎜Bertrand Meyer가 말했듯이 "소프트웨어의 아티팩트( 클래스, 모듈, 함수
등)는 확장을 위해 열려 있어야 하고 수정을 위해 닫혀 있어야 합니다." 문장은 무엇을 의미합니까? 이 원칙은 일반적으로 기존 코드를 변경하지 않고 새로운 기능을 추가할 수 있도록 허용해야 함을 의미합니다. 🎜🎜나쁨:🎜interface Employee { public function work(): void; public function eat(): void; } class HumanEmployee implements Employee { public function work(): void { // ....working } public function eat(): void { // ...... eating in lunch break } } class RobotEmployee implements Employee { public function work(): void { //.... working much more } public function eat(): void { //.... robot can't eat, but it must implement this method } }🎜좋음:🎜
interface Workable { public function work(): void; } interface Feedable { public function eat(): void; } interface Employee extends Feedable, Workable { } class HumanEmployee implements Employee { public function work(): void { // ....working } public function eat(): void { //.... eating in lunch break } } // robot can only work class RobotEmployee implements Workable { public function work(): void { // ....working } }🎜 🎜🎜< a name=" 3">🎜3.리스코프 대체 원리🎜🎜
리스코프 대체 원리(LSP)
🎜🎜간단한 원리이지만 이해하기 어려운 용어를 사용하고 있습니다. 공식적인 정의는 "S가 T의 하위 유형인 경우 프로그램의 원래 설정된 속성(검사, 작업 실행 등)을 변경하지 않고 T 유형의 모든 객체를 S 유형의 객체로 대체할 수 있습니다(예: S 객체를 사용하면 T) 객체를 대체할 수 있습니다." 이 정의는 이해하기가 더 어렵습니다 :-). 🎜이 개념에 대한 가장 좋은 설명은 부모 클래스와 자식 클래스가 있는 경우 원래 결과의 정확성을 변경하지 않고 부모 클래스와 자식 클래스를 교환할 수 있다는 것입니다. 여전히 다소 혼란스럽게 들리므로 고전적인 정사각형-직사각형 예를 살펴보겠습니다. 수학적으로 정사각형은 직사각형이지만 모델이 상속을 통해 "is-a" 관계를 사용하는 경우에는 그렇지 않습니다.
나쁨:
class Employee { public function work(): void { // ....working } } class Robot extends Employee { public function work(): void { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage(): void { $this->employee->work(); } }
좋음:
이 두 가지 유형의 쿼드를 별도로 처리하고 두 유형 모두에 맞는 보다 일반적인 하위 유형으로 대체하는 것이 좋습니다.
정사각형과 직사각형은 비슷해 보이지만 다릅니다. 정사각형은 마름모에 더 가깝고 직사각형은 평행사변형에 더 가깝습니다. 그러나 그들은 하위 유형이 아닙니다. 유사하지만 정사각형, 직사각형, 마름모, 평행사변형은 모두 고유한 특성을 지닌 서로 다른 모양입니다.
interface Employee { public function work(): void; } class Human implements Employee { public function work(): void { // ....working } } class Robot implements Employee { public function work(): void { //.... working much more } } class Manager { private $employee; public function __construct(Employee $employee) { $this->employee = $employee; } public function manage(): void { $this->employee->work(); } }
4. 인터페이스 분리 원칙
인터페이스 분리 원칙(ISP)
Interface Segregation Principle (ISP)
接口隔离原则表示:"调用方不应该被强制依赖于他不需要的接口"
有一个清晰的例子来说明示范这条原则。当一个类需要一个大量的设置项, 为了方便不会要求调用方去设置大量的选项,因为在通常他们不需要所有的 设置项。使设置项可选有助于我们避免产生"胖接口"
坏:
rrreee好:
不是每一个工人都是雇员,但是每一个雇员都是一个工人
rrreee5. 依赖倒置原则
Dependency Inversion Principle (DIP)
나쁨:
rrreee
좋음:
모든 직원이 직원은 아니지만 모든 직원은 직원입니다rrreee< a name="5"> 5. 종속성 역전 원칙
종속성 역전 원칙(DIP)