-
[객체지향] 5대원칙 SOLID우아한 테크코스/테크코스 2020. 3. 21. 13:10반응형
SOLID - 객체지향 5대원칙
SOLID 원칙 - 유지보수와 확장이 쉬운 소프트웨어를 만드는데 적용할 수 있음
SRP(Single Responsibility Principle) - 단일 책임 원칙
OCP(Open-Close Principle) - 개방 폐쇄 원칙
LSP(Liskov Substitution Principle) - 리스코프 치환 원칙
DIP(Dependency Inversion Principle) - 의존 역전 원칙
ISP(Interface Segregation Principle) - 인터페이스 분리 원칙
Single Responsibility Priciple - 단일 책임 원칙
모든 클래스는 하나의 책임만 가지며, 클래스는 그 책임을 완전히 캡슐화해야 함. 클래스가 제공하는 모든 기능은 해당 책임과 주의 깊게 부합해야 함.
로버트 마틴은 책임을 변경하려는 이유로 정의하고, 어떤 클래스나 모듈은 변경하려는 단 하나의 이유만을 가져야한다고 결론. 예를 들어 보고서를 편집하고 출력하는 모듈이 있을 때, 해당 모듈은 두 가지 이유로 변경될 수 있음. 하나는 보고서의 실질적인 내용의 변경이고, 다른 하나는 꾸미기 위한 변경인 매우 다른 원인에서 기인. 단일 책임 원칙에 의거하면 해당 문제의 두 측면은 실제로 분리된 두 책임 때문이며, 분리된 클래스나 모듈로 나누어야 함. 다른 시기에 다른 이유로 변경되어야하는 두 가지를 묶는 것은 나쁜 설계일 수 있음.
한 클래스를 한 관심사에 집중하도록 유지하는 것은, 클래스를 더욱 견고히 만들기 때문에 중요함. 앞의 예를 살펴보면 편집 과정에서 변경이 일어날 경우, 같은 클래스의 일부로 있는 출력 코드가 망가질 위험이 높음.
Open-Close Principle - 개방-폐쇄 원칙
소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에 대해서는 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다는 프로그래밍 원칙.
소프트웨어 개발 작업에 이용된 많은 모듈 중 하나를 수정할 때, 그 모듈을 이용하는 다른 모듈을 모두 수정해야 한다면, 해당 프로그램은 수정이 어려움. 개방-폐쇄 원칙은 시스템의 구조를 올바르게 리팩터링하여 나중에 이와 같은 유형의 변경이 더 이상의 수정을 유발하지 않도록 한 것 개방-폐쇄 원칙이 잘 적용된다면, 기능을 추가하거나 변경할 때 이미 제대로 동작하고 있던 원래 코드를 변경하지 않아도, 기존의 코드에 새로운 코드를 추가함으로써 기능의 추가나 변경이 가능.
확장에 대해 열려있다는 의미란?
모듈의 동작을 확장할 수 있다는 것을 의미, 즉 애플리케이션의 요구 사항이 변경될 때, 해당 변경에 맞게 새로운 동작을 추가해 모듈을 확잘할 수 있음. 즉, 모듈이 하는 일을 변경할 수 있음
수정에 대해 닫혀있다는 의미란?
모듈의 소스 코드나 바이너리 코드를 수정하지 않아도, 모듈의 기능을 확장하거나 변경할 수 있는 것. 그 모듈의 실행 가능한 바이너리 형태나 링크 가능한 라이브러리(자바의 .jar 파일처럼)를 건들 필요가 없음
추상화를 통한 개발 폐쇄 원칙
추상화는 개방-폐쇄 원칙의 핵심 요소. 객체 지향 프로그래밍 언어(Java 등)에서는 고정되기는 해도 제한되지는 않은, 가능한 동작의 묶음을 표현하는 추상화가 가능함. 모듈은 추상화를 조작할 수 있음. 이런 모듈은 고정된 추상화에 의존하므로 수정에 대해 닫혀있을 수 있고, 추상화의 새 파생 클래스를 만드는 것을 통해 확장도 가능.
개방-폐쇄 원칙과 객체 지향 언어의 관계
개방-폐쇄 원칙은 객체 지향 프로그래밍의 핵심 원칙이며, 반드시 지켜야할 기본 원칙. 해당 원칙을 무시하고 프로그래밍을 한다면, 객체 지향 프로그래밍의 가장 큰 장점인 유연성, 재사용성, 유지보수성 등을 얻을 수 없음.
Liskov Substitution Principle - 리스코프 치환 원칙
엄밀한 용어로 말하자면 (강한) 행동적 하위형화라 부르는 하위형화의 관계를 특정한 사례.
이 정의는 다음 원칙을 만들어낸 자료형의 의미론적 상호처리를 보장하기 때문에, 단순한 문법적 관계 뿐만 아닌 의미론적 관계 - q(x)를 자료형 T의 객체 x에 대해 증명할 수 있는 속성이라고 한다면, S가 T의 하위형이라면 q(y)는 자료형 S의 객체 y에 대해 증명할 수 있어야 함.
즉, 타입 S의 객체 o1과 타입 T의 인스턴스 o2가 있을 때, 어떤 프로그램에서 타입 T의 객체로 P가 사용된다고 했을 때, S가 T의 서브타입이라면 P에 대입된 o1이 o2로 치환된다고 해도 P의 행위는 바뀌지 않음.
로버트 C.마틴의 LSP 정의
- 참조되는 기반클래스의 함수는 파생클래스 객체의 상세를 알지 않고서도 사용될 수 있어야 함
- 잘 설계된 코드는 기존 코드의 변경 없이 확장 가능. OCP를 가능하게 하는 주요 메커니즘은 추상화와 다형성, 추상화와 다형성을 가능하게 하는 키 메커니즘이 상속. 추상 기반 클래스의 순수 가상 함수로부터 클래스를 상속 파생시킴으로써 추상화된 다형 인터페이스를 만들어낼 수 있음
- LSP는 상속의 룰이며, 최고의 상속 구조 특성을 갖추게 하며, OCP를 위반하지 않도록 인도하는 원칙
순화 설명
자식 클래스는 최소한 자신의 부모 클래스에서 가능한 행위는 수행할 수 있어야 함. 즉, 부모 클래스와 자식 클래스 사이의 행위가 일관되어야 함.
LSP 만족시, 프로그램에서 부모 클래스의 인스턴스 대신 자식 클래스의 인스턴스로 대체해도 프로그램의 의미가 변화하지 않음.
만족시키는 간단한 방법은 재정의를 하지 않는 것.
중요한 이유
- 지키지 않을 경우 클래스 계층이 지저분해질 수 있음. 서브클래스 인스턴스를 파라미터로 전달했을 때, 메서드가 이상하게 작동할 수 있음
- 슈퍼클래스에 대해 작성된 단위 테스트가 서브 클래스에 대해 작동되지 않음
원칙
리스코프의 '행동적 하위형'이라는 개념은 '가변 객체의 치환성'이라는 개념을 정의.
자료형 S가 자료형 T의 하위형이라면, 프로그램에서 자료형 T의 객체는 프로그램의 속성을 변경하지 않고, 자료형 S의 객체로 교체할 수 있음. 즉, T t = new T를 T t = new S로 치환 가능
용어 설명!
무공변성(invariant) - 해당 타입 자신만을 허용
공변성(convariant) - 자기자신 & 하위 클래스 타입을 허용
반공변성(contravariant) - 자기자신 & 상위 클래스 타입을 허용
선행조건 - 연산이 호출될 시점에 만족이 되어야 하는 조건
후행조건 - 선행조건이 만족된 상태에서 수행이 완료될 때 만족해야 하는 조건(연산의 구현에서 보장을 해야 하는 부분)리스코프 원칙은 시그니처에 관한 몇 가지 표준 요구 사항을 강제
- 하위형에서 메서드 인수의 반공변성
- 하위형에서 반환형의 공변성
- 하위형에서 메서드는 상위형 메서드에서 던져진 예외의 하위형을 제외하고, 새로운 예외를 던질 수 없음
하위형이 만족해야 하는 행동 조건 몇 가지
- 하위형에서 선행조건은 강화될 수 없음
- 하위형에서 후행조건은 약화될 수 없음
- 하위형에서 상위형의 불변조건은 반드시 유지되어야 함
객체는 자신의 메서드를 통해서만 수정할 수 있는 것으로 간주(캡슐화), 하위형은 상위형에 없는 메서드를 추가할 수 있기 때문에, 추가된 메서드를 통해 상위형에서 허용하지 않는 하위형 상태의 변경을 일으킬 수 있음. 이력 제약 조건은 이를 방지. 제약 조건의 위반을 정의하기 위해 '변경 가능 지점'을 '변경 불가 지점'의 하위형으로 정의해볼 수 있음. '변경 불가 지점'의 이력은 생성한 후 상태가 항상 동일해야 하기 때문에, 앞에서 가정한 정의는 이런 제약조건의 위반이며, 따라서 일반적으로 변경 가능 위치를 이력에 포함할 수 없음. 하위형으로 추가된 필드는 상위형 메서드로 감시할 대상이 아니기 때문에 안정적으로 수정할 수 있음.
전형적인 LSP 위반의 전형적인 예
너비와 높이의 조회(getter) 및 할당(setter) 메서드를 가진 직사각형 클래스로부터 정사각형 클래스를 파생하는 경우.
정사각형 클래스 : 항상 너비와 높이가 같다고 간주.
정사각형 객체가 직사각형을 다루는 문맥에서 사용되는 경우, 정사각형의 크기는 독립적으로 변경할 수 없기 때문에(혹은 그래서는 안되기 때문에) 예기치 못한 행동을 하게 됨. 이 문제는 고치기가 쉽지 않음.
정사각형 클래스의 할당 메서드를 수정하여 정사각형의 불변 조건(즉, 너비와 높이가 같음)을 유지하면, 이 메서드는 크기를 독립적으로 변경할 수 있다고 설명한 직사각형의 할당자의 사후 조건을 무력화(위반)
LSP 위반은 실전에서는 LSP를 위반한 클래스를 사용하는 코드가 실제로 기대하는 사후 조건이나 불변 조건에 따라 문제가 될 수도 있고, 아닐 수도 있음. 중요한 것은 가변성! 정사각형과 직사각형이 조회 메서드만 가진다면, 즉 불변 객체라면 LSP 위반을 발생하지 않음.
Interface Segregation Principle - 인터페이스 분리 원칙
클라이언트가 자신이 이용하지 않는 메서드에 의존하지 않아야 한다는 원칙
큰 덩어리의 인터페이스들을 구체적이고 작은 단위들로 분리시킴으로써 클라이언트들이 꼭 필요한 메서드들만 이용할 수 있게 함. => 작은 단위 = 역할 인터페이스
인터페이스 분리 법칙을 통해 시스템의 내부 의존성을 약화시켜 리팩터링, 수정, 재배포를 쉽게 할 수 있음
GRASP의 밀착원칙과 비슷함
Dependency Inversion Principle - 의존 역전 원칙
소프트웨어 모듈들을 분리하는 특정 형식을 지칭. 상위 계층(정책 결정)이 하위 계층(세부 사항)에 의존하는 전통적인 의존관계를 반전(역전) 시킴으로써 상위 계층이 하위 계층의 구현으로부터 독립되게 할 수 있음.
'상위와 하위 객체가 모두 동일한 추상화에 의존해야 한다'는 객체 지향적 설계의 대원칙을 제공
- 상위 모듈은 하위 모듈에 의존해서는 안됨. 상위 모듈과 하위 모듈 모두 추상화에 의존해야 함
- 추상화는 세부 사항에 의존해서는 안 됨. 세부 사항이 추상화에 의해 의존해야 함.
반응형'우아한 테크코스 > 테크코스' 카테고리의 다른 글
우아한 테크코스 테코톡 준비했던 자료 (18) 2021.04.19 [HTML/CSS/JS] JS Bin 사용하기 (0) 2020.04.14 [Java] 상속 관련 궁금한 부분 (0) 2020.03.18 3, 4주차 리뷰 (0) 2020.03.16 [Java] 단위 테스트 명명규칙 (0) 2020.03.16