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

[10단계 - 콘솔 UI 지원] 리브(김민주) 미션 제출합니다. #176

Merged
merged 21 commits into from
Apr 29, 2024
Merged
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ab3b07c
refactor: Reservation 저장 시 DB 조회하지 않고 객체 생성하도록 수정
Minjoo522 Apr 27, 2024
d987938
refactor: 삭제할 예약이 존재하지 않을 때 Repository에서 Optional을 반환하고 Service 계층에서 …
Minjoo522 Apr 27, 2024
6c57bca
refactor: 삭제할 시간이 존재하지 않을 때 Repository에서 Optional을 반환하고 Service 계층에서 …
Minjoo522 Apr 27, 2024
e6f8113
refactor: ExceptionHandler 사용
Minjoo522 Apr 27, 2024
6ed5224
feat: MemoryReservationTimeRepository 구현
Minjoo522 Apr 27, 2024
d62e310
feat: MemoryReservationRepository 구현
Minjoo522 Apr 27, 2024
85bbef9
add: schema.sql NOT NULL 추가
Minjoo522 Apr 27, 2024
c9612a9
docs: 콘솔 UI 예시 추가
Minjoo522 Apr 27, 2024
4c548e5
feat: 메인 페이지를 보여주며 예약, 시간이 없을 때 메시지 출력 기능 구현
Minjoo522 Apr 27, 2024
9c698e0
feat: 예약 추가/삭제, 예약 시간 추가/삭제 메뉴를 선택하는 기능 구현
Minjoo522 Apr 27, 2024
5a8b0a0
feat: 예약 시간을 추가하는 기능 구현
Minjoo522 Apr 27, 2024
9c7f39a
feat: 예약 시간을 삭제하는 기능 구현
Minjoo522 Apr 27, 2024
052d2ce
feat: 예약을 추가하는 기능 구현
Minjoo522 Apr 27, 2024
5e9b9c2
feat: 예약을 삭제하는 기능 구현
Minjoo522 Apr 27, 2024
a66a28e
docs: 콘솔 UI 설명 및 사용 방법 추가
Minjoo522 Apr 27, 2024
bf661f1
feat: 콘솔 UI 종료 기능 구현
Minjoo522 Apr 27, 2024
c04c8e8
docs: 콘솔 UI 예시 변경
Minjoo522 Apr 27, 2024
4f5c0fc
style: 불필요한 공백 제거
Minjoo522 Apr 29, 2024
b0cf1d8
refactor: 출력시 multi line string 및 lineSeparator 사용
Minjoo522 Apr 29, 2024
5f01db3
refactor: else 제거
Minjoo522 Apr 29, 2024
e20a498
refactor: save시 DB 조회하지 않고 객체 만들어서 반환하도록 변경
Minjoo522 Apr 29, 2024
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
Prev Previous commit
Next Next commit
feat: MemoryReservationTimeRepository 구현
  • Loading branch information
Minjoo522 committed Apr 27, 2024
commit 6ed5224ea49127075e614bc76919f880b681239b
4 changes: 2 additions & 2 deletions src/main/java/roomescape/domain/ReservationTime.java
Original file line number Diff line number Diff line change
@@ -15,8 +15,8 @@ public ReservationTime(LocalTime startAt) {
this(null, startAt);
}

public ReservationTime(Long id) {
this(id, null);
public ReservationTime(Long id, ReservationTime reservationTime) {
this(id, reservationTime.getStartAt());
}

public Long getId() {
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package roomescape.repository;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.atomic.AtomicLong;
import roomescape.domain.ReservationTime;

public class MemoryReservationTimeRepository implements ReservationTimeRepository {

private final Map<Long, ReservationTime> reservationTimes;
private final AtomicLong index;
Copy link
Member

Choose a reason for hiding this comment

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

class 시작 부분과 필드 사이 공백은 보통 안 넣어주시던데 여기는 들어가있네요!

Copy link
Member Author

Choose a reason for hiding this comment

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

꼼꼼하게 봐주셔서 감사합니다! 👍
수정했습니다. 😆


public MemoryReservationTimeRepository() {
this.reservationTimes = new ConcurrentSkipListMap<>();
Copy link
Member

Choose a reason for hiding this comment

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

ConcurrentSkipListMap 구현체를 사용해주셨군요!
이 선택을 하신 이유가 궁금합니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

먼저 concurrent는 동기화를 위해서 사용했습니다.
concurrentMap의 하위 구현체로 ConcurrentHashMapConcurrentSkipListMap 중 하나를 고민 했습니다.

ConcurrentHashMap을 사용할 경우 키가 정렬되어 있지 않기 때문에 전체 조회 시 sorting을 해줘야 했습니다.
반면에 ConcurrentSkipListMap을 사용하면 키가 정렬되어 있어 조회 시 sorting 할 필요가 없습니다.

ConcurrentSkipListMap의 경우 ConcurrentHashMap보다 저장에 시간이 더 소요되지만 현재 기능 상 저장, 삭제보다 조회 기능을 더 많이 사용하기 때문에 ConcurrentSkipListMap을 선택했습니다. 😊

this.index = new AtomicLong();
}

@Override
public ReservationTime save(ReservationTime reservationTimeRequest) {
ReservationTime newReservationTime = new ReservationTime(index.incrementAndGet(), reservationTimeRequest);
reservationTimes.put(newReservationTime.getId(), newReservationTime);
return newReservationTime;
}

@Override
public ReservationTime findById(Long id) {
return reservationTimes.get(id);
}

@Override
public List<ReservationTime> findAll() {
return List.copyOf(reservationTimes.values());
}

@Override
public Optional<Integer> deleteById(Long id) {
if (reservationTimes.containsKey(id)) {
reservationTimes.remove(id);
return Optional.of(1);
} else {
return Optional.empty();
Copy link
Member

Choose a reason for hiding this comment

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

Optional 잘 적용해주셨네요! 👍
그런데 이 부분은 제가 '이런 방식으로도 할 수 있다'라고만 남길 의도였는데
ExceptionHandler 와 함께 설명하면서 꼭 변경해보자는 뉘앙스로 리뷰를 달았군요... 죄송합니다 🙏
Optional 을 꼭 사용하지 않으셔도 괜찮습니다!

Copy link
Member Author

Choose a reason for hiding this comment

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

감사합니다!
해피케이스라도 잘 다뤄보자는 생각에 Optional은 생각하지도 못했고 Optional을 알고는 있었지만 대체 언제 써야하나 생각했었는데 피케이 덕분에 적합한 사용 용도를 알게 되었습니다!
지금은 삭제시 사용하고 있지만 findById 조회시 사용하면 굉장히 유용할 것 같습니다!

그리고 ExceptionHandler를 사용하고 나니 코드가 훨씬 깔끔해졌고 예외 발생시 처리 방법을 한 눈에 볼 수 있어서 유용하다고 생각했습니다. 😆

}
}
}
52 changes: 52 additions & 0 deletions src/test/java/roomescape/service/ReservationTimeServiceTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package roomescape.service;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;

import java.time.LocalTime;
import java.util.Collection;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DynamicTest;
import org.junit.jupiter.api.TestFactory;
import roomescape.dto.ReservationTimeRequest;
import roomescape.dto.ReservationTimeResponse;
import roomescape.repository.MemoryReservationTimeRepository;

class ReservationTimeServiceTest {

@TestFactory
@DisplayName("예약 시간을 저장, 조회, 삭제한다.")
Collection<DynamicTest> saveReservationTimeAndFindAndDelete() {
ReservationTimeService reservationTimeService = new ReservationTimeService(
new MemoryReservationTimeRepository());

return List.of(
dynamicTest("예약 시간을 저장한다.", () -> {
ReservationTimeRequest reservationTimeRequest = new ReservationTimeRequest(LocalTime.of(10, 0));
ReservationTimeResponse newReservationTime = reservationTimeService.createReservationTime(
reservationTimeRequest);

assertAll(
() -> assertThat(newReservationTime.id()).isSameAs(1L),
() -> assertThat(newReservationTime.startAt()).isEqualTo("10:00")
);
}),
dynamicTest("예약 시간을 모두 조회한다.", () -> {
assertThat(reservationTimeService.findReservationTimes()).hasSize(1);
}),
dynamicTest("예약 시간을 삭제한다.", () -> {
reservationTimeService.deleteReservationTime(1L);

assertThat(reservationTimeService.findReservationTimes()).hasSize(0);
}),
dynamicTest("이미 삭제된 예약 시간을 삭제하려고 시도하면 예외가 발생한다.", () -> {
assertThatIllegalArgumentException()
.isThrownBy(() -> reservationTimeService.deleteReservationTime(1L))
.withMessage("존재하지 않는 시간입니다.");
})
);
}
}