Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seminar1 심화 과제 #4

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 38 additions & 0 deletions src/main/java/org/sopt/seminar1/Diary.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.sopt.seminar1;

import java.time.LocalDate;
import java.time.LocalDateTime;

public class Diary {
private Long id;
private final String body;
private boolean isDeleted = false;
private LocalDateTime lastEditedDateTime;

public Diary(Long id, String body) {
this.id = id;
this.body = body;
this.lastEditedDateTime = LocalDateTime.now();
}

public Long getId() {
return id;
}
public String getBody() {
return body;
}

public boolean isDeleted() {
return isDeleted;
}
public void setDeleted(boolean deleted) {
this.isDeleted = deleted;
}

public LocalDateTime getLastEditedDateTime() {
return lastEditedDateTime;
}
public void setLastEditedDateTime(LocalDateTime lastEditedDateTime) {
this.lastEditedDateTime = lastEditedDateTime;
}
}
63 changes: 63 additions & 0 deletions src/main/java/org/sopt/seminar1/DiaryController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.sopt.seminar1;

import java.util.List;

public class DiaryController {
private Status status = Status.READY;
private final DiaryService diaryService = new DiaryService();

Status getStatus() {
return status;
}

void boot() {
this.status = Status.RUNNING;
}

void finish() {
this.status = Status.FINISHED;
}

// APIS
final List<Diary> getList() {
return diaryService.getDiaryList();
}

final void post(final String body) {
diaryService.writeDiary(body);
}

final void delete(final String id) {
long longId = validateAndConvertId(id);
diaryService.deleteDiary(longId);
}

final void patch(final String id, final String body) {
long longId = validateAndConvertId(id);
diaryService.updateDiary(longId, body);
}

final void restore(final String id) {
long longId = validateAndConvertId(id);
diaryService.restoreDiary(longId);
}

enum Status {
READY,
RUNNING,
FINISHED,
ERROR,
}

private long validateAndConvertId(final String id) {
if (!id.matches("^[1-9][0-9]*$")) {
throw new Main.UI.InvalidInputException();
}

try {
return Long.parseLong(id);
} catch (NumberFormatException e) {
throw new IllegalArgumentException("유효한 long 형식의 ID여야 합니다.");
}
}
}
67 changes: 67 additions & 0 deletions src/main/java/org/sopt/seminar1/DiaryRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package org.sopt.seminar1;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;

public class DiaryRepository {
private final Map<Long, Diary> storage = new ConcurrentHashMap<>();
private final AtomicLong numbering = new AtomicLong();
private int updateCount = 0;

void save(Diary diary) {
final long id = numbering.addAndGet(1);
diary.setDeleted(false);
diary = new Diary(id, diary.getBody());
storage.put(id, diary); // Diary 객체를 저장
}

List<Diary> findAll() {
final List<Diary> diaryList = new ArrayList<>();
for (long index = 1; index <= numbering.longValue(); index++) {
final Diary diary = storage.get(index);
if (diary != null && !diary.isDeleted()) {
diaryList.add(diary);
}
}
return diaryList;
}

void delete(final long id) {
Diary diary = storage.get(id);
if (diary != null) {
diary.setDeleted(true);
}
}

void update(final long id, final String newBody) {
Diary diary = storage.get(id);
if (diary != null && !diary.isDeleted()) {
LocalDateTime now = LocalDateTime.now();
LocalDateTime lastEditedDateTime = diary.getLastEditedDateTime();

if (!(lastEditedDateTime.toLocalDate().equals(now.toLocalDate()))) {
updateCount = 0;
diary.setLastEditedDateTime(now);
}
Comment on lines +43 to +49

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

해당 로직은 Repository 영역이라고 볼 수 있을까요?
Repository 의 역할은 단순히 데이터를 읽고 조회하는 역할이라고 생각이 들어요.


if (updateCount <2){
updateCount++;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateCount 에 대한 동시처리 고민도 가지면 좋을것같습니다

diary.setDeleted(false);
storage.replace(id, new Diary(id, newBody));
} else {
throw new Main.UI.InvalidInputException();
}
}
}

void restore(final long id) {
Diary diary = storage.get(id);
if (diary != null && diary.isDeleted()) {
diary.setDeleted(false);
}
}
}
24 changes: 24 additions & 0 deletions src/main/java/org/sopt/seminar1/DiaryService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.sopt.seminar1;

import java.util.List;

public class DiaryService {
private final DiaryRepository diaryRepository = new DiaryRepository();

void writeDiary(String body){
final Diary diary = new Diary(null, body);
diaryRepository.save(diary);
}
List<Diary> getDiaryList(){
return diaryRepository.findAll();
}
void deleteDiary(long id){
diaryRepository.delete(id);
}
void updateDiary(long id, String body){
diaryRepository.update(id ,body);
}
void restoreDiary(long id) { // 복구 기능 추가
diaryRepository.restore(id);
}
}
188 changes: 188 additions & 0 deletions src/main/java/org/sopt/seminar1/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
package org.sopt.seminar1;

import java.io.*;

public class Main {
public static void main(String[] args) {
final UI ui;
try {
ui = new DiaryUI(new DiaryController());
ui.runRepeatedly();
} catch (Throwable t) {

}
}

interface UI {
void runRepeatedly() throws IOException;

class UIException extends RuntimeException {
}

class InvalidInputException extends UIException {
}
}

static class DiaryUI implements UI {
private final DiaryController server;
private String selected;

public DiaryUI(DiaryController server) throws IOException {
this.server = server;
server.boot();
ConsoleIO.printLine(getStartMessage());
}

public void runRepeatedly() throws IOException {

do {
if (onMenu()) {
ConsoleIO.printLine("");
ConsoleIO.printLine(getMenu());
selected = ConsoleIO.readLine().trim().toUpperCase();
}

try {
run();
} catch (InvalidInputException e) {
ConsoleIO.printLine("잘못된 값을 입력하였습니다.");
}

if (isFinished()) {
ConsoleIO.printLine(getFinishMessage());
break;
}

selected = null;
} while (isRunning());
}

//30글자 제한
private void validateInputLength(final String input) throws InvalidInputException, IOException {
if (input.length() > 30) {
ConsoleIO.printLine("일기는 최대 30글자까지만 입력할 수 있습니다. 다시 입력해주세요.");
throw new InvalidInputException();
}
}

private void run() throws IOException {
switch (server.getStatus()) {
case READY, FINISHED, ERROR -> throw new UIException();

case RUNNING -> {
switch (selected) {
case "GET" -> {
server.getList().forEach(diary -> {
try {
ConsoleIO.printLine(diary.getId()+" : "+diary.getBody());
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
case "POST" -> {
ConsoleIO.printLine("한 줄 일기를 작성해주세요!");
final String input = ConsoleIO.readLine();
validateInputLength(input);
server.post(input);
}

case "DELETE" -> {
ConsoleIO.printLine("삭제할 id 를 입력하세요!");
final String input = ConsoleIO.readLine();
server.delete(input);
}
case "PATCH" -> {
ConsoleIO.printLine("수정할 id 를 입력하세요!");
final String inputId = ConsoleIO.readLine();

ConsoleIO.printLine("수정 body 를 입력하세요!");
final String inputBody = ConsoleIO.readLine();
validateInputLength(inputBody);
server.patch(inputId, inputBody);
}
case "RESTORE" -> {
ConsoleIO.printLine("복구할 id 를 입력하세요!");
final String input = ConsoleIO.readLine();
server.restore(input); // 복구 기능 호출
}
case "FINISH" -> {
server.finish();
}
default -> {
throw new InvalidInputException();
}
}
}

}
}

private boolean isRunning() {
return server.getStatus() == DiaryController.Status.RUNNING;
}

private boolean isFinished() {
return server.getStatus() == DiaryController.Status.FINISHED;
}

private boolean onMenu() {
return selected == null;
}

private String getMenu() {
return """
============================
- GET : 일기 불러오기
- POST : 일기 작성하기
- DELETE : 일기 제거하기
- PATCH : 일기 수정하기
- RESTORE : 일기 복구하기
""";

}

private String getStartMessage() {
return "시작합니다 :)";
}

private String getFinishMessage() {
return "종료됩니다 :)";
}
}

// not thread safe
private static class ConsoleIO {
private final static BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(System.out));
private final static BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
private final static StringBuilder sb = new StringBuilder();

public static void printLine(final String toPrint) throws IOException {
if (toPrint == null) {
throw new IllegalArgumentException("console can not print null");
}

appendLine(toPrint);
print();
clearStringBuilder();
}

public static String readLine() throws IOException {
return bufferedReader.readLine();
}

private static void appendLine(final String toPrint) {
sb.append(toPrint);
sb.append("\n");
}

private static void print() throws IOException {
bufferedWriter.write(sb.toString());
bufferedWriter.flush();
}

private static void clearStringBuilder() {
sb.setLength(0);
}
}
}