일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- 인코딩방지
- pubspec
- widget test
- 배움순서
- pubspec.yaml
- TDD 개발 방법론
- Flutter
- 객체 지향 설계
- 2D 그래픽 라이브러리
- 안드로이드를 위한
- Parameter specified as non-null is null
- 플러터
- Refresh Tocken
- 2D graphics library
- 다트 테스트
- SOLID 원칙
- 다트
- 에러 메시지를 잘보자 ^^
- dart
- permission_handler
- 테스트 주도 개발론
- refresh 토큰
- 토큰갱신
- dart test
- Same parameter
- 안드로이드
- 8시간 삽질
- 플러터 테스트
- retorift
- Android
- Today
- Total
Landroid
SOLID 원칙 쉽고 간단하게 [Java] 본문
안녕하세요 ~!
오늘은 안드로이드가 아닌 소프트웨어 공학을 주제로 가져와 봤습니다. ㅎㅎ
왜냐고요? 내 맘이니까 여러분들에게 유용한 지식을 제공하기 위해서죠ㅎ
그럼 긴 말없이 SOLID가 무엇인지 설명을 해보도록 하겠습니다.
우선 SOLID는 무엇의 약칭일까요?
한 번 펼쳐보면 다음과 같습니다.
- SRP (Single Responsibility Principle): 단일 책임 원칙
- OCP (Open Closed Principle) 개방 폐쇄 원칙
- LSP (Liskov Substitution Principle) 리스코프 치환 원칙
- ISP (Interface Segregation Principle) 인터페이스 분리 원칙
- DIP (Dependency Inversion Principle) 의존 역전 원칙
보시는 것과 같이 SOLID는 3자로 줄인 저 단어들의 앞 글자들만 따와서 만들어진 단어입니다.
그럼 이걸 쓰는 이유가 무엇일까요?
클린코드로 유명하신 로버트 C. 마틴이 SOLID를 만드셨습니다.
그리고 SOLID 덕분에 객체지향 설계가 더 쉬워졌고 유지보수와 확장도 쉬워졌기 때문에
실무뿐만 아니라 면접에서 조차 아주 중요한 개념으로 자리 잡게 되었습니다.
그러니 배워두면 은근 쓸만합니다. 좋습니다ㅎㅎ.
그럼 하나부터 다섯(사실은 넷)까지 이것들이 무엇을 뜻하는지 자바 코드로 설명해드리겠습니다.
1. SRP : 단일 책임 원칙
우선 SRP! 로버트 씨의 말을 가져온다면
"어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다."
이게 무슨 말일까? 그냥 보면 어렵지만 간단하게 해석하자면
모든 클래스는 각각 하나의 책임만을 가지라는 뜻입니다.
예시를 들자면 아래처럼 한 클래스에 유저정보(프로필)와 채팅정보를 담는다면 어떻게 될까요?
public class ChatProfile {
private String userName;
private int age;
private String profileImage;
private String chatRoomName;
private String recentChatMessage;
private int numberParticipants;
// getter and setter
}
지금이야 문제없겠지만 아래 사진과 같은 경우는 어떨까요?
만약 프로필 정보만 보여줄 거라면 당연히 채팅정보는 필요 없어집니다. 또한 클래스에 여러 가지 책임을 부여하면 클래스 크기가 커지므로 가독성에도 안 좋고 무엇보다 SRP를 위배하게 됩니다.
2. OCP : 개방 폐쇄 원칙
"소프트웨어 엔티티는 확장에 대해서는 열려 있어야 하지만 변경에 대해서는 닫혀 있어야 한다."
여기서 엔티티는 클래스, 함수, 모듈 등과 같은 것을 뜻합니다.
이건 딱히 이해하기 어렵지 않으니 바로 예시를 보여드리겠습니다.
예를 들어 여러분이 차키 들고 가기 귀찮아서 앱으로 만들었다고 칩시다.
그럼 들어가는 기능은 열기, 잠그기, 시동걸기 등이 있겠죠?
public class CarKey {
CarA myCar;
CarKey(CarA car) {
myCar = car;
}
void open() { /* 문 열림 */ }
void lock() { /* 문 닫힘 */ }
void turnOn() { /* 시동 검 */}
}
근데 차가 오래돼서 여러분은 새 차를 뽑게 되었습니다.
그럼 저 코드는 어떻게 될까요? 새 차에 맞게 새로 짜야할까요? 그러기엔 기능이 똑같은데 말이죠....
그래서 OCP 원칙에 따라 수정해봅시다!
아래처럼 인터페이스를 중간에 두어 두 클래스 사이에 직접적인 연동은 피하게 설계를 하면 해결될 수 있습니다!
public interface CarKey {
void open();
void lock();
void turnOn();
}
3. LSP : 리스코프 치환 원칙
어....리스코프가 누구죠? (바바라 리스코프, 1983년 어느 컨퍼런스에서 처음으로 이 원칙을 소개한 사람)
"서브 타입은 언제나 자신의 기반 타입으로 교체할 수 있어야 한다."
상속을 계층이 아닌 확장으로 이해하라는 말입니다. 이번엔 코드 없이 아래 예시를 한 번 볼까요?
1. 부모와 자식 -> 자식은 부모의 한 종류이다.
2. 동물과 다람쥐 -> 다람쥐는 동물의 한 종류이다.
혹시 둘의 차이를 느끼셨나요?
1번은 부모와 자식 간의 속성을 고려하지 않고 단순히 계층적 관계만을 형성하고 있습니다.
이러한 관계는 객체 설계 시 부적절하고 LSP를 위배합니다.
반대로 2번은 동물이라는 속성에서 다람쥐로 확장하는 관계를 형성하고 있으므로 LSP를 성립합니다^^
4. ISP : 인터페이스 분리 원칙
"클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다."
일단 이 원칙이 성립하려면 1번 SRP 원칙을 위배해야지 조건을 만족합니다.
무슨 소리라고 느끼실진 몰라도 ISP 원칙은 해당 클래스가 여러 인터페이스에서 최소원칙에 따라 각 상황에 맞도록 설계되어야 한다는 원칙입니다.
ISP는 조금 독특해서 발생할 일이 그닥 없지만 그래도 예시를 한 번 들겠습니다.
여기 서버도 잘하고 요리도 잘하는 친구가 있습니다. (왜 굳이 서버와 요리냐고요? 내맘이니까 -_-)
public class Person implements Backend, Cook {
@Override
public void develop() {
}
@Override
public void cook() {
}
}
근데 만약 개발만 하는 상황이라면 cook()는 필요 없을 테고 반대로 요리만 하는 상황이라면 develop()이 필요 없는 상황이 발생할 겁니다.
지금 이대로 사용해도 함수가 별로 없어서 상관없지만 만약 함수가 수십 개라면 이야기가 달라집니다. 그래서 만들어진 게 ISP 원칙인데 이 원칙을 적용한다면 다음과 같이 사용합니다.
class example {
public static void main(String[] args) {
Backend backend = new Person();
Cook cook = new Person();
}
}
간단하죠? 이렇게 쉽게 분리할 수 있습니다.
5. DIP (Dependency Inversion Principle) 의존 역전 원칙
"추상화된 것은 구체적인 것에 의존하면 안 된다. 구체적인 것이 추상화된 것에 의존해야 한다."
말 그대로입니다. 부모 클래스는 자식 또는 구체화된 클래스에 의존하면 안 된다는 의미입니다. 감이 잘 안 잡히실 수도 있으실 텐데 아쉽게도 그다지 좋은 예시를 떠올리지 못해서 ㅠㅠ
도움이 될만한 링크를 걸어두겠습니다.
https://limkydev.tistory.com/77
이상으로 SOLID에 대해서 알아보았고
이렇게 앞으로 다양한 주제를 가지고 지식을 공유하는 블로그가 되도록 하겠습니다^^