SRP
단일책임의 원칙.
하나의 클래스는 오직 한 가지 일만 맡아야 한다는 원칙.
이점으로는 유지보수 용이(버그가 나면 해당 클래스의 한 영역만 살펴보면 됨), 재사용성 증가(연산이 명확히 분리되어 있으니 다른 프로젝트에서 덧셈기만 따로 빼서 쓸 수 있음)
// SRP위반예제
// 하나의 클래스가 “계산”도 하고 “결과 출력”도 담당하고 있다면 SRP를 위반한 것!
public class Calculator {
// 덧셈 기능
public double add(double a, double b) {
return a + b;
}
// 뺄셈 기능
public double subtract(double a, double b) {
return a - b;
}
// 결과를 화면에 출력
public void printResult(double result) {
System.out.println("계산 결과: " + result);
}
}
위 코드의 문제점
- Calculator클래스 계산(add, subtract)과 출력이라는 서로 다른 두 가지 책임을 가지고 있음.
- 따라서 기능이 늘어날수록 유지보수가 어렵고 테스트하기도 번거로움이 있음
SRP적용 후
- 계산만 전담하는 Calculator클래스
public class Calculator {
public double add(double a, double b) {
return a + b;
}
public double subtract(double a, double b) {
return a - b;
}
}
- 출력만 전담하는 ResultPrinter클래스
public class ResultPrinter {
public void print(double result) {
System.out.println("계산 결과: " + result);
}
}
- 두 클래스를 조합해서 사용하는 App클래
public class App {
public static void main(String[] args) {
Calculator calc = new Calculator(); // 계산 책임
ResultPrinter printer = new ResultPrinter(); // 출력 책임
double sum = calc.add(10, 5);
printer.print(sum);
double diff = calc.subtract(10, 5);
printer.print(diff);
}
}
포함 관계(Composition) & 전략 패턴(Strategy Pattern)
1. 포함 관계(Composition)이란?
A 클래스가 B 객체를 ‘가지고 있다(has-a)라는 설계방식.
비유
- 자동차(Car) 는 엔진(Engine) 을 포함
- 회사(Company) 는 직원(Employee) 들을 포함
왜 쓰는지
- 여러기능을 모아놓기 보다 필요한 객체를 내부에 들고 있어서 책임을 분리
- 객체 간 결합도 낮춰 유연성 높음
// 포함 관계 예시: Car has-a Engine
class Engine {
void start() { System.out.println("부릉부릉"); }
}
class Car {
private final Engine engine; // Car는 Engine을 포함(has-a)
public Car() {
this.engine = new Engine(); // 생성자에서 Engine 초기화
}
public void drive() {
engine.start(); // 포함된 Engine 객체를 사용
System.out.println("달린다!");
}
}
public class Main {
public static void main(String[] args) {
Car car = new Car();
car.drive(); // → 부릉부릉 달린다!
}
}
Car 안에는 Engine이 필드로 들어가 있으니 Car는 언제든 engine.start()를 호출할 수 있음
2. 전략 패턴(Strategy Pattern)이란?
알고리즘(또는 기능)을 캡슐화해서, 실행 시점에 ‘전략(Strategy)’을 교체할 수 있게 만든 패턴.
- 알고리즘(또는 기능)을 캡슐화
- 필요할 때마다 전략(strategy) 을 교체할 수 있도록
왜 쓰는지
1) 조건문(if/else) 폭탄 방지
// 조건문만 늘어나다 보면 유지보수가 지옥…
if(symbol == '+') return a + b;
else if(symbol == '-') return a - b;
else if(symbol == '*') return a * b;
// …
2) 새 알고리즘 추가 시
- 기존 코드를 한 줄도 손대지 않고
- 새로운 클래스만 만들어서 주입(injection)하면 끝
생성자 활용
생성자(Constructor)란 무엇인가?
- 특별한 메서드로, 클래스 이름과 동일하며 반환 타입이 없음
- new 클래스명(...) 할 때 항상 한 번 호출되어 객체 초기
public class Person {
private String name;
private int age;
// 기본 생성자: 파라미터 없고, JVM이 자동 생성해주기도 함
public Person() { }
// 파라미터 생성자: 호출 시 name, age를 반드시 받아야 함
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
객체가 만들어질 때(인스턴스화) 필요한 정보를 반드시 한 번에 채워 넣는 특별한 메서드.
이점으로는 불변성 보장(final 필드를 생성자에서 초기화하면 이후 절대 null이 될 수 없음), 테스트 편의(생정자 파라미터를 바꿔가며 다양한 환경을 쉽게 주입 가능)
public class Calculator {
private final AddOperator add;
private final SubtractOperator sub;
// 생성자에서 딱 한 번 초기화
public Calculator() {
this.add = new AddOperator();
this.sub = new SubtractOperator();
}
public double add(double a, double b) {
return add.apply(a, b);
}
public double subtract(double a, double b) {
return sub.apply(a, b);
}
}
OCP
개방폐쇄의 원칙.
확장에는 열려 있어야(Open for extension), 수정에는 닫혀 있어야(Closed for modification) 함.
=> 기존 코드는 바꾸지 않고 새로운 기능만 깔끔하게 추가할 수 있어야 한다는 원칙.
//새 연산(예: PowerOperator)을 추가할 땐 → 기존 코드 한 줄도 고치지 않고
//1)PowerOperator implements Operator 클래스 생성
//2)new PowerOperator()를 ArithmeticCalculator 생성자 인자에 추가
public class PowerOperator implements Operator {
public char symbol() { return '^'; }
public double apply(double a, double b) { return Math.pow(a, b); }
}
// 기존 ArithmeticCalculator 코드는 전혀 변경 필요 없음!
List<Operator> ops = List.of(
new AddOperator(), new SubtractOperator(),
new MultiplyOperator(), new DivideOperator(),
new PowerOperator() // 추가
);
ArithmeticCalculator calc = new ArithmeticCalculator(ops);
'Spring_부캠' 카테고리의 다른 글
[Java] 제발 기억해 (0) | 2025.07.18 |
---|---|
[Java] OOP (0) | 2025.07.16 |
[JAVA] MORE.1 (2) | 2025.07.08 |
[JAVA] Level.1_Calculator (0) | 2025.07.07 |
[JAVA] Optional, Collection, Generic (1) | 2025.06.30 |