SOLID 클래스의 원리 SOLID



1. S: 단일 책임 원칙(SRP)

2. O: 개방/폐쇄 원칙(OCP)

3. L: 리스코프 대체 원칙(LSP)

4. I: 인터페이스 분리 원칙(ISP)

5. D: 종속성 반전 원칙(DIP)

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)🎜
  • 🎜I: 인터페이스 분리 원칙(ISP) 🎜
  • 🎜D: 종속성 역전 원칙(DIP)🎜
🎜🎜 1. 단일 책임 원칙🎜🎜단일 책임 원칙( 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

好:

不是每一个工人都是雇员,但是每一个雇员都是一个工人

rrreee

5. 依赖倒置原则

Dependency Inversion Principle (DIP)

인터페이스 분리 원칙의 의미는 "호출자가 필요하지 않은 인터페이스에 의존하도록 강요해서는 안 됩니다"입니다.

예 이 원칙을 보여주는 명확한 예가 있습니다. 클래스에 많은 수의 설정이 필요한 경우 호출자에게 많은 수의 옵션을 설정하도록 요구하지 않는 것이 편리합니다. 일반적으로 모든 설정이 필요하지 않기 때문입니다. 설정을 선택 사항으로 지정하면 "두꺼운 인터페이스"를 방지하는 데 도움이 됩니다

나쁨:

rrreee

좋음:

모든 직원이 직원은 아니지만 모든 직원은 직원입니다

rrreee< a name="5"> 5. 종속성 역전 원칙

종속성 역전 원칙(DIP)

이 원칙은 두 가지 기본 사항을 보여줍니다.

상위 수준 모듈은 하위 수준 모듈에 종속되어서는 안 되며, 모두 하위 수준 모듈에 종속되어야 합니다. 추상화🎜🎜추상은 구현에 의존해서는 안 되고, 구현은 추상화에 의존해야 합니다.🎜🎜처음에는 다소 모호해 보일 수 있지만, Symfony와 같은 PHP 프레임워크를 사용해 본 적이 있다면 DI(종속성 주입)를 만나보세요. 이 개념의 구현. 정확히 동일한 개념은 아니지만 종속성 반전 원칙은 구현 세부 사항과 고차 모듈 생성을 저차 모듈과 분리합니다. 이는 종속성 주입(DI)을 사용하여 달성할 수 있습니다. 가장 큰 장점은 모듈을 서로 분리한다는 것입니다. 결합은 리팩토링을 어렵게 만들고 매우 나쁜 개발 모델입니다. 🎜🎜🎜나쁨:🎜🎜rrreee🎜🎜좋음:🎜🎜rrreee🎜🎜🎜🎜 🎜