JAVA
[JAVA]객체 지향 프로그래밍의 5가지 원칙 (SOLID 원칙) 예시와 개념
채야미
2024. 2. 5. 16:06
SOLID란?
SOLID 원칙은 클린코드로 유명한 로버트 마틴 정의한 좋은 객체 지향 설계의 5가지 원리입니다.
소프트웨어 개발을 유연하고 유지보수가 쉽게하기 위해 만들어진 원칙이라고 보면 됩니다.
1. SRP 단일 책임의 원칙 - Single responsibility principle
- 한 클래스는 하나의 책임만 가져야 한다.
- 변경이 있을 때 코드 변경이 적다면 그것은 단일 책임의 원칙을 잘 준수한것 (기능 별로 책임, 계층이 잘 나눠져 있음)
클래스 Order { 생성자 ( orderDetails ) { this . orderDetails = 주문 세부정보; } 계산Total ( ) { // 총 주문 금액 계산 } generateInvoice ( ) { // 송장 생성 } }
예시 코드에서는 Order 클랴스가 주문을 계산하고 invoice(명세서)까지 같이 작성 합니다. 이는 SRP를 위반했기때문에 책임을 다른 클래스를 만들어서 분리해야 합니다.
2. OCP 개방 폐쇄 원칙 - Open/closed principle
- 소프트웨어 요소는 확장에는 열려있으나 변경에는 닫혀있어야 한다
- 기존 코드 변경을 최대한 줄일 수 있는
-> 다형성을 사용하기 - 인터페이스를 구현한 클래스를 하나 더 만들어서 새로운 기능을 구현하는 것
3. LSP 리스코프 치환 원칙 - Liskov substitution principle
- 프로그램 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다
- 다형성에서 하위 클래스는 인터페이스 규약을 지켜야한다는 의미
- 쉽게 말해 하위 클래스가 상위 클래스의 기능으로 대체 되어야 한다는 뜻
위의 예시에서 처럼 펭귄은 날 수 없는데 부모 클래스로 대체하였을때 "Bird is flying"이라는 메소드가 실행되므로 부모 클래스로 자식 클래스가 대체 될수 없습니다. 리스코프 원칙을 위반하는 예시입니다.class Bird { public void fly() { System.out.println("Bird is flying"); } } class Penguin extends Bird { // 리스코프 원칙을 위반하는 메서드 오버라이딩 @Override public void fly() { System.out.println("Penguin cannot fly"); } } class Sparrow extends Bird { // 추가적인 메서드 public void tweet() { System.out.println("Sparrow is tweeting"); } }
4. ISP 인터페이스 분리 원칙 - Interface segregation principle
- 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다
- 사용자 클라이언트 -> 운전자 클라이언트, 정비사 클라이언트로 분리
- 더 작은 클라이언트별 인터페이스를 통해 각각의 클라이언트가 필요한 것만 구현, 종속성 낮추기
- 인터페이스가 명확해짐, 대체 가능성이 높아진다
// 단일 대형 인터페이스 대신 class Worker { work ( ) { // 작업 수행 } eat ( ) { // 먹기 수행 } sleep ( ) { // 수면 수행 } } // 더 작은 클라이언트별 인터페이스 사용 class Worker { work ( ) { // 작업 수행 } } class Eater { eat ( ) { // 식사 수행 } } class Sleeper { sleep ( ) { // 수면 수행 } }
워커를 만들어서 3개의 다른 작업을 하게하는 것이 아닌, 작은 단위로 행동별로 사람을 만들어 불필요한 종속성을 줄
였습니다.
5. DIP 의존관계 역전 원칙 - D ependency Inversion Principle
- 프로그래머는 "추상화에 의존해야하고, 구체화에 의존하면 안된다"
- 구현 클래스에 의존하는 것이 아니라, 인터페이스에 의존해야한다는 의미
-> 운전자는 자동차 인터페이스에 대해 알아야하는것이지
특정한 자동차인 K3에 대해 알고 그것으로 코드를 작성하면 안됨! - 인터페이스에 의존하면 변경이 유연해진다
// 추상 인터페이스
interface Switchable {
void turnOn();
void turnOff();
}
// 저수준 모듈 (구체적인 구현)
class LightBulb implements Switchable {
@Override
public void turnOn() {
// Turn on the light bulb
}
@Override
public void turnOff() {
// Turn off the light bulb
}
}
// 고수준 모듈
class Switch {
private final Switchable device;
public Switch(Switchable device) {
this.device = device;
}
public void operate() {
// 사용하는 장치의 상태를 변경
// LightBulb 또는 다른 Switchable 인터페이스를 구현한 클래스들이 올 수 있음
device.turnOn();
// 또는 device.turnOff();
}
}
위의 예시에서는 switch 클래스에 device만 설정해 준다면
Switchable로 구현된 어떠한 전구더라도 Switch클래스에서 코드 변경없이 사용될 수 있습니다.