문제먼저 문제 원본은 아래와 같다.
문제에서 실제로 구현해야 하는 조건만 다시 정리하면 이렇다.
Multiplication클래스를 상속해서GuGuDanExpr클래스를 만든다.GuGuDanExpr에는 아래 생성자 3개가 필요하다.
GuGuDanExpr()GuGuDanExpr(int dan)GuGuDanExpr(int dan, int number)GuGuDanExpr에는public static void printAll()메서드가 필요하다.GuGuDanApp에서 난수 2개를 뽑아 각각dan,number에 저장한다.- 난수는
day4패키지의MethodLab4.getRandom()메서드를 사용해서 구한다.dan,number값에 따라 아래 3가지 기능 중 하나를 수행해야 한다.
- 둘 다
1~9면 해당 곱셈 하나만 출력dan만1~9이고number가10 이상이면 해당 단 전체 출력dan이10 이상이면1단부터 9단전체 출력
문제 해석이 문제는 단순히 자식 클래스 하나 만드는 문제로 끝나지 않는다.
먼저GuGuDanExpr의 틀을 문제 요구사항에 맞게 만든 뒤, 부모 클래스인Multiplication의 생성자를 보고 자식 생성자들을 연결해야 한다.
그다음에는 문제를 기능 기준으로 다시 정리해야 한다.
처음에는 조건이 여러 개라 복잡해 보이지만, 실제로는 출력 기능이 3개뿐이다.
그리고GuGuDanApp에서는 난수 2개를 그냥 직접 만드는 것이 아니라, 문제에서 지정한MethodLab4.getRandom()으로 받아와야 한다.
즉 이 실습의 핵심 흐름은 아래처럼 볼 수 있다.
GuGuDanExpr 틀 만들기 → 부모 생성자와 연결 → 기능 3개로 정리 → 조건별 기능 구현 → GuGuDanApp에서 MethodLab4.getRandom()으로 값을 받아 전체 흐름 연결
풀이 순서
GuGuDanExpr클래스의 요구조건 틀 먼저 만들기- 부모 클래스
Multiplication을 보고 자식 생성자 완성하기- 문제를 출력 기능 3개로 정리하기
- 첫 번째 기능 구현하기:
dan,number가 모두1~9일 때 한 문제만 출력- 두 번째 기능 구현하기:
dan은1~9,number는10 이상일 때 해당 단 전체 출력- 세 번째 기능 구현하기:
dan이10 이상일 때printAll()로 전체 구구단 출력MethodLab4.getRandom()을 반영해 전체 조건 분기 완성하기- 최종 코드 정리하기
- 실습하면서 확인한 점 정리하기
자세한 풀이1. GuGuDanExpr 클래스의 요구조건 틀 먼저 만들기처음에는 값을 바로 채우기보다, 문제에서 요구한 형태부터 먼저 맞췄다.
즉GuGuDanExpr가Multiplication을 상속하고, 생성자 3개와printAll()메서드 자리를 먼저 만들어 두는 단계다.
이 단계에서는 아직 기능이 없어도 괜찮다.
중요한 건 문제에서 요구한 클래스 구조를 먼저 맞추는 것이다.
그리고printAll()은 나중에 6단계에서 다시 설명하겠지만, 문제에서static메서드로 제시되어 있었기 때문에 틀을 만들 때부터static으로 맞춰 두었다.package practiceDay3_24_2; public class GuGuDanExpr extends Multiplication { GuGuDanExpr() { } GuGuDanExpr(int dan){ } GuGuDanExpr(int dan, int number){ } public static void printAll() { } }여기서는 아직 데이터 흐름을 볼 필요가 없다.
일단 문제에서 요구한 생성자와 메서드 자리를 먼저 만들어 두고, 그다음 단계에서 부모 클래스와 연결하면 된다.
2. 부모 클래스 Multiplication을 보고 자식 생성자 완성하기이제 부모 클래스
Multiplication을 보면, 이미dan,number를 초기화하는 생성자들이 준비되어 있다.
그래서 자식 클래스GuGuDanExpr는 값을 직접 저장하는 것이 아니라, 부모 생성자를 호출해서 초기화하면 된다.package practiceDay3_24_2; class Multiplication { private int dan; private int number; Multiplication() {} Multiplication(int dan) { this.dan = dan; } Multiplication(int dan, int number) { this.dan = dan; this.number = number; } void printPart() { if (number == 0) { for (int n = 1; n <= 9; n++) System.out.print("\t" + dan + "*" + n + "=" + dan * n); System.out.println(); } else { // Multiplication의 printPart()에서 number가 0이 아니면 계산 결과값만 출력하는 부분 System.out.println(dan * number); } } }부모 클래스의 멤버변수
dan,number는private이라서 자식 클래스에서 직접 접근할 수 없다.
그래서 자식 생성자 안에서는super()를 사용해서 부모 생성자를 호출해야 한다.package practiceDay3_24_2; public class GuGuDanExpr extends Multiplication { GuGuDanExpr() { super(); } GuGuDanExpr(int dan) { super(dan); } GuGuDanExpr(int dan, int number) { super(dan, number); } }
데이터 흐름 예상public class GuGuDanApp { public static void main(String[] args) { GuGuDanExpr a = new GuGuDanExpr(); // 1. 매개변수 없는 객체 생성 GuGuDanExpr b = new GuGuDanExpr(2); // 2. dan 값 하나를 전달하며 객체 생성 GuGuDanExpr c = new GuGuDanExpr(3, 4); // 3. dan, number 값을 함께 전달하며 객체 생성 } }
public class GuGuDanExpr extends Multiplication { GuGuDanExpr() { super(); // 4. 부모의 기본 생성자 호출 } GuGuDanExpr(int dan) { super(dan); // 5. 전달받은 dan 값을 부모 생성자로 넘김 } GuGuDanExpr(int dan, int number) { super(dan, number); // 6. 전달받은 dan, number 값을 부모 생성자로 넘김 } }
class Multiplication { private int dan; // 9. 최종적으로 값이 저장되는 부모 멤버변수 private int number; // 10. 최종적으로 값이 저장되는 부모 멤버변수 Multiplication() { // 7. 매개변수가 없으면 기본 생성자 실행 } Multiplication(int dan) { // 8. 자식이 넘긴 dan 값을 받음 this.dan = dan; // 11. 부모 멤버변수 dan에 저장 } Multiplication(int dan, int number) { this.dan = dan; // 12. 부모 멤버변수 dan에 저장 this.number = number; // 13. 부모 멤버변수 number에 저장 } }→
new GuGuDanExpr()가 실행되면 자식의 기본 생성자가 호출되고, 다시super()를 통해 부모의 기본 생성자가 실행된다.
→new GuGuDanExpr(2)가 실행되면2가 자식 생성자의dan으로 들어오고,super(dan)을 통해 부모 생성자로 전달되어 부모 멤버변수dan에 저장된다.
→new GuGuDanExpr(3, 4)가 실행되면3,4가 자식 생성자로 들어오고,super(dan, number)를 통해 부모 생성자로 전달되어 부모 멤버변수dan,number에 저장된다.
→ 즉GuGuDanExpr는 값을 직접 저장하는 클래스라기보다, 부모Multiplication이 가진 초기화 기능을 그대로 이어받아 사용하는 구조다.
3. 문제를 출력 기능 3개로 정리하기처음에는 조건이 여러 개라서 기능도 여러 개인 것처럼 보였다.
특히dan이10 이상이면 전체 구구단 출력,printAll()로 전체 구구단 출력 이 두 개를 따로 보게 되기 쉽다.
그런데 실제로는printAll()이 바로 전체 구구단을 출력하는 기능이고,
dan >= 10이라는 조건은 그 기능을 호출하는 조건일 뿐이다.
그래서 이 문제의 실제 출력 기능은 아래 3개로 정리된다.
1.dan,number가 모두1~9이면 곱셈 하나만 출력
2.dan은1~9,number는10 이상이면 해당 단 전체 출력
3.dan이10 이상이면printAll()을 호출해서1단~9단전체 출력즉 문제를 풀 때는 조건을 그대로 외우기보다,
먼저 기능을 3개로 정리하고 그다음 각 기능에 조건을 붙이는 방식으로 보는 게 훨씬 편했다.
4. 첫 번째 기능 구현하기: dan, number가 모두 1~9일 때 한 문제만 출력첫 번째로 구현한 기능은 가장 단순한 경우다.
dan과number가 모두1~9이면, 그 두 값의 곱셈 결과 하나만 출력하면 된다.
이때는dan,number두 값이 모두 필요하므로GuGuDanExpr(dan, number)생성자를 사용해서 객체를 만들었다.
그리고 부모 클래스의printPart()를 호출해 결과를 출력했다.package practiceDay3_24_2; import day4.MethodLab4; public class GuGuDanApp { public static void main(String[] args) { int dan = MethodLab4.getRandom(1, 20); int number = MethodLab4.getRandom(1, 20); GuGuDanExpr guguDan1 = new GuGuDanExpr(dan, number); if (dan <= 9 && number <= 9) { System.out.print(dan + " * " + number + " = "); guguDan1.printPart(); } } }여기서 한 번 확인한 점은, 부모 클래스
printPart()가number가 있는 경우에는 계산 결과만 출력한다는 점이었다.// Multiplication의 printPart()에서 number가 0이 아니면 계산 결과값만 출력하는 부분 else { System.out.println(dan * number); }그래서
3 * 4 = 12처럼 전체 식을 출력하려면,
앞부분3 * 4 =은GuGuDanApp에서 먼저 출력하고, 마지막 결과12는printPart()가 출력하도록 연결했다.
데이터 흐름public class GuGuDanApp { public static void main(String[] args) { int dan = MethodLab4.getRandom(1, 20); // 1. getRandom()으로 1~20 사이 난수를 dan에 저장 int number = MethodLab4.getRandom(1, 20); // 2. getRandom()으로 1~20 사이 난수를 number에 저장 GuGuDanExpr guguDan1 = new GuGuDanExpr(dan, number); // 3. dan, number를 넘겨 객체 생성 if (dan <= 9 && number <= 9) { // 4. 둘 다 1~9인지 검사 System.out.print(dan + " * " + number + " = "); // 5. 식 앞부분 먼저 출력 guguDan1.printPart(); // 6. 부모의 printPart() 호출 } } }
public class GuGuDanExpr extends Multiplication { GuGuDanExpr(int dan, int number) { super(dan, number); // 7. dan, number 값을 부모로 전달 } }
class Multiplication { private int dan; private int number; Multiplication(int dan, int number) { this.dan = dan; // 8. dan 값 저장 this.number = number; // 9. number 값 저장 } void printPart() { if (number == 0) { for (int n = 1; n <= 9; n++) System.out.print("\t" + dan + "*" + n + "=" + dan * n); System.out.println(); } else { System.out.println(dan * number); // 10. dan * number 결과 출력 } } }→
main()에서MethodLab4.getRandom()으로 난수 2개를 받아dan,number에 저장한다.
→new GuGuDanExpr(dan, number)가 실행되면 자식 생성자가 호출되고,super(dan, number)로 부모 생성자에 값이 전달된다.
→ 부모 생성자에서 전달받은 값을 부모 멤버변수dan,number에 저장한다.
→if (dan <= 9 && number <= 9)조건이 참이면 식 앞부분dan * number =를 먼저 출력한다.
→ 이어서printPart()를 호출하면 부모 클래스 안에서dan * number계산 결과가 출력된다.
→ 그래서 최종적으로 한 문제짜리 곱셈 결과가 완성된다.
5. 두 번째 기능 구현하기: dan은 1~9, number는 10 이상일 때 해당 단 전체 출력두 번째 기능은
dan만 사용해서 한 단 전체를 출력하는 경우다.
이때는number값이 필요 없기 때문에GuGuDanExpr(dan)생성자를 사용했다.
부모 클래스Multiplication을 보면dan하나만 받는 생성자가 있다.
이 생성자를 호출하면 부모의dan만 초기화되고,number는 기본값0상태로 남는다.
그리고printPart()를 보면number == 0일 때는for문으로 해당 단 전체를 출력하도록 되어 있다.
즉 이 기능은dan만 넘겨서 객체를 만들고,printPart()를 호출하면 자동으로 동작하도록 설계되어 있다.package practiceDay3_24_2; import day4.MethodLab4; public class GuGuDanApp { public static void main(String[] args) { int dan = MethodLab4.getRandom(1, 20); int number = MethodLab4.getRandom(1, 20); GuGuDanExpr guguDan2 = new GuGuDanExpr(dan); if (dan <= 9 && number >= 10) { System.out.print(dan + "단"); guguDan2.printPart(); } } }
데이터 흐름public class GuGuDanApp { public static void main(String[] args) { int dan = MethodLab4.getRandom(1, 20); // 1. dan 값 생성 int number = MethodLab4.getRandom(1, 20); // 2. number 값 생성 GuGuDanExpr guguDan2 = new GuGuDanExpr(dan); // 3. dan 값만 넘겨 객체 생성 if (dan <= 9 && number >= 10) { // 4. 두 번째 기능 조건 검사 System.out.print(dan + "단"); guguDan2.printPart(); // 5. printPart() 호출 } } }
public class GuGuDanExpr extends Multiplication { GuGuDanExpr(int dan) { super(dan); // 6. dan 값만 부모로 전달 } }
class Multiplication { private int dan; private int number; Multiplication(int dan) { this.dan = dan; // 7. dan 저장 } void printPart() { if (number == 0) { // 8. number는 따로 저장된 적이 없어서 기본값 0 for (int n = 1; n <= 9; n++) // 9. 1부터 9까지 반복 System.out.print("\t" + dan + "*" + n + "=" + dan * n); // 10. 한 단 전체 출력 System.out.println(); } else { System.out.println(dan * number); } } }→
main()에서dan,number를 받아온다.
→new GuGuDanExpr(dan)이 실행되면 자식 생성자가 호출되고,super(dan)으로 부모 생성자에dan값만 전달된다.
→ 부모 생성자는dan만 저장하고,number는 따로 값이 들어오지 않으므로 기본값0상태로 남는다.
→if (dan <= 9 && number >= 10)조건이 참이면printPart()를 호출한다.
→printPart()안에서는number == 0조건이 참이 되어for문이 실행된다.
→ 그 결과dan * 1부터dan * 9까지 한 단 전체가 출력된다.
6. 세 번째 기능 구현하기: dan이 10 이상일 때 printAll()로 전체 구구단 출력세 번째 기능은
dan이10 이상일 때 전체 구구단을 출력하는 경우다.
이 기능은 객체 하나의 상태를 출력하는 것이 아니라,1단부터 9단까지전체를 한 번에 출력하는 기능이다.
그래서 이때 사용되는 메서드가printAll()이다.
처음 틀을 만들 때부터static으로 맞춰 두었고, 여기서 그 이유가 분명해진다.
문제의 클래스 다이어그램을 다시 보면printAll()에 밑줄이 있었는데, 이건static메서드라는 뜻이다.
즉 이 메서드는 객체를 만들어 호출하는 것이 아니라, 클래스명으로 바로 호출해야 한다.
그래서GuGuDanExpr.printAll();형태로 사용한다.public class GuGuDanExpr extends Multiplication { GuGuDanExpr() { super(); } GuGuDanExpr(int dan) { super(dan); } GuGuDanExpr(int dan, int number) { super(dan, number); } public static void printAll() { for (int dan = 1; dan <= 9; dan++) { GuGuDanExpr guguDan = new GuGuDanExpr(dan); guguDan.printPart(); } } }
printAll()안에서는1부터9까지 반복하면서,
각 반복마다dan하나만 받는GuGuDanExpr객체를 만들고printPart()를 호출했다.
이렇게 하면 앞 단계에서 구현한 두 번째 기능을 그대로 재사용해서1단부터9단까지 모두 출력할 수 있다.
그리고GuGuDanApp에서는 아래처럼 연결했다.package practiceDay3_24_2; import day4.MethodLab4; public class GuGuDanApp { public static void main(String[] args) { int dan = MethodLab4.getRandom(1, 20); int number = MethodLab4.getRandom(1, 20); GuGuDanExpr guguDan1 = new GuGuDanExpr(dan, number); GuGuDanExpr guguDan2 = new GuGuDanExpr(dan); if (dan <= 9 && number <= 9) { System.out.print(dan + " * " + number + " = "); guguDan1.printPart(); } else if (dan <= 9 && number >= 10) { guguDan2.printPart(); } else if (dan >= 10) { GuGuDanExpr.printAll(); } } }
데이터 흐름public class GuGuDanApp { public static void main(String[] args) { int dan = MethodLab4.getRandom(1, 20); // 1. dan 값 생성 int number = MethodLab4.getRandom(1, 20); // 2. number 값 생성 if (dan >= 10) { // 3. 세 번째 기능 조건 검사 GuGuDanExpr.printAll(); // 4. 클래스명으로 static 메서드 호출 } } }
public class GuGuDanExpr extends Multiplication { public static void printAll() { for (int dan = 1; dan <= 9; dan++) { // 5. 1단부터 9단까지 반복 GuGuDanExpr guguDan = new GuGuDanExpr(dan); // 6. 현재 dan 값으로 객체 생성 guguDan.printPart(); // 7. 한 단 전체 출력 메서드 호출 } } }
public class GuGuDanExpr extends Multiplication { GuGuDanExpr(int dan) { super(dan); // 8. 현재 dan 값을 부모로 전달 } }
class Multiplication { private int dan; private int number; Multiplication(int dan) { this.dan = dan; // 9. 부모 멤버변수 dan 저장 } void printPart() { if (number == 0) { // 10. number는 기본값 0 for (int n = 1; n <= 9; n++) // 11. 현재 dan의 1~9 곱셈 출력 System.out.print("\t" + dan + "*" + n + "=" + dan * n); System.out.println(); } } }→
main()에서MethodLab4.getRandom()으로 난수 2개를 만든다.
→if (dan >= 10)조건이 참이면GuGuDanExpr.printAll()이 호출된다.
→printAll()은static메서드이므로 객체 없이 클래스명으로 바로 호출된다.
→printAll()안에서는for문이1부터9까지 반복된다.
→ 반복이 한 번 돌 때마다 현재dan값으로new GuGuDanExpr(dan)객체를 만든다.
→ 자식 생성자는super(dan)을 호출해서 부모의dan을 초기화한다.
→ 이어서printPart()를 호출하면number == 0분기가 실행되어 해당 단 전체가 출력된다.
→ 이 과정이1단부터9단까지 반복되면서 전체 구구단이 완성된다.
7. MethodLab4.getRandom()을 반영해 전체 조건 분기 완성하기마지막에는 문제 조건대로
MethodLab4.getRandom()을 사용해서dan,number값을 받아 전체 흐름을 연결했다.
처음에는 난수를 직접 만드는 방식으로 생각할 수 있지만, 이 문제는 이미 만들어 둔 메서드를 활용하라는 조건이 있었기 때문에 그 방식에 맞춰 구현했다.
그리고 최종 출력 형식은 제공된Multiplication의printPart()출력 방식에 맞춰 연결했다.
즉 한 문제 출력일 때는GuGuDanApp이 식 앞부분을 출력하고, 한 단 전체 출력과 전체 구구단 출력은printPart()의 반복 출력 형식을 그대로 사용했다.
아래 코드는 실습하면서 확인용 출력까지 포함한 정리본이다.
제출할 때는 필요하면 난수 확인용 출력문은 제거하면 된다.package practiceDay3_24_2; import day4.MethodLab4; public class GuGuDanApp { public static void main(String[] args) { int dan = MethodLab4.getRandom(1, 20); int number = MethodLab4.getRandom(1, 20); System.out.println(dan); System.out.println(number); GuGuDanExpr guguDan1 = new GuGuDanExpr(dan, number); GuGuDanExpr guguDan2 = new GuGuDanExpr(dan); if (dan <= 9 && number <= 9) { System.out.print(dan + " * " + number + " = "); guguDan1.printPart(); } else if (dan <= 9 && number >= 10) { guguDan2.printPart(); } else if (dan >= 10) { GuGuDanExpr.printAll(); } } }
데이터 흐름public class GuGuDanApp { public static void main(String[] args) { int dan = MethodLab4.getRandom(1, 20); // 1. getRandom()으로 dan 생성 int number = MethodLab4.getRandom(1, 20); // 2. getRandom()으로 number 생성 System.out.println(dan); // 3. 확인용으로 dan 출력 System.out.println(number); // 4. 확인용으로 number 출력 GuGuDanExpr guguDan1 = new GuGuDanExpr(dan, number); // 5. 한 문제 출력용 객체 생성 GuGuDanExpr guguDan2 = new GuGuDanExpr(dan); // 6. 한 단 전체 출력용 객체 생성 if (dan <= 9 && number <= 9) { // 7. 첫 번째 기능 조건 System.out.print(dan + " * " + number + " = "); // 8. 식 앞부분 출력 guguDan1.printPart(); // 9. 결과값 출력 } else if (dan <= 9 && number >= 10) { // 10. 두 번째 기능 조건 guguDan2.printPart(); // 11. 해당 단 전체 출력 } else if (dan >= 10) { // 12. 세 번째 기능 조건 GuGuDanExpr.printAll(); // 13. 전체 구구단 출력 } } }→
MethodLab4.getRandom()이 호출되어dan,number값이 만들어진다.
→ 만들어진 값은 먼저 확인용으로 출력된다.
→ 같은dan,number값을 바탕으로 한 문제 출력용 객체와 한 단 전체 출력용 객체를 각각 만든다.
→if문이 조건을 검사해서 현재 난수 상황에 맞는 기능 하나를 선택한다.
→ 첫 번째 기능이면 식 앞부분 출력 후printPart()가 결과값을 출력한다.
→ 두 번째 기능이면printPart()가 해당 단 전체를 출력한다.
→ 세 번째 기능이면printAll()이 호출되어 전체 구구단이 출력된다.
최종 코드Multiplication.javapackage practiceDay3_24_2;
class Multiplication {
private int dan;
private int number;
Multiplication() {}
Multiplication(int dan) {
this.dan = dan;
}
Multiplication(int dan, int number) {
this.dan = dan;
this.number = number;
}
void printPart() {
if (number == 0) {
for (int n = 1; n <= 9; n++)
System.out.print("\t" + dan + "*" + n + "=" + dan * n);
System.out.println();
} else {
// Multiplication의 printPart()에서 number가 0이 아니면 계산 결과값만 출력하는 부분
System.out.println(dan * number);
}
}
}
GuGuDanExpr.javapackage practiceDay3_24_2;
public class GuGuDanExpr extends Multiplication {
GuGuDanExpr() {
super();
}
GuGuDanExpr(int dan) {
super(dan);
}
GuGuDanExpr(int dan, int number) {
super(dan, number);
}
public static void printAll() {
for (int dan = 1; dan <= 9; dan++) {
GuGuDanExpr guguDan = new GuGuDanExpr(dan);
guguDan.printPart();
}
}
}
GuGuDanApp.javapackage practiceDay3_24_2;
import day4.MethodLab4;
public class GuGuDanApp {
public static void main(String[] args) {
int dan = MethodLab4.getRandom(1, 20);
int number = MethodLab4.getRandom(1, 20);
GuGuDanExpr guguDan1 = new GuGuDanExpr(dan, number);
GuGuDanExpr guguDan2 = new GuGuDanExpr(dan);
if (dan <= 9 && number <= 9) {
System.out.print(dan + " * " + number + " = ");
guguDan1.printPart();
} else if (dan <= 9 && number >= 10) {
guguDan2.printPart();
} else if (dan >= 10) {
GuGuDanExpr.printAll();
}
}
}
실습하면서 확인한 점
- 자식 클래스는 부모 클래스의 생성자를
super()로 호출해서 초기화할 수 있다.- 부모 멤버변수가
private이면 자식 클래스에서 직접 접근할 수 없고, 부모 생성자나 부모 메서드를 통해 사용해야 한다.- 조건이 여러 개여도 실제 기능으로 다시 정리하면 더 쉽게 보인다.
dan >= 10이면 전체 구구단 출력과printAll()은 서로 다른 기능이 아니라, 조건과 호출 메서드 관계다.- 클래스 다이어그램에서 메서드 이름에 밑줄이 있으면
static메서드라는 뜻이다.static메서드는 객체로 호출하는 게 아니라 클래스명으로 직접 호출한다.- 문제에서 이미 제공한 메서드가 있으면 직접 새로 만들기보다 그 메서드를 활용하는 방식으로 구현해야 한다.
- 이미 만들어 둔 기능을 다시 활용하면, 전체 구구단 출력도 한 단 출력 기능을 반복해서 쉽게 만들 수 있다.
이제 이 버전이면 점검에서 나온 부분까지 다 반영된 최종본으로 봐도 된다.