메모장

[실습문제] 자판기프로그램 MVC패턴 본문

JAVA/[실습문제]

[실습문제] 자판기프로그램 MVC패턴

Itchild 2024. 4. 8. 17:41
728x90
반응형

 

실습문제 !

새로운 프로그램 작성하기 == 자판기 프로그램

-> 음료수 클래스

PK,이름,가격,재고

음료추가(),음료목록출력(),구매(),음료삭제()

C, R, U, D 기능 넣어서

MVC 패턴으로 작성하기

+ 음료 검색

+ 장바구니 추가 !

 

Model - DrinkVO

package model;

public class DrinkVO {
	private int num;
	private String name;
	private int price;
	private int cnt;
	public DrinkVO(int num,String name,int price,int cnt) {
		this.num=num;
		this.name=name;
		this.price=price;
		this.cnt=cnt;
	}
	public int getNum() {
		return num;
	}
	public void setNum(int num) {
		this.num = num;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public int getCnt() {
		return cnt;
	}
	public void setCnt(int cnt) {
		this.cnt = cnt;
	}
	@Override
	public String toString() {
		String msg=num+"번 "+name+" "+price+"원 ";
		if(this.cnt==0) {
			msg+="[X]";
		}
		else {
			msg+="["+cnt+"개]";
		}
		return msg;
	}
	@Override
	public boolean equals(Object obj) { // 오버라이딩
		DrinkVO drinkVO = (DrinkVO)obj; // 다운 캐스팅
		if(this.num==drinkVO.num) {//만약 내 PK 와 비교대상의 PK가 같아?
			return true; // 같으면 참이야 
		}
		return false;
	}
	
}
 

Model - DrinkDAO

package model;

import java.util.ArrayList;

// DAO의 CRUD(비즈니스 메서드,핵심 로직)는
// 어떤 추가 요청사항에도 절대!
// "메서드 시그니처"가 변하지않도록 코드를 작성해야함!!!!!

// "메서드 시그니처"가 변경되면 다른 파트에 영향을 준다...
// '기능' 변경은 괜찮다.

// 결론) "메서드 시그니처"는 설계단계에서 절대로 변경되면 안됨!!!!!!!!!!

public class DrinkDAO {

	private static int PK = 1001;
	private ArrayList<DrinkVO> datas;

	public DrinkDAO() {
		datas = new ArrayList<DrinkVO>();
		datas.add(new DrinkVO(PK++, "콜라", 1200, 3));
		datas.add(new DrinkVO(PK++, "사이다", 900, 2));
		datas.add(new DrinkVO(PK++, "환타", 1500, 1));
	}

	// C
	public boolean insert(DrinkVO vo) { // DrinkVO vo로 인자 통일
		// 앞으로 insert에 호출할 데이터는 vo에 다 넣어놔야한다.
		// 컨트롤러로 이동 -> insert 할때 vo전달
		System.out.println("MODEL(DAO): vo: " + vo); // 로그 보여주기 vo 출력
		datas.add(new DrinkVO(PK++, vo.getName(), vo.getPrice(), vo.getCnt()));
		// vo로 부터 이름받고, vo로 부터 가격받고, vo로 부터 재고받기
		return true;
	}



	// 목록 출력 부분에서 
	// nullpointException 이 뜨는 이유 
	//★★★ java.lang.NullPointerException
	//-> "주어(주체)"가 Null이어서 발생하는 이슈
	//-> 메서드 앞의 주어가 Null이어서 발생하는 이슈
	//-> aaa.bbbbb();에서 aaa가 Null이어서 발생하는 이슈



	//	1. 전체목록 출력 => 그냥 내가가진 멤버변수 datas 반환
	//	2. 검색결과 출력 => 이름을 보고, 그 이름을 가진 애들을 반환	
	// R
	public ArrayList<DrinkVO> selectAll(DrinkVO vo) {
		if (vo.getName() == null) { 
			//지금 주어 vo.뭐시기 에서 vo가 null이라서 NullPointerException 이 뜬것이다.
			//사용자의 name이 검색을 안했어 ? // 만약에 그냥 전체목록출력이었다면 
			return datas; // 그럼 그냥 데이터를 반환해주고 
		}  else {// 이름을 보고
			ArrayList<DrinkVO> datas = new ArrayList<DrinkVO>();
			// 새로운 datas를 만들어서,
			// 검색 결과에 해당하는 애 
			for (DrinkVO data : this.datas) {//이름을 가진 애들을 넣어서 반환
				if (data.getName().contains(vo.getName())) {// 검색이름이 data이름에 존재해 ?
					// equals는 그 정확히 그 단어가 일치해야만 나온다 
					// 그래서 contains를 쓰기 
					datas.add(data);
				}
			}
			return datas;
		}
	}

	// R
	public DrinkVO selectOne(DrinkVO vo) { // PK
		for (DrinkVO data : datas) {
			if (data.getNum() == vo.getNum()) { // 데이터를 받은 pk랑 입력받은 번호랑 일치하면
				DrinkVO dVO = new DrinkVO(data.getNum(), data.getName(), data.getPrice(), data.getCnt());
				// return 자리에 data가 들어가게 되면 ,, 원본이 자료를 받은 그대로 출력 되기때문에
				// 원본 데이터에 손상이 올수도 있다는 에러가 떴다.
				// 그래서 data를 그대로 반환 하는것이 아니라 그것과 똑같은 복사본을 만들어서
				// 그 복사본을 반환 시킨다.

				return dVO;
				// 실제 DB 데이터(==datas)를 전송 xxxxx
				// 새로 vo객체를 new(객체화)해서 전송 O
			}
		}
		System.out.println(" 로그: selectOne(): 해당상품없음");
		return null;
	}


	// 오버로딩이 불가능한 기능 일 경우,
	// 기능이 매우 유사한 것이기 때문에 내부 로직에서 분리하여 관리
	// -> 유사한 기능끼리는 묶어서 관리하는것이 용이하기 때문
	// == 응집도를 높임 -> 유지보수에 용이

	// 인자를 vo로 맞춰서 결합도를 낮추고 기능이 유사한 것을 한데 모아서 응집도를 높임
	// 자바로만 했을 때의 최상급 코드

	// U
	public boolean update(DrinkVO vo) {
		for (DrinkVO data : datas) {
			if (data.getNum() == vo.getNum()) {
				// 구매할때에는 절대값이랑 내 재고랑 비교
				if (vo.getCnt() < 0) { // 구매할때
					int cnt = vo.getCnt() * (-1); // 구매하려는 양
					if (cnt > data.getCnt()) {
						System.out.println(" 로그: update(): 재고없음");
						return false;
					}
				}
				data.setCnt(data.getCnt() + vo.getCnt()); 
				// 보통 재고추가에 맞춰서 + 로 하고
				// 구매를 하면 cnt를 빼주는 걸로 많이 쓴다.
				System.out.println("data: " + data);
				return true;
			}
		}
		System.out.println(" 로그: update(): 해당상품없음");
		return false;
	}


	// D
	public boolean delete(DrinkVO vo) {
		for (int i = 0; i < datas.size(); i++) {
			if (datas.get(i).getNum() == vo.getNum()) {
				datas.remove(i);
				return true;
			}
		}
		System.out.println(" 로그: delete(): 해당상품없음");
		return false;
	}
}
 

View - DrinkView

package view;

import java.util.ArrayList;
import java.util.Scanner;



import model.DrinkVO;

public class DrinkView {

	private static Scanner sc=new Scanner(System.in);

	public int tryCatch() {
		while(true) {
			try {
				System.out.print("입력) ");
				int action=sc.nextInt();

				return action;
			}
			catch(Exception e) {
				sc.nextLine();
				System.out.println("정수로 입력해주세요!");
			}
		}
	}

	public int printMenu() {
		System.out.println("=== 자 판 기 ===");
		System.out.println("1. 음료추가");
		System.out.println("2. 음료목록출력");
		System.out.println("3. 음료구매");
		System.out.println("4. 음료제거");
		System.out.println("5. 음료재고추가");
		System.out.println("6. 음료검색"); 
		// "이" 라고 검색시 아이스크림,아이스티 등 띄우기 // selectAll(2개이상)
		// R ->selectAll 
		System.out.println("7. 프로그램 종료");
		while(true) {
			int action=tryCatch();
			if(1<=action && action<=7) {
				return action; // "유효성 검사" == 사용자의 입력값 검사
				// : 사용자의 입력값에 대하여 자료형(타입),범위 등을 확인하는 것
			}
		}
	}

	public String getDrinkName() { // 음료 이름 입력
		System.out.print("음료이름 ");
		String name=sc.next();
		return name;
	}
	public int getDrinkCnt() { // 음료 재고 입력
		System.out.print("음료재고입력) ");
		int cnt=sc.nextInt();
		return cnt;
	}
	public int getDrinkPrice() { // 가격입력
		System.out.print("음료가격입력) ");
		int price=sc.nextInt();
		return price;
	}

	public void printDrinkList(ArrayList<DrinkVO> datas) { // 음료 목록 출력
		if(datas.isEmpty()) {
			System.out.println("출력할 음료가 없습니다...");
			return;
		}
		
		for(DrinkVO v:datas) {
			System.out.println(v);
		}
	}

	public int getDrinkNum() { // 음료 번호 입력
		System.out.print("음료번호입력) ");
		int num=sc.nextInt();
		return num;
	}

	public void printTrue() { // 서비스 완료
		System.out.println("요청하신 서비스를 완료했습니다.");
	}
	public void printFalse() { // 서비스 실패
		System.out.println("요청하신 서비스는 현재 이용이 어렵습니다.");
		System.out.println("다음에 다시 이용해주시기 바랍니다.");
	}

	public void printEnd() { // 프로그램 종료
		System.out.println("프로그램 종료...");
	}

}
 

Controller - DrinkController

package ctrl;

import java.util.ArrayList;

import model.DrinkDAO;
import model.DrinkVO;
import view.DrinkView;

public class DrinkCtrl {
	private DrinkView view;
	private DrinkDAO dao;
	public DrinkCtrl() {
		view=new DrinkView();
		dao=new DrinkDAO();
	}

	public void startApp() {
		while(true) {// while문으로 한번 더 감쌌다. 
			// 장바구니가 종료되기 전까지 절대로 끝나지 않는 루프 

			System.out.println(" 로그: 사용자가 새로 입장합니다."); 
			// 장바구니를 설명하기 위해 고객이 접속한다는 뜻
			// 로그
			//  : 확인을 위한 것
			//  : 실제 서비스에서는 출력 xxx
			ArrayList<DrinkVO> cart=new ArrayList<DrinkVO>(); 
			// 카트라는 공간을 하나 새로 만든다.
			// 사용자가 새로 입장하면 장바구니가 초기화 돼야된다
			// 장바구니의 타입은 ArrayList 
			// 장바구니 장바구니 = 초기화;


			while(true) {
				int action=view.printMenu();

				if(action==1) { // 음료추가
					String name=view.getDrinkName(); // 이름을 입력받고
					int price=view.getDrinkPrice(); // 가격도 입력받고
					int cnt=view.getDrinkCnt(); // 수량도 입력받고
					DrinkVO vo=new DrinkVO(0,name,price,cnt); 
					// vo는 살짝 만들어서 쓰고 그다음에 쓰지 않는 친구 
					// pk는 지금 몰라서 그냥 0으로 설정
					// 요렇게 선언 해주고 사용하기 // 인자 4개
					// 전달하지않아도되는 값들은 0,null 등으로 설정

					System.out.println("CTRL: vo: "+vo); // vo 로그 생성
					// DAO의 CRUD에게 전달해야하는 값만 설정

					if(dao.insert(vo)) { // DrinkVO의 vo라는 값에 추가해줘  
						view.printTrue(); // true면 성공
					}
					else { 
						view.printFalse(); // false면 실패
					}
				}
				else if(action==2) { //음료 목록 추가 
					ArrayList<DrinkVO> datas=dao.selectAll(new DrinkVO(0,null,0,0)); 
					// 목록에 반환되는 값은 없지만 null을 넣으면 에러가 뜨므로 
					// 필요한 값은 없지만 초기화 해주기 
					//전체 목록을 불러올건데 
					// 인자가 vo인데 로직에서 vo를 사용하지 않아서 
					// selectAll(vo 이지만 사용하는 게 없기 때문에)
					// selectAll(null) null 값을 넣어준다.
					// 초기값 pk =0, 이름 = null, 가격 = 0, 수량 =0 ;
					view.printDrinkList(datas); // 저장된 데이터들을 forEach문으로 다 불러와줘 
				}
				else if(action==3) { // 구매하기
					int num=view.getDrinkNum(); // 음료 번호 입력 받기
					int cnt=1; // 수량은 1로 정해놨다.
					DrinkVO vo=new DrinkVO(num,null,0,-cnt);  
					// 전달하고자 하는 값만 넣어주면 된다.
					// 보통 구매에서 수량을 구입하면 재고에서 빼줘야 하기 때문에
					// -cnt라고 표현해준다 
					// 입력하면 구입할 시에 cnt를 빼줄수 있게

					if(dao.update(vo)) {
						// 구매에 성공했을때에만 장바구니에 해당 상품을 추가
						// 구매에 성공 했다~~ 그러면 
						DrinkVO data=dao.selectOne(vo); 
						//모델에게 PK,name,price좀 줄래 ? 한데 묶은 data좀 줄래?
						// data 하나 니까 selectOne
						// 이때 selectOne 해서 나온 데이터는 자판기에 들어있는 재고가 저장이 되어있기 때문에
						// data의 재고는 내가 원하던 재고로 맞춰줘야함 


						// data에 정보를 담아서 하나 가져와 //복사본을 가져왔다.
						// 근데 복사본도 원본과 동일하므로 받아온 데이터를 그대로 출력한것 
						// 목록엔 콜라가 5개 있는데 
						// 나는 내가 구입한 1개의 콜라만 보여줘야 하므로 
						// 내가 가진 콜라 1개 짜리로 data를 다시 설정한다.
						// data의 수량
						data.setCnt(cnt); 
						// ★ PK,name,price는 자판기에 저장된 상품 정보로 가능하지만,
						// ★ cnt는 사용자가 입력했던 정보로 변경해야함! 


						// 커스터마이징 할일이 없기때문에 flag 알고리즘을 사용 
						boolean flag=false; // 카트에 그게 있었는지 없었는지~
						int index=0; // 인덱스 번호 변수 선언 
						for(int i=0;i<cart.size();i++) { //장바구니의 크기만큼
							if(data.equals(cart.get(i))) { // 사용자가 선택한 제품이랑 카트 제품이 동일해?
								//데이터 인덱스 번호 자리랑 카트에 고른 인덱스 번호랑 같은 상품일때
								//연산자가 안되서 오버라이딩
								index=i;  //인덱스 위치를 기억 시켜놓으면 바로 추가하면 되니까
								// 인덱스 번호는 몇번인지 //인덱스 번호 표시
								flag=true; // true일때 작동 // 조건이 맞을때 작동
							}
						}
						if(flag) {
							// true 일때 이것도 true가 되면서 
							// 인덱스번호자리의 수량들을 다시 설정 
							// 같은 품목이니까 1-> 2로 수량이 쌓임 
							// 카트의 인덱스 번호 자리의 수량을 다시 설정
							// 기존거 에서 + 수량을 추가 
							cart.get(index).setCnt(cart.get(index).getCnt()+cnt);
							// 기존 수량에 추가한 수량을 더함 
						}
						else {
							// 혹시 cart에 없던 제품이었니 ? 그러면 add
							cart.add(data); // 데이터에 담길거 : PK,name,price,cnt 알아야한다
							// 근데 ! 기존 데이터에서 PK,name,price는 알아낼수 있는데
							// cnt는 input 값에서 얻어낼수 있다.
							// 장바구니.add(무엇을 샀는지 기입 : 내가 구매한 상품 넣기);
							// 장바구니에는 담지만 내가 담은 항목이 서로 연관없는 다른 상품이라면 
							// 재고가 중첩되지않고 물건 1개, 물건 1개 이렇게 쌓임
							// 단 , 내가 커스터마이징 할수 있는 제품은 1개, 1개 이렇게 쌓임
							//ex)아메리카노 시럽없이, 아메리카노 얼음많이 등등 
							// 내가 구매한 상품을 차곡차곡 
						}

						view.printDrinkList(cart); // 장바구니에 추가된 항목 보기 
						view.printTrue();
					}
					else {
						view.printFalse();
					}
				}
				else if(action==4) { // 음료 삭제
					int num=view.getDrinkNum();
					DrinkVO vo=new DrinkVO(num,null,0,0);
					if(dao.delete(vo)) {
						view.printTrue();
					}
					else {
						view.printFalse();
					}
				}
				else if(action==5) { // 재고 추가
					int num=view.getDrinkNum();
					int cnt=view.getDrinkCnt();// 재고추가는 입력한 만큼 더하는것
					DrinkVO vo=new DrinkVO(num,null,0,cnt); //누구를 얼마만큼 추가할래 ?
					if(dao.update(vo)) {
						view.printTrue();
					}
					else {
						view.printFalse();
					}
				}
				else if(action==6) { // 음료 검색
					String name=view.getDrinkName(); // 너 뭐 검색할래 ? 이름받기
					// 그다음 출력하기 
					ArrayList<DrinkVO> datas=dao.selectAll(new DrinkVO(0,name,0,0));
					view.printDrinkList(datas);
				}
				else if(action==7) { // 프로그램 종료
					view.printEnd();
					break;
				}
			}
			System.out.println(" 로그: 사용자가 종료했습니다.");
			cart.clear();
		}
	}
}
 

Client - DrinkClient

package client;

import controller.DrinkCtrl;

public class Client {
	public static void main(String[] args) {

		DrinkCtrl app=new DrinkCtrl();
		app.startApp();
		
		// 클라이언트는 이 코드로 고정 !	
	}
}
 

 

 

728x90
반응형