단일 책임 원칙 위반 증상
- 기능 변경이 발생했을 때 연쇄적으로 코드를 수정해야 합니다.
- 기능이 너무 복잡해서 재사용하기 어렵습니다.
- 메서드의 크기가 비대해 집니다
해결 방법
- 기능별로 클래스를 구현한다.
- 상속을 통해 각 클래스들을 이용한다.
public class AddOperation {
public int operate(int firstNumber, int secondNumber){
return firstNumber + secondNumber;
}
}// 덧셈 클래스 구현
public class SubstractOperation {
public int operate(int firstNumber, int secondNumber){
return firstNumber - secondNumber;
}
}// 뺄셈 클래스 구현
public class MultiplyOperation {
public int operate(int firstNumber, int secondNumber){
return firstNumber * secondNumber;
}
}// 곱셈 클래스 구현
public class DivideOperation {
public int operate(int firstNumber, int secondNumber){
return firstNumber / secondNumber;
}
}// 나눗셈 클래스 구현
public class Calculator {
private AddOperation addOperation;
private SubstractOperation substractOperation;
private MultiplyOperation multiplyOperation;
private DivideOperation divideOperation;
// 클래스를 변수의 타입으로 선언
public Calculator(AddOperation addOperation, SubstractOperation substractOperation, MultiplyOperation multiplyOperation, DivideOperation divideOperation) {
this.addOperation = addOperation;
this.substractOperation = substractOperation;
this.multiplyOperation = multiplyOperation;
this.divideOperation = divideOperation;
} // 생성자
public int calculate(String operator, int firstNumber, int secondNumber) {
int answer = 0;
if(operator.equals("+")){
answer = addOperation.operate(firstNumber, secondNumber);
}else if(operator.equals("-")){
answer = substractOperation.operate(firstNumber, secondNumber);
}else if(operator.equals("*")){
answer = multiplyOperation.operate(firstNumber, secondNumber);
}else if(operator.equals("/")){
answer = divideOperation.operate(firstNumber, secondNumber);
} // 연산자 판별
return answer;
}
}
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator(
new AddOperation(),
new SubstractOperation(),
new MultiplyOperation(),
new DivideOperation()
);
int firNum = 140;
int secNum = 60;
String operator = "+";
int answer = calculator.calculate(operator, firNum, secNum);
System.out.println(operator + " answer = " + answer);
operator = "-";
answer = calculator.calculate(operator, firNum, secNum);
System.out.println(operator + " answer = " + answer);
operator = "*";
answer = calculator.calculate(operator, firNum, secNum);
System.out.println(operator + " answer = " + answer);
operator = "/";
answer = calculator.calculate(operator, firNum, secNum);
System.out.println(operator + " answer = " + answer);
}
}
- 각각의 연산 클래스 구현.
- 클래스를 변수의 타입으로 선언.
- 생성자를 통해 클라이언트에서 파라미터 사용 준비
- 연산자 판별 - 값 출력
개방-폐쇄 원칙 위반 증상
- 기능 확장을 할 때마다 기존코드를 수정해야 한다.
- if else 블록이 자주 등장합니다.
- 기능 확장을 위한 코드 수정을 할 때 여러 클래스에서 다발적으로 진행이 됩니다.
해결 방법
- 변화되는 부분을 추상화해서 변화를 고정 시킨다.
- 기능이 추가될 때 클래스의 상속을 통해서 하위 클래스에서 기능을 구현 하도록 한다.
- 기존코드를 수정하지 않아도 객체 상속의 다형성 원리에 의해 기능이 확장 되도록 합니다.
- 연산 클래스는 추상화된 부모 클래스를 상속받아 기능별로 구현합니다.
``
public abstract class AbstractOperation {
public abstract int operate(int firstNumber, int secondNumber);
}// 고정된 부분 - 추상 메서드 선언.
public class AddOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber + secondNumber;
}
} // 추상메서드 오버라이드 - 덧셈 구현
public class SubstractOperation extends AbstractOperation{
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber - secondNumber;
}
}// 추상메서드 오버라이드 - 뺄셈 구현
public class MultiplyOperation extends AbstractOperation{
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber * secondNumber;
}
}// 추상메서드 오버라이드 - 곱셈 구현
public class DivideOperation extends AbstractOperation{
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber / secondNumber;
}
}// 추상메서드 오버라이드 - 나눗셈 구현
public class Calculator {
// 추상화된 부모 클래스를 포함관계로 사용한다.
private AbstractOperation operation;
public void setOperation(AbstractOperation operation) {
this.operation = operation;
}
// 연산 기능을 추상화된 부모클래스에 의존하여 처리한다.
public int calculate(int firstNumber, int secondNumber) {
return operation.operate(firstNumber, secondNumber);
}
}
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator();
int firNum = 140;
int secNum = 60;
calculator.setOperation(new AddOperation());
int answer = calculator.calculate(firNum, secNum);
System.out.println(" + answer = " + answer);
calculator.setOperation(new SubstractOperation());
answer = calculator.calculate(firNum, secNum);
System.out.println(" - answer = " + answer);
calculator.setOperation(new MultiplyOperation());
answer = calculator.calculate(firNum, secNum);
System.out.println(" * answer = " + answer);
calculator.setOperation(new DivideOperation());
answer = calculator.calculate(firNum, secNum);
System.out.println(" / answer = " + answer);
}
}
1. 각 클래스별 공통 부분을 추상클래스로 선언.
2. 각 클래스에 추상메서드 오버라이드 사용 - 연산식
3. 추상화된 부모클래스 변수 타입으로 선언.
4. 부모클래스에 의존해 기능 처리.
---
### 리스코프 치환 원칙 위반 증상
- 객체의 타입을 확인.(instanceof)
- 자식 클래스명이 연관되거나 의존성이 있는 클래스에서 자주 발생.
### 해결 방법
- 부모 와 자식 클래스 사이의 행위가 일관성이 있도록 추상화를 좀 더 정교하게 구현합니다.
- 연산 기능을 추상화한 부모 클래스에 피연산자 값의 유효성 검사를 진행하는 메서드를 추가해 줍니다.
- 계산기 클래스에서는 이 메서드를 사용하여 유효성 검사를 진행하고 이 유효성 검사가 필요한 자식 클래스에서는 이 추가된 유효성 검사 조건을 구체화 합니다.
public abstract class AbstractOperation {
public abstract int operate(int firstNumber, int secondNumber);
public boolean isInvalid(int firstNumber, int secondNumber) {
return false;
}
} // 확인용 isInvalid 메서드 선언 / 공통 부분 추상 메서드
public class AddOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber + secondNumber;
}
}// 추상클래스 오버라이드 - 연산식 반환
public class SubstractOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber - secondNumber;
}
}// 추상클래스 오버라이드 - 연산식 반환
public class MultiplyOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber * secondNumber;
}
}// 추상클래스 오버라이드 - 연산식 반환
public class DivideOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber / secondNumber;
}// 추상클래스 오버라이드 - 연산식 반환
@Override
public boolean isInvalid(int firstNumber, int secondNumber) {
if (secondNumber == 0) {
return true;
} else {
return false;
}
}// 유효성 검사
}
public class Calculator {
// 연산 기능을 추상화된 부모클래스에 의존하여 처리한다.
public int calculate(AbstractOperation operation, int firstNumber, int secondNumber) {
// instanceof 로 DivideOperation 를 따로 구분하지 않고 처리 가능
if(operation.isInvalid(firstNumber, secondNumber)){
return -99999;
}
return operation.operate(firstNumber, secondNumber);
}
}
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator();
int firNum = 140;
int secNum = 60;
int answer = calculator.calculate(new AddOperation(), firNum, secNum);
System.out.println(" + answer = " + answer);
answer = calculator.calculate(new SubstractOperation(), firNum, secNum);
System.out.println(" - answer = " + answer);
answer = calculator.calculate(new MultiplyOperation(), firNum, secNum);
System.out.println(" * answer = " + answer);
answer = calculator.calculate(new DivideOperation(), firNum, secNum);
System.out.println(" / answer = " + answer);
}
}
1. 확인용 메서드 선언.
2. 공통 부분 추상 클래스 사용
3. 각 클래스별 오버라이드 - 연산
4. 유효성 검사
---
### 인터페이스 분리 원칙 증상
- 필요하지 않은 기능을 강제로 구현해야하는 상황이 발생.
- 필요하지 않은 혹은 사용 못 하는 기능이 강제로 구현되어 사용하지 못하도록 예외처리를 해야하는 상황이 발생할 수 있음.
### 해결 방법
- 필요하지 않은 기능을 강제로 구현하지 않도록 인터페이스를 분리.
- 연산 결과를 보여주는 방법마다 인터페이스 구현.
public abstract class AbstractOperation {
public abstract int operate(int firstNumber, int secondNumber);
public abstract String getOperator();
} // 공통 부분 추상메서드 선언.
public class AddOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber + secondNumber;
}
@Override
public String getOperator() {
return "+";
}
}// 추상메서드 오버라이드
public class SubstractOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber - secondNumber;
}
@Override
public String getOperator() {
return "-";
}
}// 추상메서드 오버라이드
public class MultiplyOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber * secondNumber;
}
@Override
public String getOperator() {
return "*";
}
}// 추상메서드 오버라이드
public class DivideOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber / secondNumber;
}
@Override
public String getOperator() {
return "/";
}
}// 추상메서드 오버라이드
public interface DisplayResult {
public abstract void displayResult(AbstractOperation operation, int firstNumber, int secondNumber);
} // 각각의 기능에 맞는 인터페이스 선언.
public interface DisplayWithOperator {
public abstract void displayResultWithOperator(AbstractOperation operation, int firstNumber, int secondNumber);
}
// 연산 결과만 출력
public class DisplayTypeA extends Calculator implements DisplayResult{
@Override
public void displayResult(AbstractOperation operation, int firstNumber, int secondNumber) {
int answer = calculate(operation, firstNumber, secondNumber);
System.out.println(answer);
}
}
// 연산 과정을 포함한 출력
public class DisplayTypeB extends Calculator implements DisplayWithOperator{
@Override
public void displayResultWithOperator(AbstractOperation operation, int firstNumber, int secondNumber) {
int answer = calculate(operation, firstNumber, secondNumber);
String operator = operation.getOperator();
System.out.println(firstNumber + " " + operator + " " + secondNumber + " = " + answer);
}
}
public class Calculator {
public int calculate(AbstractOperation operation, int firstNumber, int secondNumber){
return operation.operate(firstNumber, secondNumber);
}
}
public class Client {
public static void main(String[] args) throws Exception {
int firNum = 140;
int secNum = 60;
// 연산 결과만 출력
DisplayTypeA displayTypeA = new DisplayTypeA();
displayTypeA.displayResult(new AddOperation(), firNum, secNum);
// 연산 과정까지 출력
DisplayTypeB displayTypeB = new DisplayTypeB();
displayTypeB.displayResultWithOperator(new AddOperation(), firNum, secNum);
}
}
1. 공통 부분 추상 메서드.
2. 필요한 기능이 있는 인터페이스 구현.
3. 각 클래스별 인터페이스 상속
---
### 의존성 역전 원칙을 위반 증상
- 저수준 모듈에서 변경이 발생되면 고수준 모듈에 수정사항이 발생.
### 해결 방법
- 고수준 모듈의 변화되는 부분을 추상화한다.
- 저수준 모듈을 추상화에 의존시킨다.
- 계산기 클래스에 추상화된 부모 클래스를 포함시킨다.
- 연산 클래스는 추상화된 부모 클래스를 상속받아 기능별로 구현.
public abstract class AbstractOperation {
public abstract int operate(int firstNumber, int secondNumber);
}// 공통 부분 추상클래스 선언.
public class AddOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber + secondNumber;
}
} // 각 클래스별 추상메서드 오버라이드 연산
public class SubstractOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber - secondNumber;
}
} // 각 클래스별 추상메서드 오버라이드 연산
public class MultiplyOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber * secondNumber;
}
}// 각 클래스별 추상메서드 오버라이드 연산
public class DivideOperation extends AbstractOperation {
@Override
public int operate(int firstNumber, int secondNumber) {
return firstNumber / secondNumber;
}
}// 각 클래스별 추상메서드 오버라이드 연산
public class Calculator {
// 추상화된 부모 클래스를 포함관계로 사용한다.
private AbstractOperation operation;
public void setOperation(AbstractOperation operation) {
this.operation = operation;
}
// 연산 기능을 추상화된 부모클래스에 의존하여 처리한다.
public int calculate(int firstNumber, int secondNumber) {
return operation.operate(firstNumber, secondNumber);
}
}
public class Client {
public static void main(String[] args) {
Calculator calculator = new Calculator();
int firNum = 140;
int secNum = 60;
calculator.setOperation(new AddOperation());
int answer = calculator.calculate(firNum, secNum);
System.out.println(" + answer = " + answer);
calculator.setOperation(new SubstractOperation());
answer = calculator.calculate(firNum, secNum);
System.out.println(" - answer = " + answer);
calculator.setOperation(new MultiplyOperation());
answer = calculator.calculate(firNum, secNum);
System.out.println(" * answer = " + answer);
calculator.setOperation(new DivideOperation());
answer = calculator.calculate(firNum, secNum);
System.out.println(" / answer = " + answer);
}
}
```
- 공통 부분 추상클래스 선언
- 각 클래스별 상속,오버라이딩 연산 구현
- 부모 클래스 타입 선언.
- 3번을 이용한 값 출력.
'Java' 카테고리의 다른 글
[Java - Hash] HashTable / HashCollision(해시 충돌) (0) | 2023.09.04 |
---|---|
[Java] 직접 구현해보는 Stack (1) | 2023.09.03 |
[Java] REST API 간단 이해 (0) | 2023.08.04 |
[Java] DTO DAO 한번에 이해하기 (0) | 2023.07.30 |
[Java] 문자열 String, Buffer, Builder (0) | 2023.07.23 |