Java 객체지향 설계를 해보자 - 계산기 공장 만들기

Doyeon·2023년 1월 27일
0
post-thumbnail

💡 객체지향 프로그래밍이란?

자바는 대표적인 객체지향 프로그래밍 언어이다.

그렇다면 객체지향 프로그래밍이 도대체 뭘까?

객체지향이 뭐길래 프로그램 변경이 쉽고 재사용성이 좋아지는걸까?

객체지향을 이해하기 위해서는 객체지향의 특성인 캡슐화, 상속, 추상화, 다형성을 알아야 한다.

계산기 공장을 만들면서 객체지향 특성을 살려서 프로그래밍을 구현해보자!


🏭 계산기 공장

Step 1

🕹️ 계산기 기능 : 출력, 연산
  • A타입 : +, -, *, /
  • B타입 : *, /
  • C타입 : +, -, *, /, %

공통 요구사항 발생

  • D 타입 추가 요구
  • D 타입 : +, - 기능

구현 기능이 조금씩 다른 A, B, C 타입의 계산기를 만들려고 한다.
각각 CalculatorA , CalculatorB , CalculatorC 로 클래스를 만들어보자.

  • CalculatorA
    public class CalculatorA {
        final int WIDTH;
        final int HEIGHT;
    
        public CalculatorA() {
            WIDTH = 30;
            HEIGHT = 30;
        }
    
        void display(int num1 , String operator, int num2, double result) {
            System.out.println(num1 + " " + operator + " " + num2 + " = " + result);
        }
    
        double operate(int num1, String operator, int num2) {
            double result = -99999;
            switch (operator) {
                case "+":
                    result = num1 + num2;
                    break;
                case "-":
                    result = num1 - num2;
                    break;
                case "*":
                    result = num1 * num2;
                    break;
                case "/":
                    if (num2 > 0) {
                        result = num1 / num2;
                    }
                    break;
            }
            display(num1, operator, num2, result);
            return result;
        }
    
    }
  • CalculatorB
    public class CalculatorB {
        final int WIDTH;
        final int HEIGHT;
    
        public CalculatorB() {
            WIDTH = 30;
            HEIGHT = 30;
        }
    
        void display(int num1 , String operator, int num2, double result) {
            System.out.println(num1 + " " + operator + " " + num2 + " = " + result);
        }
    
        double operate(int num1, String operator, int num2) {
            double result = -99999;
            switch (operator) {
                case "*":
                    result = num1 * num2;
                    break;
                case "/":
                    if (num2 > 0) {
                        result = num1 / num2;
                    }
                    break;
            }
            display(num1, operator, num2, result);
            return result;
        }
    }
  • CalculatorC
    public class CalculatorC {
        final int WIDTH;
        final int HEIGHT;
    
        public CalculatorC() {
            WIDTH = 30;
            HEIGHT = 30;
        }
    
        void display(int num1 , String operator, int num2, double result) {
            System.out.println(num1 + " " + operator + " " + num2 + " = " + result);
        }
    
        double operate(int num1, String operator, int num2) {
            double result = -99999;
            switch (operator) {
                case "+":
                    result = num1 + num2;
                    break;
                case "-":
                    result = num1 - num2;
                    break;
                case "*":
                    result = num1 * num2;
                    break;
                case "/":
                    if (num2 > 0) {
                        result = num1 / num2;
                    }
                    break;
                case "%":
                    result = num1 % num2;
                    break;
            }
            display(num1, operator, num2, result);
            return result;
        }
    }
  • Factory
    public class Factory {
        public static void main(String[] args) {
            CalculatorA calculatorA = new CalculatorA();
            calculatorA.operate(100 ,"/",20);
            System.out.println();
            CalculatorB calculatorB = new CalculatorB();
            calculatorB.operate(100 ,"-",20);
            System.out.println();
            CalculatorC calculatorC = new CalculatorC();
            calculatorC.operate(100 ,"%",21);
        }
    }

A, B, C 타입에 맞게 각각의 클래스를 구현했다. 이 때, +, - 기능만 탑재한 D 타입의 계산기를 추가해달라는 요청이 발생했다. CalculatorA, B, C 를 만들었듯이 D에는 +, - 기능만 넣고 클래스를 구현하면 다음과 같다.

  • CalculatorD
    public class CalculatorD {
        final int WIDTH;
        final int HEIGHT;
    
        public CalculatorD() {
            WIDTH = 30;
            HEIGHT = 30;
        }
    
        void display(int num1 , String operator, int num2, double result) {
            System.out.println(num1 + " " + operator + " " + num2 + " = " + result);
        }
    
        double operate(int num1, String operator, int num2) {
            double result = -99999;
            switch (operator) {
                case "+":
                    result = num1 + num2;
                    break;
                case "-":
                    result = num1 - num2;
                    break;
            }
            display(num1, operator, num2, result);
            return result;
        }
    }

Step 2

🕹️ 계산기 기능 : 출력, 연산
  • A타입 : +, -, *, /
  • B타입 : *, /
  • C타입 : +, -, *, /, %
  • D타입 : +, -

공통 요구사항 발생

  • -99999 일 경우 , Error 가 출력 되도록 변경

A, B, C, D 타입의 계산기를 만들어놨더니 또 다른 요구사항이 발생했다. 연산 결과 값이 -99999인 경우, “Error” 라는 메시지가 뜨도록 변경해달라고 한다.

CalculaterA 부터 CalculaterD 까지 일일이 출력 부분을 고치려고 하니 모든 클래스를 다 수정해야하는 번거로움이 생긴다. 어떻게 하면 좀 더 쉽게 변경사항을 수정할 수 있을까?

여기서 사용할 객체지향의 특성이 바로 “추상화”이다.

CalculaterA ~ CalculaterD 까지 모든 클래스에서 공통으로 사용하는 변수와 기능을 뽑아 추상클래스(Abstract class)로 만들어보자.

  • AbstractCalculator
    public abstract class AbstractCalculator {
        final int WIDTH = 30; // 공통으로 사용하는 변수도 추상 클래스에 선언해준다.
        final int HEIGHT = 30;
    
        abstract double operate(int num1, String operator, int num2);
    
        void display(int num1 , String operator, int num2, double result) {
            if (result == -99999)
                System.out.println("Error!");
            else
                System.out.println(num1 + " " + operator + " " + num2 + " = " + result);
        }
    }
    모든 계산기 타입에서 공통으로 사용하는 기능은 연산(operate)과 출력(display)이다.
    • operate (연산)
      • 연산은 계산기 타입마다 기능 구현이 달라지므로, abstract method(추상 메서드)로 만들어준다.
    • display (출력)
      • 모든 타입에서 출력 기능은 동일하므로, 일반 메서드로 작성해준다.

잠깐! 여기서 추상화의 편리함을 찾을 수 있다.

우리는 모든 타입의 계산기에서 result 값이 -99999 일 때, “Error!” 라는 메시지를 출력해주기로 했다.

추상 클래스가 없었다면, CalculatorA ~ CalculatorD까지 일일이 display 메서드를 수정해야 했을 것이다.

하지만, 추상 클래스를 만들어 놓은 덕분에 모든 클래스가 아닌 추상 클래스에 있는 display 메서드만 수정해주면 된다.

Calculator 클래스들은 AbstractCalculator 클래스를 상속받아서 추상메서드인 operate 를 타입에 맞게 기능 구현해주면 되고, display 함수는 호출만 해주면 된다.

이렇게 클래스가 상속을 받으면, 부모가 되는 클래스의 함수를 호출해서 바로 쓸 수 있다. 코드가 간결해진다!

  • CalculatorA
    public class CalculatorA extends AbstractCalculator {
    
        double operate(int num1, String operator, int num2) {
            double result = -99999;
            switch (operator) {
                case "+":
                    result = num1 + num2;
                    break;
                case "-":
                    result = num1 - num2;
                    break;
                case "*":
                    result = num1 * num2;
                    break;
                case "/":
                    if (num2 > 0) {
                        result = num1 / num2;
                    }
                    break;
            }
            display(num1, operator, num2, result);
            return result;
        }
    
    }
  • CalculatorB
    public class CalculatorB extends AbstractCalculator {
    
        double operate(int num1, String operator, int num2) {
            double result = -99999;
            switch (operator) {
                case "*":
                    result = num1 * num2;
                    break;
                case "/":
                    if (num2 > 0) {
                        result = num1 / num2;
                    }
                    break;
            }
            display(num1, operator, num2, result);
            return result;
        }
    }
  • CalculatorC
    public class CalculatorC extends AbstractCalculator {
    
        double operate(int num1, String operator, int num2) {
            double result = -99999;
            switch (operator) {
                case "+":
                    result = num1 + num2;
                    break;
                case "-":
                    result = num1 - num2;
                    break;
                case "*":
                    result = num1 * num2;
                    break;
                case "/":
                    if (num2 > 0) {
                        result = num1 / num2;
                    }
                    break;
                case "%":
                    result = num1 % num2;
                    break;
            }
            display(num1, operator, num2, result);
            return result;
        }
    }
  • CalculatorD
    public class CalculatorD extends AbstractCalculator {
    
        double operate(int num1, String operator, int num2) {
            double result = -99999;
            switch (operator) {
                case "+":
                    result = num1 + num2;
                    break;
                case "-":
                    result = num1 - num2;
                    break;
            }
            display(num1, operator, num2, result);
            return result;
        }
    
    }

Step 3

🕹️ 계산기 기능 : 출력, 연산
  • A타입 : +, -, *, /
  • B타입 : *, /
  • C타입 : +, -, *, /, %
  • D타입 : +, -

공통 요구사항 발생

  • 전원 on, off 기능 추가
  • num1 과 num2 스위칭 기능 추가
    - B, D 타입은 제외
    - 스위칭은 이전에 진행한 연산 상황에서 num1 과 num2를 변경하여 다시 재 연산 해주는 기능
    - 연산이 진행될 때 num1, num2, operator의 기록이 저장될 수 있도록 변수 추가 및 저장 코드 추가

또 다른 요구사항이 발생했다. 전원 기능과 스위치 기능이 필요하다. 추상 클래스를 만들어 기능들을 추상화하지 않았다면, 또 모든 타입의 계산기 클래스를 수정해야 했을 것이다.

하지만 우리는 추상 클래스를 만들었으므로 걱정할 필요 없다.

AbstractCalculator 에 새로 추가할 기능들을 만들어보자!

  • AbstractCalculator
    public abstract class AbstractCalculator {
        final int WIDTH = 30;
        final int HEIGHT = 30;
        boolean power = false; // 전원 기능에 필요한 변수 power
        int num1;         // 스위칭 기능으로 연산할 때 필요한 변수 num1, num2, operator 
        int num2;
        String operator;
    
        abstract double operate(int num1, String operator, int num2);
    
    		// 스위치 기능
        void switchingNumber() {
            operate(num2, operator, num1);
        }
    
    		// 전원 기능
        void powerOnOff() {
            power = !power;
            System.out.println("power = " + power);
        }
    
        void display(int num1 , String operator, int num2, double result) {
            if (result == -99999)
                System.out.println("Error!");
            else
                System.out.println(num1 + " " + operator + " " + num2 + " = " + result);
        }
    }
    • switchingNumber (스위치 기능)
      • num1과 num2를 바꾸어 연산한다.
      • 연산을 위해 operate 함수를 호출할텐데, 함수 호출 시 넣을 매개변수가 필요하다. 그래서 이 추상 클래스에서 num1, num2, operator 변수를 선언해줘야 한다.
    • powerOnOff (전원 기능)

이제 Calculator 클래스로 넘어가기 전에 생각해보자.

CalculatorA ~ CalculatorD 클래스에서는 operate 에서 계산을 위해 매개변수로 받은 num1, num2 와 operator 를, 부모인 AbstractCalculator 클래스의 변수 num1, num2, operator에 넣어줘야 한다.

public class CalculatorA extends AbstractCalculator {

    double operate(int num1, String operator, int num2) {
        **this.num1 = num1;
        this.num2 = num2;
        this.operator = operator;**
				// ..
		}
}

넣어주지 않는다면 어떤 문제가 발생할까?

public class Factory {
    public static void main(String[] args) {
        CalculatorA calculatorA = new CalculatorA();
        calculatorA.operate(100, "*", 5);
        System.out.println("switching");
        calculatorA.switchingNumber(); // Error! 
        System.out.println();
    }
}
public abstract class AbstractCalculator {
		// ...
    int num1;         
    int num2;
    String operator;

    void switchingNumber() {
        operate(num2, operator, num1);
    }
}

CalculatorA 에서 {this.num1 = num;}으로 부모 클래스에 있는 변수의 값을 정해주지 않았다면, switchingNumber() 를 호출했을 때, NullPointerException 이 발생한다. AbstractCalculator 에서 switchingNumber 를 실행시키려고 할 때, num1, num2, operator 값이 지정되지 않았기 때문이다.

그래서 CalculatorA ~ CalculatorD 클래스의 operate 메서드 내에서 {this.num1 = num;} 으로 값을 넣어줘야 한다.

그런데 잠깐, 계산기 B와 D는 스위치 기능이 제외되었다. 하지만 지금 상태라면 CalculatorBCalculatorD 의 인스턴스에서 switchingNumber() 메서드를 호출하면 AbstractCalculator 에서 선언한 메서드가 실행되고만다.

switchingNumber() 메서드가 실행되지 않도록 @Override 를 통해 “Error!” 메시지가 나오도록 구현해주자.

  • CalculatorA
    public class CalculatorA extends AbstractCalculator {
    
        double operate(int num1, String operator, int num2) {
            this.num1 = num1;
            this.num2 = num2;
            this.operator = operator;
            double result = -99999;
            switch (operator) {
                // ...
            }
            display(num1, operator, num2, result);
            return result;
        }
    
    }
  • CalculatorB
    public class CalculatorB extends AbstractCalculator {
    
        @Override
        void switchingNumber() {
            System.out.println("Error! - no switching function");
        }
    
        double operate(int num1, String operator, int num2) {
            this.num1 = num1;
            this.num2 = num2;
            this.operator = operator;
            double result = -99999;
            switch (operator) {
                // ...
            }
            display(num1, operator, num2, result);
            return result;
        }
    }
  • CalculatorC
    public class CalculatorC extends AbstractCalculator {
    
        double operate(int num1, String operator, int num2) {
            this.num1 = num1;
            this.num2 = num2;
            this.operator = operator;
            double result = -99999;
            switch (operator) {
                // ...
            }
            display(num1, operator, num2, result);
            return result;
        }
    }
  • CalculatorD
    public class CalculatorD extends AbstractCalculator {
    
    		@Override
        void switchingNumber() {
            System.out.println("Error! - no switching function");
        }
    
        double operate(int num1, String operator, int num2) {
            this.num1 = num1;
            this.num2 = num2;
            this.operator = operator;
            double result = -99999;
            switch (operator) {
                // ...
            }
            display(num1, operator, num2, result);
            return result;
        }
    
    }

step3 - change1 [인터페이스]

여기까지 구현하고 보니, CalculatorBCalculatorD 가 마음에 걸린다. 계산기 B, D 는 스위치 기능이 없는 계산기인데, switchingNumber() 라는 메서드가 아예 호출되지 않도록 만들 수는 없을까?

지금은 계산기 B, D 에서도 스위치 기능이 호출 가능하기 때문에, 스위치 기능이 호출되었을 경우 “Error!”라고 띄워주도록 코드를 작성했다. 그런데 이 코드는 불필요하지 않은가?

이 불필요함을 해소하기 위해 우리는 공통 기능을 모아 인터페이스를 만들 것이다.

A, B, C, D 타입의 모든 계산기에서 사용하는 기능 따로, A, C 타입 계산기에서만 사용하는 기능 따로 구현해 볼 것이다.

  • A, B, C, D 공통 기능 → CommonFunction

    • operate : 연산 기능
    • powerOnOff : 전원 기능
    • display : 출력 기능
  • A, C 공통 기능 -> SwitchingFunction

    • switchingNumber : 스위칭 기능
  • CommonFunction

    public interface CommonFunction {
        abstract double operate(int num1, String operator, int num2);
        void powerOnOff();
        void display(int num1 , String operator, int num2, double result);
    }
  • SwitchingFunction

    public interface SwitchingFunction {
        void switchingNumber();
    }

이제 인터페이스 구현추상 클래스 상속을 통해 계산기 클래스를 수정해보자!

  • abstract class AbstractCalculator <- Interface CommonFunction
  • class CalculatorACalculatorC <- abstract class AbstractCalculator
  • class CalculatorACalculatorC <- Interface SwitchingFunction
  • class CalculatorBCalculatorD <- abstract class AbstractCalculator

인터페이스에 있는 메서드는 public 이므로, 계산기 클래스에서 구현할 때도 public 으로 해줘야 됨을 기억하자!

  • CalculatorA
    public class CalculatorA extends AbstractCalculator implements SwitchingFunction {
    
        public double operate(int num1, String operator, int num2) {
            this.num1 = num1;
            this.num2 = num2;
            this.operator = operator;
            double result = -99999;
            switch (operator) {
                // ...
            }
            display(num1, operator, num2, result);
            return result;
        }
    
        @Override
        public void switchingNumber() {
            operate(num2, operator, num1);
        }
    }
  • CalculatorB
    public class CalculatorB extends AbstractCalculator {
    
        public double operate(int num1, String operator, int num2) {
            this.num1 = num1;
            this.num2 = num2;
            this.operator = operator;
            double result = -99999;
            switch (operator) {
                // ...
            }
            display(num1, operator, num2, result);
            return result;
        }
    }
  • CalculatorC
    public class CalculatorC extends AbstractCalculator implements SwitchingFunction {
    
        public double operate(int num1, String operator, int num2) {
            this.num1 = num1;
            this.num2 = num2;
            this.operator = operator;
            double result = -99999;
            switch (operator) {
                // ...
            }
            display(num1, operator, num2, result);
            return result;
        }
    
        @Override
        public void switchingNumber() {
            operate(num2, operator, num1);
        }
    }
  • CalculatorD
    public class CalculatorD extends AbstractCalculator {
    
        public double operate(int num1, String operator, int num2) {
            this.num1 = num1;
            this.num2 = num2;
            this.operator = operator;
            double result = -99999;
            switch (operator) {
                // ...
            }
            display(num1, operator, num2, result);
            return result;
        }
    
    }
  • Factory
    public class Factory {
        public static void main(String[] args) {
            CalculatorA calculatorA = new CalculatorA();
            calculatorA.operate(100, "*", 5);
            calculatorA.operate(100, "/", 3);
            calculatorA.display(100, "-", 5, 95);
            calculatorA.powerOnOff();
            System.out.println("switching");
            calculatorA.switchingNumber();
            System.out.println();
            CalculatorB calculatorB = new CalculatorB();
            calculatorB.operate(100, "*", 2);
            calculatorB.operate(100, "-", 3);
            calculatorB.display(100, "/", 5, 20);
            calculatorB.powerOnOff();
    //    		calculatorB.switchingNumber();  // error!
            System.out.println();
            CalculatorC calculatorC = new CalculatorC();
            calculatorC.operate(100, "*", 3);
            calculatorC.operate(100, "%", 5);
            calculatorC.display(100, "+", 5, 0);
            calculatorC.powerOnOff();
            System.out.println("switching");
            calculatorC.switchingNumber();
            System.out.println();
            CalculatorD calculatorD = new CalculatorD();
            calculatorD.operate(100, "%", 3);
            calculatorD.operate(100, "-", 5);
            calculatorD.display(100, "+", 5, 105);
            calculatorD.powerOnOff();
    //    		calculatorD.switchingNumber();  // error!
        }
    }

공통 기능과 스위치 기능을 나누어 인터페이스를 구현했기 때문에, 이제 CalculatorBCalculatorD 에서는 switchingNumber() 메서드를 부를 수 없다. 기능을 사용 안한다고 굳이 오버라이딩해서 에러 메시지를 띄우지 않아도 된다.


step3 - change2 [캡슐화]

이번에는 추상 클래스 AbstractCalculator 에 있는 num1, num2, operator 변수를 외부에서 쉽게 바꾸지 못하도록 캡슐화할 것이다.

캡슐화를 위해 AbstractCalculator 클래스의 변수 num1, num2, operator 를 private 으로 만들자.

그리고 변수의 초기값을 만들어주는 메서드들을 만들 것이다.

  • setOperateVar : 위 변수들 값을 초기화 하는 메서드 (값을 초기화할 때 꼭 생성자만 사용되는 것은 아니다.)

  • getNum1getNum2getOperator : private 변수들의 값을 얻는 getter

  • setNum1setNum2setOperator : private 변수들의 값을 설정하는 setter

  • AbstractCalculator

    public abstract class AbstractCalculator implements CommonFunction {
        final int WIDTH = 30;
        final int HEIGHT = 30;
        boolean power = false;
        private int num1;
        private int num2;
        private String operator;
    
        public AbstractCalculator() {
        }
    
        public AbstractCalculator(int num1, String operator, int num2) {
            this.num1 = num1;
            this.operator = operator;
            this.num2 = num2;
        }
    
        public void setOperateVar(int num1, String operator, int num2) {
            this.num1 = num1;
            this.num2 = num2;
            this.operator = operator;
        }
    
        public int getNum1() {
            return num1;
        }
    
        public void setNum1(int num1) {
            this.num1 = num1;
        }
    
        public int getNum2() {
            return num2;
        }
    
        public void setNum2(int num2) {
            this.num2 = num2;
        }
    
        public String getOperator() {
            return operator;
        }
    
        public void setOperator(String operator) {
            this.operator = operator;
        }
    
        public void powerOnOff() {
            power = !power;
            System.out.println("power = " + power);
        }
    
        public void display(int num1 , String operator, int num2, double result) {
            if (result == -99999)
                System.out.println("Error!");
            else
                System.out.println(num1 + " " + operator + " " + num2 + " = " + result);
        }
    }

이제 계산기 클래스에서도 {this.num1 = num1;} 이런 식으로 부모 클래스의 변수에 접근하지 못한다.

setOperateVar 을 이용해 모두 초기화해주자.

  • CalulatorA
    public class CalculatorA extends AbstractCalculator implements SwitchingFunction {
    
        public CalculatorA() {
        }
    
        public CalculatorA(int num1, String operator, int num2) {
            super(num1, operator, num2);
        }
    
        public double operate(int num1, String operator, int num2) {
            // num1, num2, operate에 해당하는 동일한 변수들이 없기 때문에 super, this를 이용해 다양한 방식으로 접근이 가능하다.
    				// setOperateVar 사용해도 된다.
    				super.setNum1(num1);
            this.setNum2(num2);
            setOperator(operator);
            double result = -99999;
            switch (operator) {
                case "+":
                    result = num1 + num2;
                    break;
                case "-":
                    result = num1 - num2;
                    break;
                case "*":
                    result = num1 * num2;
                    break;
                case "/":
                    if (num2 > 0) {
                        result = num1 / num2;
                    }
                    break;
            }
            display(num1, operator, num2, result);
            return result;
        }
    
        @Override
        public void switchingNumber() {
            operate(super.getNum2(), this.getOperator(), getNum1()); // 변수 가져올때도 getter 사용
        }
    }
  • CalculatorB
    public class CalculatorB extends AbstractCalculator {
    
        public double operate(int num1, String operator, int num2) {
            setOperateVar(num1, operator, num2);  // 변수 값 초기화를 위해 setOperateVar 사용
            double result = -99999;
            // ...
            }
            display(num1, operator, num2, result);
            return result;
        }
    }
  • CalculatorC
    public class CalculatorC extends AbstractCalculator implements SwitchingFunction {
    
        public double operate(int num1, String operator, int num2) {
            setOperateVar(num1, operator, num2);
            double result = -99999;
            // ...
        }
    
        @Override
        public void switchingNumber() {
            operate(super.getNum2(), this.getOperator(), getNum1());
        }
    }
  • CalculatorD
    public class CalculatorD extends AbstractCalculator {
    
        public double operate(int num1, String operator, int num2) {
            setOperateVar(num1, operator, num2);
            double result = -99999;
            // ...
        }
    
    }

TEST를 해보자! - [다형성]

이제 테스트 코드를 만들어보자. 매개변수 타입이 다음과 같은 코드를 만들 것이다.

  • CommonFunction Interface
  • SwitchingFunction Interface
  • AbstractCalculator abstract class
  • Test
    // import는 개발한 경로에 맞춰서 작성
    import javaprac.calculatorfactory.step3.change2private.AbstractCalculator;
    import javaprac.calculatorfactory.step3.change2private.CommonFunction;
    import javaprac.calculatorfactory.step3.change2private.SwitchingFunction;
    
    public class Test {
        void calculatorTestCommonFunction(CommonFunction calculator) {
            calculator.operate(100, "*", 5);
            calculator.display(100, "-", 5, 95);
            calculator.powerOnOff();;
        }
    
        void calculatorTestSwitching(SwitchingFunction calculator) {
            calculator.switchingNumber();
        }
    
        void calculatorTestAbstract(AbstractCalculator calculator) {
            calculator.operate(100, "%", 5);
            calculator.display(100, "+", 5, 105);
            calculator.powerOnOff();
        }
    }
  • Factory
    import javaprac.calculatorfactory.step3.change2private.*;
    
    public class Factory {
        public static void main(String[] args) {
            // AbstractCalculator calculator; 이것도 사용 가능!
            CommonFunction calculator; 
    
            calculator = new CalculatorA();
            calculator.operate(100, "/", 5);
            calculator.display(100, "/", 5, 20);
            calculator.powerOnOff();
            System.out.println("switching");
            // calculator.switchingNumber(); // 다형성 원리에 의해서 해당 스위칭 기능은 추상화된 클래스 즉, 부모 클래스에 없는 기능이기 때문에 사용 불가능
    
            System.out.println();
            calculator = new CalculatorB();
            calculator.operate(100, "%", 5);
            calculator.display(100, "*", 5, 500);
            calculator.powerOnOff();
            System.out.println();
    
            calculator = new CalculatorC();
            calculator.operate(100, "/", 5);
            calculator.display(100, "%", 5, 0);
            calculator.powerOnOff();
            System.out.println();
    
            calculator = new CalculatorD();
            calculator.operate(100, "/", 5);
            calculator.display(100, "-", 5, 95);
            calculator.powerOnOff();
            System.out.println();
    
            // SwitchingFunction function2 = new CalculatorB(); // Error! CalculatorB 는 SwitchingFunction 인터페이스를 구현하지 않음
            SwitchingFunction function = new CalculatorA(30, "*", 50);
            function.switchingNumber();
            System.out.println();
    
            // 매개변수 다형성
            Test test = new Test();
            test.calculatorTestCommonFunction(new CalculatorB());
            System.out.println();
            test.calculatorTestSwitching(new CalculatorA(30, "*", 50));
            System.out.println();
            test.calculatorTestAbstract(new CalculatorC());
        }
    }
    • 이 코드는 CommonFunction 타입의 변수 calculator를 만들었지만, AbstractCalculator 타입으로 선언해도 된다.
      • operate 메서드는 AbstractCalculator 에 없지만 CommonFunction 인터페이스에 정의되어 있었고 이를 AbstractCalculator 가 구현했기 때문에 사용 가능하다
    • SwitchingFunction function = new CalculatorA(30, "*", 50);
      • 원래는 연산을 한번 진행하고 변수를 기록해야 하지만, SwitchingFunction 을 사용하면 operate 메서드를 사용할 수 없기 때문에 테스트를 위해 만들어둔 생성자를 사용해서 강제로 num1, num2, operator 에 값을 넣는다
    • test 함수의 매개변수 타입에 해당하는 인터페이스를 구현하거나, 매개변수 타입에 해당하는 추상 클래스를 상속한 클래스의 인스턴스라면 매개변수로 사용할 수 있다. → 매개변수의 다형성
profile
🔥

0개의 댓글