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): 의존성 역전 원칙 – 고수준 모듈이 저수준 모듈에 의존해서는 안 되며, 추상화에 의존해야 한다.
예시코드(SOLID원칙 적용 전)
public class Calculator {
// 덧셈 기능
public int add(int a, int b) {
return a + b;
}
// 곱셈 기능
public int multiply(int a, int b) {
return a * b;
}
}
Calculator 리팩토링: SOLID 원칙 적용
1. SRP (단일 책임 원칙) 적용
Calculator 클래스는 여러 책임(덧셈, 곱셈)을 가지고 있습니다. 따라서, 각 기능을 별도의 클래스로 분리하여 단일 책임을 부여하는 것이 좋습니다.
SRP 적용 후:
public interface Operation {
int calculate(int a, int b);
}
public class Adder implements Operation {
@Override
public int calculate(int a, int b) {
return a + b;
}
}
public class Multiplier implements Operation {
@Override
public int calculate(int a, int b) {
return a * b;
}
}
• Adder 클래스는 덧셈 기능만 담당하고, Multiplier 클래스는 곱셈 기능만 담당합니다.
• Operation 인터페이스는 각 연산이 calculate(int a, int b)라는 동일한 인터페이스를 따르게 만듭니다.
2. OCP (개방/폐쇄 원칙) 적용
클래스가 확장에는 열려 있고, 수정에는 닫혀 있어야 합니다. 즉, 새로운 기능이 추가될 때 기존 클래스를 수정하지 않고 확장해야 합니다. 위의 Operation 인터페이스는 새로운 연산을 추가할 때 기존 코드를 수정할 필요 없이 확장할 수 있습니다.
OCP 적용 후:
public class Subtractor implements Operation {
@Override
public int calculate(int a, int b) {
return a - b;
}
}
• 새로운 Subtractor 클래스는 뺄셈 기능을 추가했지만, 기존 Adder와 Multiplier 코드는 수정되지 않았습니다. 이렇게 확장을 통해 기능을 추가할 수 있습니다.
3. LSP (리스코프 치환 원칙) 적용
Operation 인터페이스를 구현하는 모든 클래스는 동일한 방식으로 사용될 수 있어야 합니다. 즉, Operation 인터페이스의 서브클래스들은 부모 인터페이스와 동일하게 동작해야 하며, 호출 측에서 동작이 일관되어야 합니다.
LSP 적용 후:
public class Calculator {
private Operation operation;
public Calculator(Operation operation) {
this.operation = operation;
}
public int performCalculation(int a, int b) {
return operation.calculate(a, b);
}
}
• Calculator 클래스는 Operation 타입을 사용하여 덧셈, 곱셈, 뺄셈 등의 연산을 동적으로 결정합니다.
• Operation을 구현하는 클래스라면 어떤 연산이든 교체되어도 일관된 방식으로 동작해야 합니다.
4. ISP (인터페이스 분리 원칙) 적용
인터페이스가 너무 많은 기능을 포함하면 불필요한 의존성이 생기기 때문에, 인터페이스는 가능한 세분화되어야 합니다. 여기서는 Operation 인터페이스가 각 연산에 대해 필요한 기능만 제공하므로, 인터페이스 분리가 적절하게 이루어졌습니다.
5. DIP (의존성 역전 원칙) 적용
고수준 모듈(Calculator)이 저수준 모듈(Adder, Multiplier)에 의존하지 않고, **추상화된 인터페이스(Operation)**에 의존해야 합니다. 이를 통해 추상화된 인터페이스에 의존하게 하고, 구체적인 구현은 외부에서 주입받을 수 있습니다.
DIP 적용 후:
public class Main {
public static void main(String[] args) {
// 덧셈을 수행하는 계산기
Calculator addCalculator = new Calculator(new Adder());
System.out.println("덧셈 결과: " + addCalculator.performCalculation(5, 3));
// 곱셈을 수행하는 계산기
Calculator multiplyCalculator = new Calculator(new Multiplier());
System.out.println("곱셈 결과: " + multiplyCalculator.performCalculation(5, 3));
}
}
• 의존성 주입을 통해 Calculator는 구체적인 구현(Adder, Multiplier)이 아닌 **인터페이스(Operation)**에만 의존하게 됩니다. 이로 인해 Calculator는 확장성 있고 유연하게 설계되었습니다.
최종 코드
Operation 인터페이스
public interface Operation {
int calculate(int a, int b);
}
Adder (덧셈)
public class Adder implements Operation {
@Override
public int calculate(int a, int b) {
return a + b;
}
}
Multiplier (곱셈)
public class Multiplier implements Operation {
@Override
public int calculate(int a, int b) {
return a * b;
}
}
Subtractor (뺄셈)
public class Subtractor implements Operation {
@Override
public int calculate(int a, int b) {
return a - b;
}
}
Calculator
public class Calculator {
private Operation operation;
public Calculator(Operation operation) {
this.operation = operation;
}
public int performCalculation(int a, int b) {
return operation.calculate(a, b);
}
}
Main
public class Main {
public static void main(String[] args) {
Calculator addCalculator = new Calculator(new Adder());
System.out.println("덧셈 결과: " + addCalculator.performCalculation(5, 3));
Calculator multiplyCalculator = new Calculator(new Multiplier());
System.out.println("곱셈 결과: " + multiplyCalculator.performCalculation(5, 3));
}
}
결론
이 방식은 SOLID 원칙을 충실히 따르는 방식으로, 각 클래스는 하나의 책임만 가지고 있으며, 인터페이스를 통해 확장 가능하게 설계되었습니다. 또한 의존성 주입을 사용하여 유연성과 재사용성을 극대화했습니다.
'Java' 카테고리의 다른 글
[JAVA] 자바의 실행 흐름 (0) | 2025.03.08 |
---|---|
[Java] 익명클래스, 함수형 인터페이스, 익명함수(람다식) (1) | 2024.10.05 |
[Java] Stream (2) | 2024.09.29 |
[Java] Optional (2) | 2024.09.29 |
[Java] 리스트(List) 와 배열(Array)의 차이점 (0) | 2024.09.29 |