https://khalilstemmler.com/articles/solid-principles/solid-typescript/#Liskov-Substition-Principle
SOLID 정의
- SOLID
- S: Single Responsibility Principle
- O: Open-Closed Principle
- L: Liskov-Substitution Principle
- I: Interface Segregation Principle
- D : Dependency Inversion Principle
- 정의
- 기능/클래스 구성을 안전하고, 유지보수에 좋고, 유연하게 할 수 있는 소프트웨어 설계 원칙
- 배경
- 과거에 작성된 코드가 현재 needs에 맞지 않을 경우, 코드 변경은 cost가 큼
- 따라서, 우리는 코드를 변경할 수 있도록 작성해야함
- 배우는 점
- Test 가능한 코드 작성
- 이해하기 쉬운 코드 작성
- 찾기 쉬운 코드 작성
- 의도한 동작만 하는 코드 작성
- 빠르게 수정하고 확장하는 코드 작성 (+버그 없이)
- Policy(Rule)와 Detail(Implementation)이 분리된 코드 작성
- Swap이 가능한 코드 작성
Single Responsibility Principle (단일 책임 원칙)
Class 또는 function은 변경시 단 한가지 이유만 존재해야한다
- 나쁜 예제
- 3개 부서의 Employee가 존재
- HR
- Accounting
- IT - SRP violation 예시
- Employee 클래스의 각 메서드에서 HR / Accounting / IT 로직을 모두 구현
- 3개 부서의 Employee가 존재
// https://khalilstemmler.com/articles/solid-principles/solid-typescript/#Liskov-Substition-Principle
class Employee {
public calculatePay (): number {
// implement algorithm for hr, accounting and it
}
public reportHours (): number {
// implement algorithm for hr, accounting and it
}
public save (): Promise<any> {
// implement algorithm for hr, accounting and it
}
}
- 안좋은 이유
- 하나의 class에 여러 부서의 알고리즘이 위치
- 한 부서의 알고리즘을 수정할 때, 다른 부서의 알고리즘에 영향을 줄 수 있음
- 이러한 작업에서 if / switch가 남발할 수 있음
- switch문의 코드가 길어질 경우, switch를 여러 class로 나눠 refactoring해야할 시그널일 수 있음
- 좋은 예제
- 추상클래스인 Employee를 각 부서가 상속받아 구현
- 한 부서의 알고리즘 변경을 위한 독립적인 공간이 각각 존재
- [중요] 애플리케이션을 사용하는 사용자(HR/Accounting/IT)에 따라 책임이 분리되어있음
// https://khalilstemmler.com/articles/solid-principles/solid-typescript/#Liskov-Substition-Principle
abstract class Employee {
// This needs to be implemented
abstract calculatePay (): number;
// This needs to be implemented
abstract reportHours (): number;
// let's assume THIS is going to be the
// same algorithm for each employee- it can
// be shared here.
protected save (): Promise<any> {
// common save algorithm
}
}
class HR extends Employee {
calculatePay (): number {
// implement own algorithm
}
reportHours (): number {
// implement own algorithm
}
}
class Accounting extends Employee {
calculatePay (): number {
// implement own algorithm
}
reportHours (): number {
// implement own algorithm
}
}
class IT extends Employee {
...
}
Open-Closed Principle (개방-폐쇄 원칙)
소프트웨어는 확장에 열려있고 수정에 닫혀있어야한다
- 정의
- 새로운 기능을 추가할 때, 기존 코드의 수정을 필요로 하면 안됨
- loose coupling을 위해 policy와 detail의 분리하여, high-level 컴포넌트를 low-level detail의 변경으로부터 지키는 것
- 방법
- interface / abstract class에서 구현해야할 High-level Policy 명시
- concrete class에서 detail 구현
- 나쁜 예제
- SendGrid를 사용하여 email을 보내는 서비스 구현을 요청 받음
- SendGridService라는 concrete class를 구현 - 3달 뒤, SendGrid가 너무 비싸서 MailChimp로 변경 작업이 필요
- 다시 MailChimpService라는 concrete class를 구현
- 이 때, 이를 교체하는 cost가 큼
- SendGrid를 사용하여 email을 보내는 서비스 구현을 요청 받음
- 좋은 예제
- Mail service와 관련된 interface를 정의
- 실제 구현은 각각에게 맡김
- 참고1
- Dependency Inversion Principle과 밀접한 관련
- concretion 대신 interface에 의존한다는 점에서 - Liskov-Substitution Principle과 밀접한 관련
- type/interface에 의존하면 swap이 가능하다는 점에서
- Dependency Inversion Principle과 밀접한 관련
- 참고2
- 아키텍쳐 관점에서 business logic을 실행하는 Use Case가 가장 high-level 컴포넌트
Liskov-Substitution Principle (리스코브-치환 법칙)
하나의 implementation을 다른 것으로 swap이 가능해야한다
- 좋은 예제
- 위 IMailService interface를 SendGridService, MailChimpService 등이 구현
- "Dependency Injection"을 통해, 우리 class에서 하나의 concrete class가 아닌 interface를 참조
- 다양한 concretion으로 swap 가능
Interface Segregation Principle (인터페이스 분리 원칙)
class가 필요없는 것에 의존하지 말아야한다
- 정의
- 고유한 기능으로 인터페이스를 나누어야함
- High-level policy를 분리하고, 이를 결합하여 low-level detail을 구현
https://blog.itcode.dev/posts/2021/08/16/interface-segregation-principle
- 나쁜 예제
- 스마트폰을 정의하는 abstract class(SmartPhone)
- 통화 (call)
- 메시지 (message)
- 무선충전 (wirelessCharge)
- AR (ar)
- 생체인식 (biometrics) - 갤럭시 S20은 위 기능들을 모두 지원
- 그런데 스마트폰인 갤럭시 S2에는 "무선충전/AR/생체인식" 기능을 포함하지 않음
- 상속할 경우 불필요한 구현/Overriding이 필요
- "통화/메시지" 기능은 포함
- 스마트폰을 정의하는 abstract class(SmartPhone)
// https://blog.itcode.dev/posts/2021/08/16/interface-segregation-principle
/**
* 스마트폰 추상 객체
*
* @author RWB
* @since 2021.08.16 Mon 16:48:03
*/
abstract public class SmartPhone
{
/**
* 통화 함수
*
* @param number: [String] 번호
*/
public void call(String number)
{
System.out.println(number + " 통화 연결");
}
/**
* 문자 메시지 전송 함수
*
* @param number: [String] 번호
* @param text: [String] 내용
*/
public void message(String number, String text)
{
System.out.println(number + ": " + text);
}
/**
* 무선충전 함수
*/
public void wirelessCharge()
{
System.out.println("무선 충전");
}
/**
* AR 함수
*/
public void ar()
{
System.out.println("AR 기능");
}
/**
* 생체인식 추상 함수
*/
abstract public void biometrics();
}
- 좋은 예제 (해결 방법)
- SmartPhone에 스마트폰의 공통된 최소한의 기능(통화/메시지)만 남김
- 나머지 기능(무선충전/AR/생체인식)을 interface로 정의
- 갤럭시 S20에서 SmartPhone을 포함한 모든 기능을 implement
// https://blog.itcode.dev/posts/2021/08/16/interface-segregation-principle
/**
* 무선충전 인터페이스
*
* @author RWB
* @since 2021.08.16 Mon 18:23:33
*/
public interface WirelessChargable
{
/**
* 무선충전 추상 함수
*/
void wirelessCharge();
}
/**
* AR 인터페이스
*
* @author RWB
* @since 2021.08.16 Mon 18:24:29
*/
public interface ARable
{
/**
* AR 추상 함수
*/
void ar();
}
/**
* 생체인식 인터페이스
*
* @author RWB
* @since 2021.08.16 Mon 18:25:08
*/
public interface Biometricsable
{
/**
* 생체인식 추상 함수
*/
void biometrics();
}
Dependency Inversion Principle (의존 역전 원칙)
Abstraction은 detail을 의존하면 안된다.
Detail은 Abstraction을 의존해야한다.
- 정리
- Abstraction
- interface
- abstract class - detail
- concrete class
- Abstraction
- 나쁜 예제
- interface의 메서드 파라미터에 concrete class(PrettyEmail)를 포함
interface IMailService {
// refering to concrete "PrettyEmail" and "ShortEmailTransmissionResult" from an abstraction
sendMail(email: PrettyEmail): Promise<ShortEmailTransmissionResult>
}
- 좋은 예제
- 위 PrettyEmail의 interface로 수정
class SendGridEmailService implements IMailService {
// concrete class relies on abstractions
sendMail(email: IMail): Promise<IEmailTransmissionResult> {
}
}
'CS > 언어' 카테고리의 다른 글
[JAVA] 객체지향 설계 - 객체지향 4대 특성 (0) | 2022.06.26 |
---|---|
[JAVA] Delegation & Composition & Aggregation (0) | 2022.06.24 |
[Java] 이중 콜론 연산자 (::) (0) | 2022.03.31 |
[JAVA] Hash와 Thread-Safe (0) | 2022.01.19 |
[JAVA] ArrayList에서 특정 값을 가진 원소들 찾기 (0) | 2022.01.14 |