- 객체지향 프로그래밍에서 단일 책임 원칙(SRP)와 개방-폐쇄 원칙(OCP)에 대해 설명하고, 각각의 원칙을 적용한 코드 예시를 들기
단일 책임 원칙과 개방-폐쇄 원칙에 대해 설명하기 전에 SOLID 원칙이라는게 있다.
SOLID 원칙이란 객체지향 프로그래밍을 만드는 데 있어서 지켜야할 5가지 소프트웨어 개발 원칙이다.
S - SRP(Single Resonsibility Principle)
O - OCP(Open Closed Principle)
L - LSP(Liskov Substitution Principle)
I - ISP(Interface Segregation Principle)
D - DIP(Dependency Inversion Principle)
총 5개다.
이 원칙들을 준수하면 소프트웨어를 더욱 유용하게, 유지보수를 수월하게, 확장을 유연하게 해준다.
특히 대규모 시스템 설계에는 필수적이다.
이중 S, O에 해당하는 단일 책임 원칙과 개방-폐쇄 원칙에 대해 알아본다.
단일 책임 원칙(Single Responsibility Principle)
클래스는 단 하나의 책임만을 가져야 한다는 원칙이다. 여기서 책임이란 CRUD를 의미하는 것 같고, CRUD의 기능 중 하나의 기능만을 담당해야 한다는 의미로 보인다. 이를 준수하면 기능을 변경할 때 수정해야 할 코드가 줄어들고, 코드의 유지보수가 용이해진다.
이 원칙을 섣불리 실제 코딩에 적용하면 '책임 = 동작' 으로 오해하고 단일한 동작으로 컴포넌트(클래스)를 쪼개는 행동을 할 수 있다.
너무 많이 쪼개지면 전체 로직을 이해하기 어렵고, 공수가 많아지기 때문에 단일한 동작을 갖도록 하는 것은 함수로 한정해야만 한다.
예를들어 로그인 기능에 대해, 사용자의 아이디와 비밀번호를 입력받고 로그인 버튼을 누르면 인증 요청을 보낸다고 하자.
이 기능은 로그인과 관련된 것만 담당하며 다른 기능과 분리된다.
만약 이 로그인 기능 안에 '비밀번호 찾기' 기능을 추가하고 싶다면, 단일 책임 원칙에 따라 새로운 컴포넌트를 생성해서 해당 기능을 담당하도록 해야한다. 이렇게 하면 로그인 기능은 여전히 로그인에 대한 책임만 가지고 있으며 새롭게 생성된 컴포넌트는 비밀번호 찾기 기능에 대한 책임만 가지게 된다.
적절한 예가 있어서 직접 작성해본다.
출처:
https://blog.itcode.dev/posts/2021/08/13/single-responsibility-principle
package 연습문제;
public class Car {
private final String fn;
private final int[] wheel = {0, 0, 0, 0};
// set
public Car(String fn) { // 파라미터로 fn을 받고 이는 휠 구동 방식이라고 함
this.fn = fn;
}
public void run(int power) { // 파라미터로 int를 넣음
switch(fn.toUpperCase()) { // 입력한 fn 변수값이 대문자
case "FWD" : { // FWD라면
wheel[0] = power; // int[] wheel[0]에 power를 집어넣고
wheel[1] = power; // wheel[0]에도 power를 넣는다
// {power, power, 0, 0}
}
case "RWD" : {
wheel[2] = power;
wheel[3] = power;
// {0, 0, power, power}
}
case "AWD" : {
wheel[0] = power;
wheel[1] = power;
wheel[2] = power;
wheel[3] = power;
}
} // switch 끝
System.out.println("휠 동력 상태 : " + wheel[0] + wheel[1] + wheel[2] + wheel[3]);
} // run 끝
// 휠 구동방식(set) / 휠에 동력 할당하기(switch-case) / 휠 상태 출력하기(syso)
// switch-case에 책임 3개가 이 클래스에 있다
} // car 끝
위의 Car 클래스에는 역할이 총 3개 할당되어있다.
이러한 방식은 캡슐화와 맞지 않으며 마찬가지로 단일 책임 원칙과도 먼 코드이다.
이 클래스의 책임을 나눠보자!
package 연습문제;
public class Car2 {
protected final String wd;
protected final int[] wheel = {0, 0, 0, 0};
public Car2(String wd) {
this.wd = wd;
}
}
package 연습문제;
public interface Run {
abstract public void run(int power);
}
본문에는 Car2에 Run 인터페이스까지 구현이 되어있지만 내 이클립스는 안되길래? 따로 Run 인터페이스를 만들었다.
package 연습문제;
public class FrontWheelCar extends Car2 implements Run {
// 전륜차 객체입니다.
FrontWheelCar(String wd) {
super(wd);
// super.wd = wd;는 안된다
}
// 주행 함수 Run 구현
@Override
public void run(int power) {
//Car2에 있는 int[] wheel을 쓴다
wheel[0] = power;
wheel[1] = power;
System.out.println("휠 동력 상태 : " + wheel[0] + wheel[1] + wheel[2] + wheel[3]);
}
} // FrontWheelCar 끝
class RearWheelCar extends Car2 implements Run {
// 후륜차 객체입니다.
RearWheelCar(String wd) {
super(wd);
}
@Override
public void run(int power) {
wheel[2] = power;
wheel[3] = power;
System.out.println("휠 동력 상태 : " + wheel[0] + wheel[1] + wheel[2] + wheel[3]);
}
} // RearWheelCar 끝
class AllWheelCar extends Car2 implements Run {
public AllWheelCar(String wd) {
super(wd);
}
@Override
public void run(int power) {
wheel[0] = power;
wheel[1] = power;
wheel[2] = power;
wheel[3] = power;
System.out.println("휠 동력 상태 : " + wheel[0] + wheel[1] + wheel[2] + wheel[3]);
}
} // AllWheelCar 끝
이후에 전륜, 후륜, 사륜차에 대한 클래스를 각각 구현했다. 상위 클래스가 되는 Car2를 상속하고 인터페이스 Run을 구현한다.
따라서 3개의 각 클래스는 하나의 역할만을 수행하게 된다!
와우~ 좋은 코드에요!
개방-폐쇄 원칙(Open-Closed Principle)
'JAVA' 카테고리의 다른 글
| 객체 직렬화 / 역직렬화 (1) | 2025.06.10 |
|---|---|
| Stram API의 map과 flatMap의 차이점 (0) | 2025.06.08 |
| List<E> 컬렉션 (0) | 2025.06.04 |
| 컬렉션 프레임워크 - 2 (0) | 2025.06.04 |
| 컬렉션 프레임워크 - 1 (0) | 2025.06.04 |