java 자판기 아이템 관리-1

코등어·2024년 12월 11일
0

사전캠프

목록 보기
19/19

잡소리

이번엔 기존에 만들었던 스파르타 자판기 프로그램을 만드는 문제를 풀다가
음료수의 이름과 가격을 담은 item 가지고 아주 간단한 crud! 또 텍스트 파일을 저장소로 라는 프로그램을 구현해보자!
라는 포부를 가지고.. 파일 입출력에 대해 공부하고 삽질을 하다가 어쩌다보니(?) model, repository, service, controller, main을 계층으로 쓰는 산출물이 나왔다..
일단 파일 별로 코드만 띄워놓고 설명은 추후에 작성할 생각이다.
물론.. 로직을 고치게 될 경우도 있다..

왜 이런 생각을 했는가?

생각의 시작은 매우 단순했다. 아이템을 관리하고 싶어! 였다.
단순히 추가만 하는게 아니라 삭제, 수정, 내가 가지고 있는 아이템들을 보고싶어!
어? 근데 프로그램을 실행할 때 마다 맨 처음엔 무조건 아이템을 하나 이상은 추가를 해야하는게
불편해.. 내가 추가시켜 놓은 아이템을 불러오고 싶어
이런 생각들을 가지고 시작하게 되었다.
또 그러다 보니 main 메서드에 또 모든걸 때려 박는 장관이 펼쳐졌고
나름 역할을 나누어서 코드를 짜보고 싶었다.

model

package sparta_test.level2_3.model;

import java.util.Objects;

public class Item {
    private int id;
    private String name;
    private int price;

    public int getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public int getPrice() {
        return price;
    }

    public void setId(int id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Item item = (Item) o;
        return id == item.id && price == item.price && Objects.equals(name, item.name);
    }

    @Override
    public int hashCode() {
        return Integer.hashCode(id);
    }
}

모델은 왜 이렇게 짰어?

추후 좀 더 자세히 글을 써볼 예정이지만, 필자가 생각한 여기서의 모델이란 실제의 개념을 추상화하여 소프트웨어 객체로 표현한 것을 나타내는 계층이다.
내가 생각한 아이템이란 것은 번호, 이름, 가격이 존재하고 동일한 이름을 가진 아이템이 생겨나지 않기를 생각했다. 이름이 같아도 가격이 다른 것도 아니라 아예 동일한 이름부터 가지면 안된다고 생각했다.

repository

package sparta_test.level2_3.repository;

import sparta_test.level2_3.model.Item;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class ItemRepository {
    private File file;
    private List<Item> itemList;
    private int id;

    public ItemRepository(String filePath) {
        this.file = new File(filePath);
        this.itemList = new ArrayList<>();
        this.id = 1;  // 초기 ID 설정
        dataFromFile();  // 파일에서 아이템을 로드
    }

    public boolean addItem(Item item) {
        item.setId(id++);
        itemList.add(item);
        dataToFile();  // 파일에 저장
        return true;
    }

    public List<Item> getItems() {
        return itemList;
    }
    public Item readByName(String name){
        for (Item target : itemList) {
            if (target.getName().equals(name)) {
                return target;
            }
        }
        return null;
    }

    public boolean remove(Item item) {
        boolean removed = itemList.remove(item);
        if (removed) {
            dataToFile();  // 파일에 저장
        }
        return removed;
    }

    public boolean update(Item item) {
        for (int i = 0; i < itemList.size(); i++) {
            Item target = itemList.get(i);
            if (target.getName().equals(item.getName())) {
                itemList.set(i, item);
                dataToFile();  // 파일에 저장
                return true;
            }
        }
        return false;
    }

    private void dataFromFile() {
        // 파일이 없으면 생성
        if (!file.exists()) {
            try {
                file.createNewFile();  // 파일 생성
            } catch (IOException e) {
                System.out.println("파일 생성 실패");
                return;
            }
        }

        // 파일을 읽어 데이터 추가
        try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
            String line;
            while ((line = reader.readLine()) != null) {
                String[] data = line.split(" ");
                if (data.length == 3) {
                    try {
                        Item item = new Item();
                        item.setId(Integer.parseInt(data[0]));
                        item.setName(data[1]);
                        item.setPrice(Integer.parseInt(data[2]));
                        itemList.add(item);
                    } catch (NumberFormatException e) {
                        System.out.println("잘못된 데이터 형식: " + line);
                    }
                }
            }
        } catch (IOException e) {
            System.out.println("파일 읽기 실패");
        }
    }

    private void dataToFile(){ //auto closeable
        try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
            for(Item item : itemList){
                writer.write(item.getId() + " " + item.getName() + " " + item.getPrice());
                writer.newLine();
            }
        }catch (IOException e){
            System.out.println("파일 데이터 저장 실패");
        }

    }

}

repository는 왜 이렇게 짰어?

repository는 데이터베이스에 직접 접근을 할 수 있는 역할을 하는 계층이라고 생각했다.
아이템이 저장된 곳에(여기서는 메모장ㅋㅋ) 아이템을 추가, 수정, 삭제, 불러오기(전체든 한개든)의 작업을 할 수 있는 곳이라 생각했다.
dataToFiledataFromFile 처럼 아이템을 저장하는 작업을 어디서 해야할지 고민을 좀 했었다.
이유는 파일 입출력에 대한 개념도 없었고 데이터를 저장소에 저장, 불러오는 과정을 어디서 처리할지에 대한 개념조차 잡히지 않은 상태였기 때문이라고 생각한다.
결론은 아주 간단했다. 데이터를 저장하고 불러오는 것도 저장소에 접근하는 것이 아닌가?
그렇다면 repository에 써야지.

service

package sparta_test.level2_3.service;

import sparta_test.level2_3.model.Item;
import sparta_test.level2_3.repository.ItemRepository;

import java.util.List;

public class ItemService {
    private final ItemRepository itemRepository;

    public ItemService(ItemRepository itemRepository) {
        this.itemRepository = itemRepository;
    }

    public boolean addItem(Item item){
        if(isNull(item)){
            return false;
        }
        if(isItemExist(item)){
            System.out.println("아이템이 이미 존재합니다.");
            return false;
        }
        return itemRepository.addItem(item);
    }

    public boolean update(Item item){
        if(isNull(item)){
            return false;
        }
        if(isItemExist(item)){
            return itemRepository.update(item);
        }
        System.out.println(item.getName()+"에 해당하는 아이템이 존재하지 않습니다.");
        return false;
    }

    public boolean remove(String name){
        Item item = itemRepository.readByName(name);
        if(isItemExist(item)){
            return itemRepository.remove(item);
        }
        System.out.println(item.getName()+"에 해당하는 아이템이 존재하지 않습니다.");
        return false;
    }

    public List<Item> getAllItems(){
        return itemRepository.getItems();
    }

    public Item getItemByName(String name) {
        List<Item> items = itemRepository.getItems();
        for (Item item : items) {
            if (item.getName().equals(name)) {
                return item;
            }
        }
        System.out.println(name + "에 대한 아이템은 존재하지 않습니다.");
        return null;
    }

    public boolean isItemExist(Item item){
        List<Item> items = itemRepository.getItems();
        for(Item target : items){
            if(target.getName().equals(item.getName())){
               // System.out.println("동일한 아이템이 존재합니다.");
                return true;
            }
        }
        //System.out.println(item.getName()+"에 해당하는 아이템이 존재하지 않습니다.");
        return false;
    }
    public boolean isNull(Item item){
        if (item == null) {
            System.out.println("아이템이 null입니다.");
            return true;
        }
        return false;
    }
}

service는 왜 이렇게 짰어?

service는 repository에서 가져온 데이터를 가공하는 곳으로
예를 들어 아이템이 null값인지, 추가, 수정 작업을 할때 동일한 아이템이 존재하는지
체크를 해주는 곳이라는 생각했다.

controller

package sparta_test.level2_3.controller;

import sparta_test.level2_3.model.Item;
import sparta_test.level2_3.service.ItemService;

import java.util.List;

public class ItemController {
    private final ItemService itemService;

    public ItemController(ItemService itemService) {
        this.itemService = itemService;
    }

    // 아이템 추가
    public boolean addItem(Item item) {
        boolean success = itemService.addItem(item);
        if (success) {
            System.out.println("아이템이 성공적으로 추가되었습니다.");
        } else {
            System.out.println("아이템 추가에 실패했습니다.");
        }
        return success;
    }

    // 아이템 수정
    public boolean updateItem(Item item) {
        boolean success = itemService.update(item);
        if (success) {
            System.out.println("아이템이 성공적으로 수정되었습니다.");
        } else {
            System.out.println("아이템 수정에 실패했습니다.");
        }
        return success;
    }

    // 아이템 삭제
    public boolean removeItem(String name) {
        boolean success = itemService.remove(name);
        if (success) {
            System.out.println("아이템이 성공적으로 삭제되었습니다.");
        } else {
            System.out.println("아이템 삭제에 실패했습니다.");
        }
        return success;
    }

    // 아이템 목록 조회
    public List<Item> getAllItems() {
        return itemService.getAllItems();
    }

    // 이름으로 아이템 조회
    public Item getItemByName(String name) {
        return itemService.getItemByName(name);
    }
}

controller는 왜이렇게 짰어?

controller는 사용자의 요청을 처리, 응답 반환을 하는 계층으로
요청한 데이터를 service에 전달하고 처리된 결과를 사용자에게 반환하는
중개자의 역할을 한다고 생각했다.
그래서 단순히 요청에 대한 성공, 실패에 관한 코드만 작성했다.

main

package sparta_test.level2_3.main;

import sparta_test.level2_3.controller.ItemController;
import sparta_test.level2_3.model.Item;
import sparta_test.level2_3.repository.ItemRepository;
import sparta_test.level2_3.service.ItemService;

import java.util.List;
import java.util.Scanner;

public class ItemMain {
    public static void main(String[] args) {
        String filePath = "items.txt";  // 아이템을 저장할 txt 파일 경로
        ItemRepository itemRepository = new ItemRepository(filePath);
        ItemService itemService = new ItemService(itemRepository);
        ItemController itemController = new ItemController(itemService);

        Scanner sc = new Scanner(System.in);

        while (true) {
            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. 종료");
            System.out.print("선택: ");
            int choice = sc.nextInt();
            sc.nextLine();

            switch (choice) {
                case 1:
                    Item addItem = new Item();
                    System.out.println("1. 아이템 추가");
                    System.out.print("추가할 아이템의 이름을 입력하세요: ");
                    String name = sc.nextLine();
                    if(name.isEmpty()){
                        System.out.println("아이템 이름은 비어있을 수 없습니다.");
                        break;
                    }
                    System.out.print("추가할 아이템의 가격을 입력하세요: ");
                    int price = sc.nextInt();
                    sc.nextLine();
                    if(price < 0){
                        System.out.println("가격은 음수가 될 수 없습니다.");
                        break;
                    }
                    addItem.setName(name);
                    addItem.setPrice(price);
                    itemController.addItem(addItem);
                    break;

                case 2:
                    System.out.println("2. 아이템 목록 조회");
                    List<Item> itemList = itemController.getAllItems();
                    if(itemList.isEmpty()){
                        System.out.println("아이템 목록이 비어 있습니다.");
                    }else {
                        System.out.println("아이템 목록:");
                        for (Item i : itemList) {
                            System.out.println("ID: " + i.getId() + "번, 이름: " + i.getName() + ", 가격: " + i.getPrice() +"원");
                        }
                    }
                    break;

                case 3:
                    System.out.println("3. 아이템 이름으로 조회");
                    System.out.print("찾을 아이템의 이름을 입력해주세요: ");
                    String searchName = sc.nextLine();
                    Item foundItem = itemController.getItemByName(searchName);
                    System.out.println("==아이템 정보==");
                    System.out.println("ID: " + foundItem.getId() + ", 이름: " + foundItem.getName() + ", 가격" + foundItem.getPrice() + "원");
                    break;

                case 4:
                    System.out.println("4. 아이템 삭제");
                    System.out.print("삭제할 아이템의 이름을 입력하세요: ");
                    String deleteName = sc.nextLine();
                    if(deleteName.isEmpty()){
                        System.out.println("아이템 이름은 비어있을 수 없습니다.");
                        break;
                    }
                    itemController.removeItem(deleteName);
                    break;

                case 5:
                    System.out.println("5. 아이템 수정");
                    System.out.print("수정하려는 아이템의 이름을 입력하세요: ");
                    String existedItem = sc.nextLine();
                    Item updateItem = itemController.getItemByName(existedItem);
                    System.out.print("새로운 아이템의 이름을 입력하세요: ");
                    String updateName = sc.nextLine();
                    if(updateName.isEmpty()){
                        System.out.println("아이템 이름은 비어있을 수 없습니다.");
                        break;
                    }
                    System.out.print("새로운 아이템의 가격을 입력하세요: ");
                    int updatePrice = sc.nextInt();
                    sc.nextLine();
                    if(updatePrice < 0){
                        System.out.println("가격은 음수가 될 수 없습니다.");
                        break;
                    }
                    updateItem.setName(updateName);
                    updateItem.setPrice(updatePrice);
                    itemController.updateItem(updateItem);
                    break;

                case 6:
                    System.out.println("시스템을 종료합니다.");
                    return;

                default:
                    System.out.println("잘못된 입력입니다.");
            }
        }
    }
}
profile
정형화되지 않은 날 것의 생각을 기록합니다.

0개의 댓글