[OOP] SOLID 원칙 - 객체지향 설계 원칙 5가지
객체지향 프로그래밍의 5가지 주요 원칙은 소프트웨어 설계의 품질을 높이고 유지보수성을 강화하기 위한 지침이다.
SRP, OCP, LSP, ISP, DIP 5가지가 있다.
단일 책임 원칙 (SRP, Single Responsibility Principle)
하나의 클래스는 단 하나의 책임만 가져야 한다. (= 클래스)
- 목적 : 클래스를 변경해야 하는 이유가 단 하나뿐이어야 한다.
- 장점 : 클래스의 역할이 명확해지고 코드의 가독성과 유지보수성이 향상된다.
class Person {
void cook(); //요리하기 - 요리사
void plate(); //플레이팅 - 요리사
void order(); //주문하기 - 손님
void pickup(); //픽업하기 - 손님
void eat(); //먹기 - 손님
}
class Chef {
void cook();
}
class Customer {
void order();
void pickup();
void eat();
}
개방-폐쇄 원칙 (OCP, Open/Closed Principle)
클래스는 확장에는 열려있어야 하고, 수정에는 닫혀있어야 한다.
- 확장에는 열려있다 : 요구사항이 변경될 때, 새로운 동작을 추가하여 애플리케이션의 기능을 확장할 수 있다.
- 수정에는 닫혀있다 : 기존의 코드를 수정하지 않고 애플리케이션의 동작을 추가하거나 변경할 수 있다.
- 목적 : 기존 코드를 수정하지 않고 새로운 기능을 추가할 수 있도록 설계
- 장점 : 코드 변경으로 인한 버그를 최소화할 수 있다.
운전자는 자동차가 변경되어도 엑셀을 밟거나 브레이크를 밟는 것에 대한 영향을 받지 않는다
= 운전자가 수정에는 닫혀있다.
자동차는 Bus나 Truck 외에도 다른 자동차 종류로 클래스를 인터페이스를 통해서 확장할 수 있고, 운전자의 역할에는 영향을 끼치지 않는다.
= 확장에는 열려있다.
interface Car {
void accel();
void brake();
}
class Bus implements Car {
void accel() { //속도 7 증가 };
void brake() { //속도 5 감소 };
}
class Truck implements Car {
void accel() { //속도 5 증가 };
void brake() { //속도 3 감소 };
}
리스코프 치환 원칙 (LSP, Liskov Substitution Principle)
상위 클래스 객체는 하위 클래스 객체로 대체할 수 있어야 한다.
- 목적 : 다형성을 올바르게 구현하고 프로그램의 일관성을 유지
- 장점 : 상속 관계에서 프로그램 동작이 깨지지 않도록 보장한다.
class Car {
int speed;
void drive() {
this.speed += 10;
}
}
class Bus extends Car {
int speed;
int km;
@Override
void drive() {
// 부모 기능 그대로 수행
this.speed += 10;
// 하위 타입 기능 추가
this.km += 1;
}
}
인터페이스 분리 원칙 (ISP, Interface Segregation Principle)
클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안 된다.
- 목적 : 인터페이스를 세분화하여 필요한 메소드만 제공
- 장점 : 인터페이스를 작게 만들어서 구현 클래스가 불필요한 메소드를 갖지 않게 함
날 수 없는 동물도 있기 때문에, 동물 인터페이스를 사용하면 강아지는 날 수 없기 때문에 동물 인터페이스를 사용하지 못한다.
public interface Animal {
void walk();
void fly();
}
public class Dog implements Animal {
public void walk() {
// 강아지 걷기
}
public void fly() {
// 강아지는 날 수 없음
}
}
수정된 코드
// 걷기
public interface Walkable {
void walk();
}
// 날기
public interface Flyable {
void fly();
}
public class Dog implements Walkable {
public void walk() {
// 강아지 걷기
}
}
public class Bird implements Walkable, Flyable {
public void walk() {
// 새 걷기
}
public void fly() {
// 새 날기
}
}
의존 역전 원칙 (DIP, Dependency Inversion Principle)
상위 클래스는 하위 클래스에 의존해서는 안 되며, 둘 다 추상화에 의존해야 한다.
=> 구현 클래스에 의존하지 말고, 인터페이스에 의존해라
- 목적 : 구체 클래스가 아닌 추상화(인터페이스, 추상 클래스)에 의존하도록 설계
- 장점 : 모듈 간 결합도를 낮추고 유연성을 증가시킨다.
왼쪽은 DIP가 적용되어 있지 않아서 자동차가 일반 타이어와 스노우 타이어에 직접적으로 의존 관계를 맺고 있다.
타이어를 인터페이스로 만들어 이 인터페이스를 통해 구현체인 일반 타이어, 스노우 타이어 등을 적절하게 가질 수 있어야 한다.