자바 프로그래밍 입문
8주
신림
입문
x 1
### 소개
> "**책**은 몇 번 읽었는데.. 막상 **코딩**을 하려면 뭐 부터 해야할지 모르겠어요.."
> "**객체지향 프로그래밍**이라는게 잘 이해가 잘 안됩니다.."
> "자바를 배우면 **무얼 만들 수 있죠?**"
### 진행방향
+ 문제를 주어주고, 이를 해결하기 위한 개념을 토의하며, 이를 바탕으로 실제 프로그램을 만듭니다.
### 목표
+ 자바 기본문법을 숙지
+ 객체지향 프로그래밍의 이해
+ 객체지향적 프로그램 설계능력 향상
+ 프로그램 구현 능력 증진
### 교재
+ [**MIT Introduction to Programming in Java**](https://goo.gl/Kl5I19)
- Types, variables, operators
- More types, methods, conditionals
- Loops and arrays
- Objects and classes
- Access control, class scope, packages, Java API
- Design, debugging, interfaces
- Inheritance, exceptions, file I/O
### 시간 및 장소
+ 매주 월요일 7시 - 9시 / 총 8주 과정
+ 신림역 통스토리

### 인원
+ 2~4 명
### 비용
+ 10 만원(4주 기준, 총 8주)
### 연락처
+ 이메일: [email protected]
+ 카 톡: chaesam
# 자기소개하기
### 상황설정
이제 막 대학교 새내기각 된 박컴돌(20) 학생. 난생 처음 자바라는 것을 배우고, 첫 과제를 부여 받게 되는데..
### 요구사항
아래와 같이 동작하는 자기소개를 하는 프로그램을 작성하시오.

### 예시코드
```java
import java.util.Scanner;
public class SelfIntroduction {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.printf("이름을 입력하세요: ");
String name = scanner.next();
System.out.println();
System.out.println(name + " 학생~ 프로그래밍 입문을 축하합니다~");
System.out.println("웰컴 투 헬~ (\\ .. /)/");
}
}
```
# 달러 환전하기
### 상황설정
우연찮게 도박 사이트에 접속하게 된 박컴돌(21) 학생. 주사위 도박 게임에 빠지고 마는데..
> 게임 룰:
> 주사위를 던져 큰 수가 나온 사람이 달러를 획득한다.
> 획득 달러량은 주사위의 수와 같다.
### 요구사항
+ 1에서 6사이의 숫자를 반환한 뒤, 그 숫자에 달러 환율을 곱하여 결과값을 출력하시오.
+ 환율은 1달러 = 1116.07143원으로 한다.(2016.10.10 기준)

### 예시코드
```java
public class DiceGambling {
public static void main(String[] args) {
int n = 1 + (int)(Math.random() * 1000) % 6;
double dallarRate = 1116.07143;
double krw = n * dallarRate;
System.out.println(n + " 달라 = " + krw + " 원");
}
}
```
# 비만도 검사하기
### 상황설정
'학교-집-학교-집'을 반복하며 밤낮없이 전공 공부와 과제를 작성해오던 박컴돌(21) 학생. 항상 컴퓨터 앞에서 떠날 줄 모르고.. 그렇게 몸매는 망가져만 가는데..
### 요구사항
키와 몸무게를 입력받아 비만도를 측정해주는 프로그램을 작성하시오. 비만도는 [BMI지수](https://goo.gl/KFn4wX)를 사용하여 네 가지 단계(저체중, 정상, 과체중, 비만)로 구분 할 것.

### 예시코드
```java
import java.util.Scanner;
public class BMICalculator {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("키와 몸무게를 입력하시오. [예] 182.4 75.5");
double tall = scanner.nextDouble() / 100;
double weight = scanner.nextDouble();
double bmi = weight / (tall * tall);
if (bmi < 18.5)
System.out.println("저체중 입니다.");
else if (bmi < 23)
System.out.println("정상 입니다.");
else if (bmi < 25)
System.out.println("과체중 입니다.");
else
System.out.println("비만 입니다.");
}
}
```
# 펀치머신
### 상황설정
이태원에 놀러간 박컴돌(21) 학생. 친구들과 함꼐 맛나는 음식과 시원한 맥주를 마시고 돌아오는 길에서 펀치 머신과 마주하게 된다..
### 요구사항
아래의 기록을 참고하여 가장 고득점자를 찾는 프로그램을 만드시오.

|이름|점수|
|:--|:--|
|Elena|590|
|Thomas|252|
|Hamilton|562|
|Suzie|251|
|Phil|519|
|Matt|408|
|Alex|870|
|Emma|646|
|John|185|
|James|620|
|Emily|577|
|Daniel|633|
|Neda|937|
|Aaron|380|
|Kate|285|
### 예시코드
```java
public class Punch {
public static void main(String[] args) {
String[] names = { "Elena", "Thomas", "Hamilton", "Suzie", "Phil",
"Matt", "Alex", "Emma", "John", "James",
"Emily","Daniel", "Neda", "Aaron", "Kate" };
int[] scores = {590, 252, 562, 251, 519,
408, 870, 646, 185, 620,
577, 633, 937, 380, 285};
int highScoreIdx = 0;
for (int i = 0; i < scores.length; i++)
if (scores[highScoreIdx] < scores[i])
highScoreIdx = i;
System.out.println("최고득점자는 " + names[highScoreIdx] + " 입니다.");
}
}
```
# 대전 게임 만들기 I
### 상황설정
평소 알피지 게임을 좋아하던 박컴돌(21) 학생. 어느날 무자비한 PK를 당하고 만다. 아무리 싸워도 이길 수 없음을 깨닫고, 결국 게임을 접고야 만다.
> "아오 빡쳐.. 내가 게임을 만들면, 아무도 날 못이기겠지..?"
### 요구사항
아래와 같이 동작하는 두 영웅의 대전 게임을 구현하시오.

+ 영웅은 다음과 같은 속성을 갖는다.
- 이름
- 체력 (모든 영웅의 기본체력은 100으로 동일)
+ 영웅은 펀치를 통해 상대 영웅에게 피해를 입힐 수 있다.
- 펀치는 랜덤하게 발동된다.
+ 게임은 두 영웅의 생성과 동시에 시작되며 하나가 죽을 때까지 진행된다.
### 설계

### 구현
```java
import java.util.Random;
public class Hero {
private String name;
private int hp = 100;
// 생성자
public Hero(String name) {
this.name = name;
}
// 펀치
public void punch(Hero enermy) {
enermy.hp -= 10;
System.out.printf("[%s]의 펀치\n", this.name);
System.out.printf("\t%s: %d/100\n", enermy.name, enermy.hp);
}
// 테스트 코드
public static void main(String[] args) throws InterruptedException {
Hero arthas = new Hero("아서스");
Hero leona = new Hero("레오나");
Random random = new Random();
while (arthas.hp > 0 && leona.hp > 0) {
Boolean isArthasTurn = random.nextBoolean();
Hero attacker = isArthasTurn ? arthas : leona;
Hero defender = isArthasTurn ? leona : arthas;
attacker.punch(defender);
Thread.sleep(1000);
}
}
}
```
### 실행결과
<div class="embed-responsive embed-responsive-4by3">
<iframe class="embed-responsive-item" src="https://www.youtube.com/embed/Ojq_T378_mE"></iframe>
</div>
# 그래픽 API: 움직이는 박스들
### 학습목표
+ Access Control
+ Class Scope
+ Packages
+ Java API
### 과제5: Graphics I
우리는 객체를 만드는 방법에 대하여 배웠습니다. 객체사용의 장점은 다른사람들이 만든 코드를 쉽게 사용할 수 있다는 점입니다. (물론 다른 여러 장점들이 더 있습니다) 이번 과제에서는 Java API가 기본으로 제공하는 Graphics와 Containers를 사용함으로써 이를 경험해보도록 하겠습니다.
### 요구사항
1. 초기 화면에 3개의 다른 도형이 그려지도록 구현하시오.
2. BouncingBox 인스턴스를 사용하여 각각의 도형이 다른 방향으로 움직이도록 할 것.
(ArrayList를 사용하여 도형들을 관리 할 것.)
### 초기설정
1. 이클립스로부터 프로젝트를 생성합니다.
2. 다음 3개의 클래스를 생성하고 주어진 소스코드를 복사하여 붙여넣습니다.
- SimpleDraw
- BouncingBox
- DrawGraphics
3. 프로그램을 실행해 봅니다. 정상실행이 된 경우 아래의 화면이 나오게 됩니다.

+ 실행이 안되는 경우, **`Simple`**클래스를 선택 후 실행해보세요~
### 스켈레톤 코드
+ SimpleDraw.java
```java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JPanel;
/** Displays a window and delegates drawing to DrawGraphics. */
public class SimpleDraw extends JPanel implements Runnable {
private static final long serialVersionUID = -7469734580960165754L;
private boolean animate = true;
private final int FRAME_DELAY = 50; // 50 ms = 20 FPS
public static final int WIDTH = 300;
public static final int HEIGHT = 300;
private DrawGraphics draw;
public SimpleDraw(DrawGraphics drawer) {
this.draw = drawer;
}
/** Paint callback from Swing. Draw graphics using g. */
public void paintComponent(Graphics g) {
super.paintComponent(g);
// Enable anti-aliasing for better looking graphics
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
draw.draw(g2);
}
/** Enables periodic repaint calls. */
public synchronized void start() {
animate = true;
}
/** Pauses animation. */
public synchronized void stop() {
animate = false;
}
private synchronized boolean animationEnabled() {
return animate;
}
public void run() {
while (true) {
if (animationEnabled()) {
repaint();
}
try {
Thread.sleep(FRAME_DELAY);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(String args[]) {
final SimpleDraw content = new SimpleDraw(new DrawGraphics());
JFrame frame = new JFrame("Graphics!");
Color bgColor = Color.white;
frame.setBackground(bgColor);
content.setBackground(bgColor);
// content.setSize(WIDTH, HEIGHT);
// content.setMinimumSize(new Dimension(WIDTH, HEIGHT));
content.setPreferredSize(new Dimension(WIDTH, HEIGHT));
// frame.setSize(WIDTH, HEIGHT);
frame.setContentPane(content);
frame.setResizable(false);
frame.pack();
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { System.exit(0); }
public void windowDeiconified(WindowEvent e) { content.start(); }
public void windowIconified(WindowEvent e) { content.stop(); }
});
new Thread(content).start();
frame.setVisible(true);
}
}
```
+ BouncingBox.java
```java
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class BouncingBox {
int x;
int y;
Color color;
int xDirection = 0;
int yDirection = 0;
final int SIZE = 20;
/**
* Initialize a new box with its center located at (startX, startY),
* filled with startColor.
*/
public BouncingBox(int startX, int startY, Color startColor) {
x = startX;
y = startY;
color = startColor;
}
/** Draws the box at its current position on to surface. */
public void draw(Graphics surface) {
// Draw the object
surface.setColor(color);
surface.fillRect(x - SIZE/2, y - SIZE/2, SIZE, SIZE);
surface.setColor(Color.BLACK);
((Graphics2D) surface).setStroke(new BasicStroke(3.0f));
surface.drawRect(x - SIZE/2, y - SIZE/2, SIZE, SIZE);
// Move the center of the object each time we draw it
x += xDirection;
y += yDirection;
// If we have hit the edge and are moving in the wrong direction, reverse direction
// We check the direction because if a box is placed near the wall, we would get "stuck"
// rather than moving in the right direction
if ((x - SIZE/2 <= 0 && xDirection < 0) ||
(x + SIZE/2 >= SimpleDraw.WIDTH && xDirection > 0)) {
xDirection = -xDirection;
}
if ((y - SIZE/2 <= 0 && yDirection < 0) ||
(y + SIZE/2 >= SimpleDraw.HEIGHT && yDirection > 0)) {
yDirection = -yDirection;
}
}
public void setMovementVector(int xIncrement, int yIncrement) {
xDirection = xIncrement;
yDirection = yIncrement;
}
}
```
+ DrawGraphics.java
```java
import java.awt.Color;
import java.awt.Graphics;
public class DrawGraphics {
BouncingBox box;
/** Initializes this class for drawing. */
public DrawGraphics() {
box = new BouncingBox(200, 50, Color.RED);
}
/** Draw the contents of the window on surface. Called 20 times per second. */
public void draw(Graphics surface) {
surface.drawLine(50, 50, 250, 250);
box.draw(surface);
}
}
```
### 문제해결
#### Part One: Drawing Graphics
먼저 `DrawGraphics` 클래스를 열어줍니다. 초기 실행시 보이는 정사각형과 직선이 보이는데 이를 그려주는 메쏘드가 `draw` 입니다. **요구사항1**에서 3개의 도형을 그려달라 했으므로 우리는 하나의 다른 도형만 추가해 주면 되겠습니다~ (초기 2개의 도형을 지우고 다른 도형으로 바꾸셔도 됩니다~ Be Creative!)
Java API에서 [java.awt.Graphics](http://docs.oracle.com/javase/8/docs/api/)를 참고하시면 직사각형, 직선, 타원, 등등 기타 여러가지 도형을 그릴 수 있는데, 저는 타원을 그려보겠습니다.
+ 타원 추가하기
- Java API에서 타원을 그리는 함수를 찾아보았습니다~

- 클릭을 해보면 친절한(?) 영어설명이 되어있어요 -_-;;

- 설명을 따라 타원을 그려보았습니다.
```java
public void draw(Graphics surface) {
surface.drawLine(50, 50, 250, 250);
box.draw(surface);
surface.drawOval(100, 100, 100, 200);
}
```

#### Part Two: Containers and Animation
우리가 만든 `DrawGraphics`클래스와 `BouncingBox`클래스는 애니메이션 효과를 지원합니다. `DrawGraphics`는 초당 20번 `draw`메쏘드를 호출함으로써 화면을 지속적으로 새로이 보여줍니다. `BouncingBox`에서는 박스의 움직임에 대한 설정을 `setMovementVector`메쏘드를 통해 제공합니다. 예를들면 `setMovementVector(1,0)`은 오른쪽방향으로 박스가 천천히 움직이고, `setMovementVector(0,-2)`는 위쪽으로 빠르게 움직입니다.
그럼 이제 **요구사항2**를 만족하기 위해 `DrawGraphics`클래스를 변경해봅시다. (`setMovementVector()`메쏘드는 DrawGraphics의 생성자에서 한번만 설정해 주면 됩니다)
+ BouncingBox 생성
- ArrayList를 만들고, 3개의 BouncingBox 인스턴스를 추가해주었습니다.
```java
public class DrawGraphics {
ArrayList<BouncingBox> boxes;
public DrawGraphics() {
boxes = new ArrayList<BouncingBox>();
// BouncingBox 생성자의 전달인자는 박스의 x, y 좌표와 색상입니다.
BouncingBox boxA = new BouncingBox(200, 50, Color.RED);
BouncingBox boxB = new BouncingBox(150, 50, Color.GREEN);
BouncingBox boxC = new BouncingBox(100, 50, Color.BLUE);
boxes.add(boxA);
boxes.add(boxB);
boxes.add(boxC);
}
...
}
```
- 다음으로 `draw`메쏘드를 통 박스를 그려주도록 하겠습니다~
```java
public class DrawGraphics {
...
public void draw(Graphics surface) {
surface.drawLine(50, 50, 250, 250);
surface.drawOval(100, 100, 100, 150);
for(BouncingBox box : boxes) {
box.draw(surface);
}
}
}
```
- 실행결과

+ BouncingBox 움직이기
- `DrawGraphics`의 생성자에서 이동속도를 설정해 줍니다~
```java
public DrawGraphics() {
boxes = new ArrayList<BouncingBox>();
BouncingBox boxA = new BouncingBox(200, 50, Color.RED);
BouncingBox boxB = new BouncingBox(150, 50, Color.GREEN);
BouncingBox boxC = new BouncingBox(100, 50, Color.BLUE);
boxA.setMovementVector(1, 2);
boxB.setMovementVector(4, 3);
boxC.setMovementVector(4, 3);
boxes.add(boxA);
boxes.add(boxB);
boxes.add(boxC);
}
```
- 실행해 봅니다~!!

### 추가과제
**java.lang.Random** 클래스의 `nextInt(int bound)`메쏘드를 이용하여 매번 실행시 BouncingBox의 개수가 바뀌도록 만들어봅시다~~

# 대전 게임 만들기 II
### 상황설정
나만의 게임을 만들기 시작한 박컴돌(21) 학생. 영웅에게 전직 시스템을 도입시키기로 하는데..
### 요구사항
기존 대전 게임 시스템에 전직시스템을 추가 하시오.

+ 영웅은 직업을 선택할 수 있다.
- 기사
- 법사
+ 기사는 체력이 추가되며, 배쉬를 사용 할 수 있다.
- 체력: +100
- 배쉬: 데미지 30
+ 법사는 체력이 추가되며, 파이어볼을 사용 할 수 있다.
- 체력: +80
- 파이어볼: 데미지 35
+ 게임은 두 영웅의 생성과 동시에 시작되며 둘중 하나가 죽을 때까지 서로 공격 한다.
+ 영웅이 공격 기회를 얻을 확률은 동일하다.
+ 모든 기술은 동일한 발동 확률을 지닌다.
### 설계

### 구현
**MainTest.java**
```java
import java.util.Random;
public class MainTest {
// 테스트 코드
public static void main(String[] args) throws InterruptedException {
Hero arthas = new Knight("아서스");
Hero leona = new Wizard("레오나");
Random random = new Random();
while (arthas.hp > 0 && leona.hp > 0) {
Boolean isArthasTurn = random.nextBoolean();
Hero attacker = isArthasTurn ? arthas : leona;
Hero defender = isArthasTurn ? leona : arthas;
attacker.attack(defender);
Thread.sleep(1000);
}
}
}
```
**Hero.java**
```java
public class Hero {
protected String name;
protected int hp = 100;
// 생성자
public Hero(String name) {
this.name = name;
}
// 펀치
public void punch(Hero enermy) {
enermy.hp -= 10;
System.out.printf("[%s]의 ☞ 펀치 ☞\n", this.name);
System.out.printf("\t%s: %d\n", enermy.name, enermy.hp);
}
// 공격
public void attack(Hero enermy) {
punch(enermy);
}
}
```
**Knight.java**
```java
import java.util.Random;
public class Knight extends Hero {
// 생성자
public Knight(String name) {
super(name);
super.hp += 100; // 체력 +100
}
// 배쉬
public void bash(Hero enermy) {
enermy.hp -= 30;
System.out.printf("[%s]의 ⤷︎ 배쉬 ⤵\n", this.name);
System.out.printf("\t%s: %d\n", enermy.name, enermy.hp);
}
// 공격
@Override
public void attack(Hero enermy) {
if (new Random().nextBoolean())
this.bash(enermy);
else
super.attack(enermy);
}
}
```
**Wizard.java**
```java
import java.util.Random;
public class Wizard extends Hero {
// 생성자
public Wizard(String name) {
super(name);
super.hp += 80;
}
// 파이어볼
public void fireball(Hero enermy) {
enermy.hp -= 35;
System.out.printf("[%s]의 ✡ 파이어볼 ✡\n", this.name);
System.out.printf("\t%s: %d\n", enermy.name, enermy.hp);
}
// 공격
@Override
public void attack(Hero enermy) {
if (new Random().nextBoolean())
this.fireball(enermy);
else
super.attack(enermy);
}
}
```
### 실행결과
<div class="embed-responsive embed-responsive-4by3">
<iframe class="embed-responsive-item" src="https://www.youtube.com/embed/G2thT593Dpg"></iframe>
</div>
# 매출 계산하기 I
손수 적어 정리하던 매출.. 이제는 프로그램을 만들어 해결하기!
---
### 상황설정
라면가게를 오픈한 왕라면(45) 사장님.. 요즘 손님이 부쩍늘어 행복한 고민중이군요. 일일이 적어오던 매출전표도 이제는 도저히 손으로 감당하기가 어려워졌다 합니다.(~~종종 금액이 맞지 않는 경우가 생긴다 하네요~~)

---
### 아이디어
컴공과에 재학중인 왕라면 사장님의 아들 왕공돌(21) 학생. 이러한 문제를 해결하기 위해 직접 매출계산 소프트웨어를 만들기도 결심합니다. 주문이 들어오는 대로 바로바로 매출을 기록해주는 소프트웨어를 만들기로 한 것이죠.

---
### 메뉴분석
왕라면집 메뉴는 라면과 김밥으로 단 2가지만 있다고 합니다.

---
### 프로그램 설계
왕공돌 학생은, 프로그램을 구현하기위해 설계를 시작하였습니다. 필요 클래스는 3가지로 정하였으며 역할은 아래와 같습니다.

+ **SalesAnalyzer**(매출 분석기)
- 주문이 들어오면 라면, 김밥같은 객체들을 일일히 기록
- 기록된 주문을 토대로 매출을 계산
+ **Ramyun**(라면)
- 메뉴의 이름과 가격 정보를 지님.
+ **Kimbob**(김밥)
- 메뉴의 이름과 가격 정보를 지님.
---
### 스켈레톤 코드
현재까지 왕공돌 학생이 만든 코드는 아래와 같습니다.
**SalesAnalyzer.java**
```java
import java.util.ArrayList;
import java.util.Random;
public class SalesAnalyzer {
// 주문 리스트
private ArrayList<Ramyun> ramyunOrders;
private ArrayList<Kimbob> kimbobOrders;
public SalesAnalyzer() {
this.ramyunOrders = new ArrayList<Ramyun>();
this.kimbobOrders = new ArrayList<Kimbob>();
}
// 메인 테스트
public static void main(String[] args) {
SalesAnalyzer sales = new SalesAnalyzer();
// 랜덤 주문 생성.
sales.randomOrders();
// 매출 계산.
int totalSales = sales.calculateSales();
System.out.println("총 매출액: " + totalSales + "원");
}
// 랜덤 주문 생산기
public void randomOrders() {
Random random = new Random();
int n = 51 + random.nextInt(50);
for (int i = 0; i < n; i++) {
if (random.nextBoolean())
addRamyunOrder(new Ramyun());
else
addKimbobOrder(new Kimbob());
}
}
// 라면 주문 기록
public void addRamyunOrder(Ramyun ramyun) {
ramyunOrders.add(ramyun);
}
// 김밥 주문 기록
public void addKimbobOrder(Kimbob kimbob) {
kimbobOrders.add(kimbob);
}
// 매출 계산
public int calculateSales() {
int raCount = 0;
int kimCount = 0;
int raSales = Ramyun.PRICE * raCount;
int kimSales = Kimbob.PRICE * kimCount;
int totalSales = 0;
System.out.printf("[라면] %d개: %d원\n", raCount, raSales);
System.out.printf("[김밥] %d개: %d원\n", kimCount, kimSales);
System.out.println("==================");
return totalSales;
}
}
```
**Ramyun.java**
```java
public class Ramyun {
public static final String NAME = "라면";
public static final int PRICE = 3000;
}
```
**Kimbob.java**
```java
public class Kimbob {
public static final String NAME = "김밥";
public static int PRICE = 2000;
}
```
---
### 프로그램 구현
이제부터 주어진 소스코드를 분석하고 아래와 같은 결과화면을 얻도록 소스코드를 작성해 봅시다.

---
### Next..
왕라면 사장님께서 메뉴(떡볶이, 튀김, 순대, etc..)를 추가한다고 합니다.
# 매출 계산하기 II
인터페이스를 하용하여 소스코드 구조를 간결하게!
---
### 상황설정
얼마 전 라면가게를 오픈한 왕라면 사장님!! 가게명을 **왕분식**으로 변경후, 다양한 메뉴를 추가하기 시작하는데..

---
### 복잡해지는 소스코드
이로 인해 매출 계산 프로그램은 확장작업이 필요하게 된다..

메뉴가 추가될수록 늘어만 가는 대량의 코드.. 왕공돌(21세; 매출계산 프로그램 개발자) 학생의 고민은 깊어만 가는데..
---
### 스켈레톤 코드
현재까지 작성된 소스코드는 아래와 같습니다.
**SalesAnalyzer.java**
```java
import java.util.ArrayList;
import java.util.Random;
public class SalesAnalyzer {
// 주문 리스트
private ArrayList<Ramyun> ramyunOrders;
private ArrayList<Kimbob> kimbobOrders;
private ArrayList<Topokki> topokkiOrders;
private ArrayList<Twigim> twigimOrders;
private ArrayList<Sundae> sundaeOrders;
private ArrayList<Mandu> manduOrders;
private ArrayList<Odeng> odengOrders;
public SalesAnalyzer() {
this.ramyunOrders = new ArrayList<Ramyun>();
this.kimbobOrders = new ArrayList<Kimbob>();
this.topokkiOrders = new ArrayList<Topokki>();
this.twigimOrders = new ArrayList<Twigim>();
this.sundaeOrders = new ArrayList<Sundae>();
this.manduOrders = new ArrayList<Mandu>();
this.odengOrders = new ArrayList<Odeng>();
}
// 메인 테스트
public static void main(String[] args) {
SalesAnalyzer sales = new SalesAnalyzer();
// 랜덤 주문 생성.
sales.randomOrders();
// 매출 계산.
int totalSales = sales.calculateSales();
System.out.println("총 매출액: " + totalSales + "원");
}
// 랜덤 주문 생산기
public void randomOrders() {
Random random = new Random();
int n = 51 + random.nextInt(50);
for (int i = 0; i < n; i++) {
switch (random.nextInt(7)) {
case 0:
addRamyunOrder(new Ramyun());
break;
case 1:
addKimbobOrder(new Kimbob());
break;
case 2:
addTopokkiOrder(new Topokki());
break;
case 3:
addTwgimOrder(new Twigim());
break;
case 4:
addSundaeOrder(new Sundae());
break;
case 5:
addManduOrder(new Mandu());
break;
case 6:
addOdengOrder(new Odeng());
break;
}
}
}
// 라면 주문 기록
public void addRamyunOrder(Ramyun ramyun) {
ramyunOrders.add(ramyun);
}
// 김밥 주문 기록
public void addKimbobOrder(Kimbob kimbob) {
kimbobOrders.add(kimbob);
}
// 떡복이 주문 기록
public void addTopokkiOrder(Topokki topokki) {
topokkiOrders.add(topokki);
}
// 튀김 주문 기록
public void addTwgimOrder(Twigim twigim) {
twigimOrders.add(twigim);
}
// 순대 주문 기록
public void addSundaeOrder(Sundae sundae) {
sundaeOrders.add(sundae);
}
// 만두 주문 기록
public void addManduOrder(Mandu mandu) {
manduOrders.add(mandu);
}
// 오뎅 주문 기록
public void addOdengOrder(Odeng odeng) {
odengOrders.add(odeng);
}
// 매출 계산
public int calculateSales() {
int raCount = ramyunOrders.size();
int kimCount = kimbobOrders.size();
int toCount = topokkiOrders.size();
int twiCount = twigimOrders.size();
int sunCount = sundaeOrders.size();
int manCount = manduOrders.size();
int oCount = odengOrders.size();
int raSales = Ramyun.PRICE * raCount;
int kimSales = Kimbob.PRICE * kimCount;
int toSales = Topokki.PRICE * toCount;
int sunSales = Sundae.PRICE * sunCount;
int twiSales = Twigim.PRICE * twiCount;
int manSales = Mandu.PRICE * manCount;
int oSales = Odeng.PRICE * oCount;
int totalSales = raSales + kimSales + toSales + sunSales + twiSales
+ manSales + oSales;
System.out.printf("[라면] %d개: %d원\n", raCount, raSales);
System.out.printf("[김밥] %d개: %d원\n", kimCount, kimSales);
System.out.printf("[떡볶이] %d개: %d원\n", toCount, toSales);
System.out.printf("[튀김] %d개: %d원\n", twiCount, twiSales);
System.out.printf("[순대] %d개: %d원\n", sunCount, sunSales);
System.out.printf("[만두] %d개: %d원\n", manCount, manSales);
System.out.printf("[오뎅] %d개: %d원\n", oCount, oSales);
System.out.println("==================");
return totalSales;
}
}
```
**Ramyun.java**
```java
public class Ramyun {
public static final String NAME = "라면";
public static final int PRICE = 3000;
}
```
**Kimbob.java**
```java
public class Kimbob {
public static final String NAME = "김밥";
public static int PRICE = 2000;
}
```
**Topokki.java**
```java
public class Topokki {
public static final String NAME = "떡볶이";
public static int PRICE = 2000;
}
```
**Twigim.java**
```java
public class Twigim {
public static final String NAME = "튀김";
public static int PRICE = 2000;
}
```
**Sundae.java**
```java
public class Sundae {
public static final String NAME = "순대";
public static int PRICE = 2000;
}
```
**Mandu.java**
```java
public class Mandu {
public static final String NAME = "만두";
public static int PRICE = 2500;
}
```
**Odeng.java**
```java
public class Odeng {
public static final String NAME = "오뎅";
public static int PRICE = 2500;
}
```
**실행결과**

# 매출 계산하기 III
---
### 자바 인터페이스
메뉴의 추가로 인해 거대해진 `SalesAnalyzer`클래스.. 왕공돌 학생은 이를 해결하기 위해 교수님께 조언을 구한다..

교수님 왈,

이에 따라, 왕공돌 학생은 인터페이스에 대해 조사를 하게되고.. 기존 소스코드에 인터페이스를 적용하기로 한다.
> **자바 인터페이스**
> 같은 기능을 하는 클래스들을 하나로 묶어 관리할 수 있게 해준다
---
### 인터페이스 생성과 구현
먼저 인터페이스를 추가해주는 왕공돌 학생.
**menu.java**
```java
public interface Menu {
public String getName();
public int getPrice();
}
```
다음으로 각각의 메뉴객체(라면, 김밥, ...)들에게 인터페이스를 구현하도록 해주었군요.
**Ramyun.java**
```java
public class Ramyun implements Menu {
public static final String NAME = "라면";
public static final int PRICE = 3000;
@Override
public String getName() {
return NAME;
}
@Override
public int getPrice() {
return PRICE;
}
}
```
**Kimbob.java**
```java
public class Kimbob implements Menu {
public static final String NAME = "김밥";
public static int PRICE = 2000;
@Override
public String getName() {
return NAME;
}
@Override
public int getPrice() {
return PRICE;
}
}
```
**Topokki.java**
```java
public class Topokki implements Menu {
public static final String NAME = "떡볶이";
public static int PRICE = 2000;
@Override
public String getName() {
return NAME;
}
@Override
public int getPrice() {
return PRICE;
}
}
```
**Twigim.java**
```java
public class Twigim implements Menu {
public static final String NAME = "튀김";
public static int PRICE = 2000;
@Override
public String getName() {
return NAME;
}
@Override
public int getPrice() {
return PRICE;
}
}
```
**Sundae.java**
```java
public class Sundae implements Menu {
public static final String NAME = "순대";
public static int PRICE = 2000;
@Override
public String getName() {
return NAME;
}
@Override
public int getPrice() {
return PRICE;
}
}
```
**Mandu.java**
```java
public class Mandu implements Menu {
public static final String NAME = "만두";
public static int PRICE = 2500;
@Override
public String getName() {
return NAME;
}
@Override
public int getPrice() {
return PRICE;
}
}
```
**Odeng.java**
```java
public class Odeng implements Menu {
public static final String NAME = "오뎅";
public static int PRICE = 2500;
@Override
public String getName() {
return NAME;
}
@Override
public int getPrice() {
return PRICE;
}
}
```
---
### 리팩토링 하기
인터페이스의 모든 구현을 마친 왕공돌 학생. 이제 최종 보스인 `SalesAnalyzer`클래스를 리팩토링 하기 시작하는데..
> **리팩토링이란?**
> 결과의 변동 없이, 소스코드의 구조를 효율적으로 변경하는 작업
```java
import java.util.ArrayList;
import java.util.Random;
public class SalesAnalyzer {
// 주문 리스트
private ArrayList<Menu> orders;
public SalesAnalyzer() {
orders = new ArrayList<Menu>();
}
// 메인 테스트
public static void main(String[] args) {
SalesAnalyzer sales = new SalesAnalyzer();
// 랜덤 주문 생성.
sales.randomOrders();
// 매출 계산.
int totalSales = sales.calculateSales();
System.out.println("총 매출액: " + totalSales + "원");
}
// 랜덤 주문 생산기
public void randomOrders() {
Random random = new Random();
int n = 51 + random.nextInt(50);
for (int i = 0; i < n; i++) {
orders.add(randomOrder());
}
}
private Menu randomOrder() {
Menu menu = null;
Random random = new Random();
switch (random.nextInt(7)) {
case 0:
menu = new Ramyun();
break;
case 1:
menu = new Kimbob();
break;
case 2:
menu = new Topokki();
break;
case 3:
menu = new Twigim();
break;
case 4:
menu = new Sundae();
break;
case 5:
menu = new Mandu();
break;
case 6:
menu = new Odeng();
break;
}
return menu;
}
// 매출 계산
public int calculateSales() {
int totalSales = 0;
int raCount = 0;
int kimCount = 0;
int toCount = 0;
int twiCount = 0;
int sunCount = 0;
int manCount = 0;
int oCount = 0;
for (Menu menu : orders) {
if (menu.getClass() == Ramyun.class)
raCount++;
else if (menu.getClass() == Kimbob.class)
kimCount++;
else if (menu.getClass() == Topokki.class)
toCount++;
else if (menu.getClass() == Twigim.class)
twiCount++;
else if (menu.getClass() == Sundae.class)
sunCount++;
else if (menu.getClass() == Mandu.class)
manCount++;
else if (menu.getClass() == Odeng.class)
oCount++;
totalSales += menu.getPrice();
}
int raSales = Ramyun.PRICE * raCount;
int kimSales = Kimbob.PRICE * kimCount;
int toSales = Topokki.PRICE * toCount;
int sunSales = Sundae.PRICE * sunCount;
int twiSales = Twigim.PRICE * twiCount;
int manSales = Mandu.PRICE * manCount;
int oSales = Odeng.PRICE * oCount;
System.out.printf("[라면] %d개: %d원\n", raCount, raSales);
System.out.printf("[김밥] %d개: %d원\n", kimCount, kimSales);
System.out.printf("[떡볶이] %d개: %d원\n", toCount, toSales);
System.out.printf("[튀김] %d개: %d원\n", twiCount, twiSales);
System.out.printf("[순대] %d개: %d원\n", sunCount, sunSales);
System.out.printf("[만두] %d개: %d원\n", manCount, manSales);
System.out.printf("[오뎅] %d개: %d원\n", oCount, oSales);
System.out.println("==================");
return totalSales;
}
}
```
---
### 최종결과
훗날 왕공돌 학생은 추가되는 메뉴에도 손쉽게 프로그램을 확장할 수 있었다고 한다..
