[Java] 객체지향 설계 원칙 SOLID 알아보기
by coco3o객체지향 프로그래밍 설계시 좋은 객체지향 설계를 위한 SOLID 라고 불리는 5가지 원칙이 있다.
SOLID 원칙은 소프트웨어를 설계함에 있어 이해하기 쉽고, 유연하며, 유지보수 및 확장이 편하다는 장점이 있다.
1. SRP(Single responsibility principle) : 단일 책임 원칙
2. OCP(Open-closed principle) : 개방-폐쇄 원칙
3. LSP(Liskov substitution principle) : 리스코프 치환 원칙
4. ISP(Interface segregation principle) : 인터페이스 분리 원칙
5.DIP(Dependency inversion principle) : 의존관계 역전 원칙
1. SRP - 단일 책임 원칙
한 클래스는 하나의 책임만 가져야 한다.
모든 클래스는 각각 하나의 책임만 가져야 하며, 수정할 이유는 단 한 가지여야 한다.
즉, 클래스는 그 책임을 완전히 캡슐화해야 함을 말한다.
예를들어, 결제 클래스가 있다치면 이 클래스는 오직 결제 기능만을 책임지고,
만약 이 클래스를 수정해야 한다면 결제에 관련된 문제일 뿐일 것이다.
2. OCP - 개방-폐쇄 원칙
확장에는 열려있고 변경에는 닫혀 있어야 한다.
소프트웨어의 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확장에는 열려 있어야 하지만 변경에는 폐쇄적이어야 함을 의미한다.
즉, 기존의 코드를 변경하지 않으면서, 기능을 추가할 수 있도록 설계가 되는 원칙을 말한다.
예를들어, 캐릭터 하나를 생성한다고 할 때 각 캐릭터마다 움직임이 다를 경우,
움직임 패턴 구현을 하위 클래스에 맡긴다면 캐릭터 클래스의 수정은 필요없고(Closed),
움직임 패턴만 재정의 하면 된다.(Open)
개방-폐쇄 원칙을 적용하기 위한 중요 메커니즘은 추상화와 다형성이다.
3. LSP - 리스코프 치환 원칙
서브 타입은 언제나 자신의 기반 타입으로 변경할 수 있어야 한다.
상위 타입은 항상 하위 타입으로 대체될 수 있어야 함을 의미한다.
즉, 부모 클래스가 들어갈 자리에 자식 클래스를 넣어도 역할을 하는데 문제가 없어야 한다는 의미이다.
리스코프 치환 원칙은 다형성과 확장성을 극대화하며, 개방-폐쇄 원칙을 구성한다.
4. ISP - 인터페이스 분리 원칙
하나의 일반적인 인터페이스보다 여러 개의 구체적인 인터페이스가 낫다.
인터페이스 분리 원칙은 각 역할에 맞게 인터페이스를 분리하는 것이다.
인터페이스 내에 메소드는 최소한 일수록 좋다. 즉, 최소한의 기능만 제공하면서 하나의 역할에 집중하라는 뜻이다.
단일 책임 원칙(SRP)과 인터페이스 분할 원칙(ISP)은 같은 문제에 대한 두 가지 다른 해결책이라고 볼 수 있다.
가능한 최소한의 인터페이스를 사용하도록 하여 단일 책임을 강조한다고 볼 수 있다.
일반적으로 ISP보다 SRP 할 것을 권장하고 있다.
5. DIP - 의존관계 역전 원칙
구체적인 것이 추상화된 것에 의존해야 한다. 자주 변경되는 구체 클래스에 의존하지마라.
의존 관계를 맺을 때 고수준 모듈이 저수준 모듈에 직접 의존하는 것을 피해야 한다는 것이다.
- 고수준 모듈 : 변경이 없는 추상화된 클래스(또는 인터페이스)
- 저수준 모듈 : 위의 추상화된 클래스나 인터페이스를 상속받은 구현체 클래스
코드로 설명해보자면,
public interface Pizza {
void prepare();
void bake();
void cut();
}
public class CheesePizza implements Pizza {
@Override
public void prepare() {
System.out.println("Preparing Cheese Pizza...");
}
@Override
public void bake() {
System.out.println("Baking Cheese Pizza...");
}
@Override
public void cut() {
System.out.println("Cutting Cheese Pizza...");
}
}
이렇게 Pizza라는 인터페이스(고수준 모듈)와 CheesePizza라는 구현 클래스(저수준 모듈)이 있을 때,
public class Chef {
private final Pizza pizza;
public Chef(Pizza pizza) {
this.pizza = pizza;
}
public void makePizza() {
pizza.prepare();
pizza.bake();
pizza.cut();
}
}
public class Main {
public static void main(String[] args) {
Pizza cheesePizza = new CheesePizza();
Chef chef = new Chef(cheesePizza);
chef.makePizza();
}
}
Chef 클래스에서 의존성 주입을 할 때 변하기 쉬운 CheesePizza 클래스에 직접 의존하지 말고 고수준 모듈인 Pizza 인터페이스를 의존하라는 의미이다.
참고자료 :
https://hckcksrl.medium.com/solid-%EC%9B%90%EC%B9%99-182f04d0d2b
'🌈Programming > Java' 카테고리의 다른 글
[Java] Optional 올바르게 사용하기 (11) | 2023.01.07 |
---|---|
[Java] 정적 메소드(static Method)는 언제 사용할까? (2) | 2022.06.11 |
[Java] String 클래스의 contains() 메소드 (0) | 2021.10.27 |
[Java] toString() 과 String.valueOf() 비교 및 차이점 (0) | 2021.09.02 |
[Java] StringTokenizer 문자열 분리하기 (split과 차이는 뭘까?) (0) | 2021.08.10 |
블로그의 정보
슬기로운 개발생활
coco3o