객체는 단 한 개의 책임을 가져야 한다.
하나의 객체가 여러가지 책임을 가지고 있다고 해도, 가만히 있으면 별다른 문제가 생기지는 않는다. 🥨변화가 있을 때 문제가 생긴다.
하나의 객체에 a,b,c 3가지 책임이 있을 때 a가 변화했을 때 b,c에게 까지 영향을 준다.
하나의 객체가 DB 접근도 하고, View에 보여주기도 한다고 해보자.
그러면 그 객체로 DB 접근만 하고 싶을 때에도 View에 보여주는 의존성이 포함된다.
'서로 다른 이유'라는 말이 좀 헷갈렸다. 서로 다른 이유보다는 서로 다른 책임이 나에게 더 와닿았다.
DB 접근을 수정한다는 이유로, View에 보여주기를 수정한다는 이유로..
이런 식으로 다른 이유로 "🥨변경"이 생기면 단일책임원칙을 위배하고 있다는 시그널이다.
단일책임원칙은 변경과 관련이 크다.
기능은 확장할 수 있고 (개방) | 사용하는 쪽에서는 변경이 없어야 한다. (폐쇄)
이때, 기능이 변경이 아니라 확장이다. 🥨다형성과 관련이 깊다.
기능 추가시 🥨유연하게 추가할 수 있다.
🥨추상화 & 🥨다형성
이것은 곧 상속으로 연결된다.
상속하면 일부 필요한 구현은 오버라이딩할 수 있지만, 외부 사용하는 쪽에서는 변경이 없으니까!
if (car isInstanceOf Matiz) {
}
if (car == Matiz) {
} else if (car == Sonata) {
}
개방폐쇄원칙에서 폐쇄가 위배되고 있다.
상위 Type의 객체를 하위 Type으로 교체해도 문제가 없다.
외부 Client 입장에서 상위 Type 개념에 맞게 사용했지만, 그게 하위 Type에는 적용되지 않을 수 있다.
하위 Type이 상위 Type을 보고서는 예측할 수 없는 행동을 하는 것이다.
(예)
InputStream : 데이터 없을 때 return -1
MyInputStream : 데이터 없을 때 return 0
while (inputStream.read() != -1) {
// 여기에 MyInputStream이 온다면 무한 loop
}
(예) 잘못된 상속 관계
class Square extends Rectangle {
}
사용자 입장에서는 Rectangle - 직사각형을 보고, width = height * 2라는 메서드를 만들 수 있다. 하지만 이게 Square에서는 적용되지 않는다.
개념적으로 정사각형->직사각형은 상속 관계이지만, 실제로는 적절하지 않은 상속관계
(예)
class NoDiscountItem extends Item {
}
사용하는 쪽에서는 Item만 보고 할인 로직을 작성할 수 있다.
void discount (Item item) {
item.updatePrice (item.getPrice * 0.8);
}
그럼 사용하는 쪽에서는 NoDiscountItem인지 type 체크를 해줘야 한다.
클라이언트가 사용하지 않는 인터페이스에 변경이 발생하더라도 영향을 받으면 안된다.
처음에 클라이언트가 아니라 구현체 관점에서 잘못 이해했다.
예를 들어
Dog extends Animal {
void fly();
}
당연히 이렇게 안하잖아! 이게 왜 원칙이야;🤔라고 생각함.
구현체가 사용하지 않는 인터페이스를 가지고 있다는 게 아니라 사용자 입장에서 인터페이스를 분리하라는 의미다.
하나의 거대한 구현체(객체)를 클라이언트 입장에서 interface를 분리한다.
User1
Printer printer = new 복합기();
printer.print();
User2
Fax fax = new 복합기();
fax.fax();
그러면 fax 관련 interface에 변경이 생겨도 printer를 사용하는 user1는 아무런 영향을 받지 않는다. :)
고수준 모듈은 저수준 모듈의 구현에 의존해서는 안 된다. 저수준 모듈이 고수준 모듈에서 정의한 추상타입에 의존해야 한다.
말이 진짜 어렵당
의존관계를 맺을 때 변화하기 쉬운 것 또는 자주 변화하는 것보다는 변화하기 어려운 것, 거의 변화가 없는 것에 의존하라는 원칙
고수준 모듈
class Notification {
public void send() {
_email.SendEmail();
_sms.SendSMS();
}
}
이런 식으로 실제 구현체를 사용하여 의미있는 단일 기능을 제공한다.
저수준 모듈
실제 구현체
public class SMS {
}
저수준 모듈(구현체)를 추상화시켜서, 고수준 모듈 (사용하는 쪽)에는 변경없이 실제 구현체를 변경할 수 있게 한다.
interface Message {}
class SMS implements Message;
class Noticitaion {
public void send() {
for (Messgae message : List<Message>) {
message.send();
}
}
}
이런 식으로 수정한다는 것이다.