객체지향 설계의 5원칙
좋은 소프트웨어 설계는 응집도를 높이고 결합도를 낮추어야 한다. -> 결합도가 높다는 것은 객체의 변경이 생겼을 때 수정해야 하는 객체가 많다는 것을 의미한다. -> 이러한 설계는 결론적으로 유지보수를 쉽게하기 위한 설계이다.
1. SRP(Single Responsibility Principle) : 단일 책임 원칙
어떤 클래스를 변경하는 이유는 오직 하나뿐이어야 한다. (단일 책임)
예를 들어, 남자라는 객체의 다양한 책임(남편, 아들, 직장직원, 예비군) 이라는 다양한 책임이 있을 때에 객체지향의 설계에서는 나쁜 냄시가 난다고 말한다.
이런 경우 책임을 분리하는 것이 단일 책임 원칙이라고 부른다. (위의 경우 책임에 따라서 4개의 클래스로 나눈다)
단일 책임 원칙이 지켜지지 않은 경우를 살펴보자.
예를 들어서 남자는 반드시 군대를 가야하고, 여자는 반드시 군대를 가지 않는다고 가정하자.
이 때, 사람 클래스에 군번이라는 속성이라면 여자는 필요하지도 않은 속성을 가져야 한다.
이 경우 사람 클래스를 남자 클래스, 여자 클래스로 분리해 각 객체에 맞는 책임을 질 수 있도록 해야한다.
아래와 같이 메서드 내에서 분기에 따라서 결과가 다르다면 그 또한 단일 책임 원칙이 지켜지지 않은 것이다.
위와 같은 경우 아래와 같이 상속을 통해 리팩토링 할 수 있다.
단일 책임 원칙은 추상화와 관련이 깊다 -> 추상화는 각 도메인에 맞도록 객체를 설계하는 즉, 단일 책임 원칙이라고 볼 수 있다.
2. OCP(Open Closed Principle) : 개방 폐쇠 원칙
자신의 확장에는 열려있고, 주변의 변화에 대해서는 닫혀 있어야 한다.
개방 : 기능의 확장에는 열려 있어야 한다.
폐쇄 : 기존 코드의 수정 없이 기능 확장이 가능해야 한다.
예시
차종이 바뀐다고 해서, 운전하는 근본적인 방법이 바뀌는 것은 아니다.
JDBC 는 JDBC 인터페이스를 통해서, 데이터페이스가 변경되더라도 문제 없이 연결할 수 있다.
Java 는 목적파일(.class) 을 통해서 운영체제가 변경되더라도 문제 없이 실행시킬 수 있다.
편의점에서 직원은 물건 정리와 판매가 주 행동이기 때문에, 사람이 바뀐다고 해서 근본적으로 바뀌는 것은 아니다.
배역이 하는 대사와 행동은 배우가 바뀐다고 해서 바뀌지 않는다.
개방 폐쇄 원칙을 지키는 방법은 인터페이스를 통해서 특정 메서드(행동) 을 구현하게 강제하는 것이다.
이렇게 되면 각 클래스들은 책임에 맞는 메서드를 구현하게 된다.
개방 폐쇄 원칙을 무시하면 유연성, 재사용성, 유지보수성의 이점을 얻을 수 없다.
3. LSP(Liskov Substitution Principle) : 리스코프 치환 원칙
서브타입은 언제나 기반타입으로 교체 될 수 있어야 한다.
부모 클래스와 자식 클래스의 행위의 일관성이 있어야 한다! (Java Collection Framework)
올바른 리스코프 치환 원칙
하위 클래스 is a kind of 상위 클래스 - 하위 분류는 상위 분류의 한 종류이다.
구현 클래스 is able to 인터페이스 - 구현 분류는 인터페이스 할 수 있어야 한다.
분류도와 같은 객체 구조는 리스코프 치환 원칙을 따른다.
ex,
동물 뽀로로 = new 펭귄();
잘못된 리스코프 치환 원칙
자식 클래스로 부모 클래스의 내용을 상속하는데, 기존 코드에서 보장 하던 조건을 수정하거나 적용시키지 않아서, 기존 부모 클래스를 사용하는 코드에서 예상하지 않은 오류를 발생 시킨다면 문제다.
부모 클래스와 자식 클래스의 행위의 일관성이 사라졌다..
계층도/조직도와 같은 객체 구조는 리스코프 치환 원칙을 위반한다.
ex,
아버지 춘향이 = new 딸();
4. ISP(Interface Segregation Principle) : 인터페이스 분리 원칙
다중 책임을 가지는 클래스에 대해서 각각의 책임에 따른 인터페이스를 상속 받게 만들어서 특정 책임에 요구되는 경우, 특정 인터페이스로 캐스팅해 사용하는 방법이다.
클라이언트 입장에서 자신이 사용하지 않는 메서드와 의존 관계를 맺어서는 안된다!
다중 책임을 가지는 클래스에 대한 개선 방법으로 다중 책임 클래스에 대해 해결하는 방법은 2가지가 있다.
단일 책임 원칙
인터페이스 분리 원칙
일반적인 경우 단일 책임 원칙을 지키는 것이 좋을 해결책이다.
코드가 간결하다!
상위 클래스는 풍성할수록 좋다.
빈약한 상위 클래스의 경우, 형변환이 계속적으로 발생해 상속의 장점을 누리지 못한다.. (코드가 더러워진다)
인터페이스는 꼭 필요한 행위만 넣어주는 것이 좋다.
인터페이스는 그 역할에 충실한 최소한만 공개하는 것이 좋다.
그래야만 특정 역할에 맞는 기능만 가질 수 있다.
5. DIP(Dependency Inversion Principle) : 의존 역전 원칙
고차원 모듈(자동차)은 저차원 모듈(스노우 타이어)에 의존하면 안된다. 두 모듈 모두 추상화 된 것에 의존해야 한다.
추상화(인터페이스)에 의존해야 하지 구체화(클래스)된 것에 의존해서는 안된다.
예를 들면 자동차가 스노우타이어에 의존하는 것이 아닌, 추상화된 타이어의 의존하고, 타이어의 인스턴스가 스노우타이어가 되는 설계가 옳은 설계이다.
개방 폐쇄 원칙과 비슷하게 느껴질 것이다.
이렇듯 하나의 해결책을 찾으면, 그 안에 여러 설계 원칙이 녹아 있는 경우가 많다.
Last updated