더하기, 빼기, 나누기, 곱하기 연산을 수행할 수 있는 Calculator 클래스
Calculator Class
public double calculate(String operator,int firstNumber, int secondNumber){
double ret = 0.0;
if("+".equals(operator)) {
ret = firstNumber + secondNumber;
}
else if("-".equals(operator)){
ret = firstNumber - secondNumber;
}
else if("*".equals(operator)) {
ret = firstNumber * secondNumber;
}
else if("/".equals(operator)){
ret = firstNumber / secondNumber;
}
return ret;
}
테스트
public class Main {
public static void main(String[] args) {
Calculator cal = new Calculator();
int firstNum = 2;
int secondNum = 2;
System.out.println(firstNum + " + " + secondNum + " = " + cal.calculate("+",firstNum,secondNum));
System.out.println(firstNum + " - " + secondNum + " = " + cal.calculate("-",firstNum,secondNum));
System.out.println(firstNum + " * " + secondNum + " = " +cal.calculate("*",firstNum,secondNum));
System.out.println(firstNum + " / " + secondNum + " = " +cal.calculate("/",firstNum,secondNum));
}
}
결과
-> %연산자 추가라 가볍게 패스
AddOperation(더하기), SubstractOperation(빼기), MultiplyOperation(곱하기), DivideOperation(나누기) 연산 클래스를을 만든 후 클래스간의 관계를 고려하여 Calculator 클래스와 관계를 맺습니다.
public class AddOperation {
public double operate(int firstNumber, int secondNumber) {
return firstNumber + secondNumber;
}
}
public class SubstractOperation {
public double operate(int firstNumber, int secondNumber) {
return firstNumber - secondNumber;
}
}
public class MultiplyOperation {
public double operate(int firstNumber, int secondNumber) {
return firstNumber * secondNumber;
}
}
public class DivideOperation {
public double operate(int firstNumber, int secondNumber) {
return firstNumber / secondNumber;
}
}
public class Calculator {
private final AddOperation addOperation;
private final SubstractOperation substractOperation;
private final MultiplyOperation multiplyOperation;
private final DivideOperation divideOperation;
public Calculator() {
this.addOperation = new AddOperation();
this.substractOperation = new SubstractOperation();
this.multiplyOperation = new MultiplyOperation();
this.divideOperation = new DivideOperation();
}
public double add(int firstNum, int secondNum) {
return addOperation.operate(firstNum,secondNum);
}
public double substract(int firstNum, int secondNum) {
return substractOperation.operate(firstNum,secondNum);
}
public double multiply(int firstNum, int secondNum) {
return multiplyOperation.operate(firstNum,secondNum);
}
public double divide(int firstNum, int secondNum) {
if(secondNum == 0) {
throw new ArithmeticException("0을 나눌수 없습니다");
}
return divideOperation.operate(firstNum,secondNum);
}
}
public class Main {
public static void main(String[] args) {
Calculator calculator = new Calculator();
System.out.println(calculator.add(2,2));
System.out.println(calculator.subtract(2,2));
System.out.println(calculator.multiply(2,2));
System.out.println(calculator.divide(2,2));
}
}
자바의 코드는 SOLID원칙을 따라야 좋은 코드이다
Step 2에서 한 작업은 SOLID의 S인 SRP(Single Responsibility) 단일책임을 지킨 케이스이다.
좀더 설명 하자면
Step1에서는 Calculator Class 하나에 calculate 메소드가 존재한다
그러면 Calulator Class에선 하나의 책임만 있는가?
아니다 +,-,*,/ 4가지 책임이 있다... 그런즉 단일책임이 아닌셈이다
그러면 Step 2를 보다
Calulator Class에선 각 operation을 정해주는 하나의 책임과
add,sub,mul,div Class에서는 각 operation을 수행해주는 하나의 책임이 있다,
즉 SRP를 잘 따른셈이다.
AddOperation(더하기), SubstractOperation(빼기), MultiplyOperation(곱하기), DivideOperation(나누기) 연산 클래스들을 AbstractOperation(추상 클래스)를 사용하여 추상화하고 Calculator 클래스의 내부 코드를 변경합니다.
UML 보시기 전에 아래의 관계는 모두 포함관계라고 보시면 됩니다!!
추상 클래스를 쓴다. 즉 미완성 설계도이라 완성된 제품을 만들 수 없듯이 추상 클래스로 인스턴스를 생성할 수 없다.
그래서 추상 클래스는 상속을 통해서 자손클래스에 의해서만 완성 시킬수 있다.
-> 자손인 AddOperation,SubstractOperation,MultiplyOperation,DivideOperation Class에서 완성을 시켜줘야 한다!
public class AddOperation extends AbstractOperation {
@Override
double operate(int firstNumber, int secondNumber) {
return firstNumber + secondNumber;
}
}
public class SubstractOperation extends AbstractOperation{
@Override
public double operate(int firstNumber, int secondNumber) {
return firstNumber - secondNumber;
}
}
public class MultiplyOperation extends AbstractOperation{
@Override
public double operate(int firstNumber, int secondNumber) {
return firstNumber * secondNumber;
}
}
public class DivideOperation extends AbstractOperation{
@Override
public double operate(int firstNumber, int secondNumber) {
return firstNumber / secondNumber;
}
}
abstract class AbstractOperation {
abstract double operate(int firstNumber, int secondNumber);
}
public class Calculator {
private AbstractOperation operation;
public Calculator(AbstractOperation operation){
this.operation = operation;
}
public double calculate(int firstNum, int secondNum) {
return operation.operate(firstNum,secondNum);
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("시용할 연산자를 선택하세요 (+,-,*,/)");
String operator;
operator= sc.nextLine();
AbstractOperation operation = null;
while(!"+".equals(operator) && !"-".equals(operator) && !"*".equals(operator) && !"/".equals(operator)) {
System.out.println("잘못된 연산자를 사용하였습니다");
System.out.println("시용할 연산자를 선택하세요 (+,-,*,/)");
operator = sc.nextLine();
}
if("+".equals(operator)) {
operation = new AddOperation();
}
else if("-".equals(operator)){
operation = new SubstractOperation();
}
else if("*".equals(operator)){
operation = new MultiplyOperation();
}
else if("/".equals(operator)){
operation = new DivideOperation();
}
Calculator calculator = new Calculator(operation);
System.out.println("첫 번째 숫자를 입력하세요:");
int firstNum = sc.nextInt();
sc.nextLine();
System.out.println("두 번째 숫자를 입력하세요:");
int secondNum = sc.nextInt();
System.out.println(firstNum + " " + operator + " " + secondNum + "= " + calculator.calculate(firstNum,secondNum));
}
}
자바에서 인터페이스에 대해 학습할 떄 매개변수로 객체를 받을 때 구체 클래스 타입으로 받는게 아니라, 다형성을 이용해 인터페이스 타입으로 통신하는 것이 좋다.
대표적으로 컬렉션 프레임워크에서 ArrayList 자료형을 인스턴스화 할 때 변수 타입으로 ArrayList 구체 클래스 타입으로 선언하는 것이 아닌 List 같은 인터페이스 타입으로 선언.
이 또한 DIP 원칙을 따른것이다!!
높은 결합도의 문제점
위에 문제점을 보아 step4 코드는 낮은 결합도를 가지고 있다
그래서 위에 숙제는 추상클래스를 통해 나머지 연산자 operation 클래스안에서 operate 메소드를 override해서 완성을 시켜서 사용을 한다!
더이상 연산자를 매개변수의 통해 받을 필요가 없습니다!!
왜냐하면 해당 연산자로 opertaion으로 받기떄문에 그 operation은 해당 연산자로 밖에 쓰이지 못한다!