Skip to content
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,5 @@ out/

### VS Code ###
.vscode/

/bin/
15 changes: 15 additions & 0 deletions src/main/java/nextstep/courses/domain/Capacity.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package nextstep.courses.domain;

import java.util.Objects;

public class Capacity {
private final int value;

Expand All @@ -17,4 +19,17 @@ public int getValue() {
public boolean isFull(int registeredCount) {
return registeredCount >= value;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Capacity)) return false;
Capacity capacity = (Capacity) o;
return value == capacity.value;
}

@Override
public int hashCode() {
return Objects.hash(value);
}
}
28 changes: 22 additions & 6 deletions src/main/java/nextstep/courses/domain/Image.java
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package nextstep.courses.domain;

public class Image {
private static final int MAX_IMAGE_SIZE = 1024 * 1024;
private static final int WIDTH_LIMIT = 300;
private static final int HEIGHT_LIMIT = 200;
private static final double REQUIRED_ASPECT_RATIO = 1.5;
private static final double ASPECT_RATIO_TOLERANCE = 0.01;
private String fileName;
private String contentType;
private long sizeInBytes;
private int width;
private int height;
private static final int IMAGE_SIZE_1MB = 1024 * 1024;
private static final int WIDTH_LIMIT = 300;
private static final int HEIGHT_LIMIT = 200;
private static final double REQUIRED_ASPECT_RATIO = 1.5;
private static final double ASPECT_RATIO_TOLERANCE = 0.01;

public Image(String fileName, String contentType, long sizeInBytes, int width, int height) {
validate(contentType, sizeInBytes, width, height);
Expand All @@ -21,8 +21,24 @@ public Image(String fileName, String contentType, long sizeInBytes, int width, i
this.height = height;
}

public String getFileName() {
return fileName;
}
public String getContentType() {
return contentType;
}
public long getSizeInBytes() {
return sizeInBytes;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}

private void validate(String contentType, long sizeInBytes, int width, int height) {
if (sizeInBytes > IMAGE_SIZE_1MB) {
if (sizeInBytes > MAX_IMAGE_SIZE) {
throw new IllegalArgumentException("이미지 크기는 1MB 이하여야 합니다.");
}

Expand Down
10 changes: 9 additions & 1 deletion src/main/java/nextstep/courses/domain/PaidSession.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@ public PaidSession(Long id, String name, Period period, Image coverImage,
this.tuitionFee = new TuitionFee(tuitionFee);
}

public Capacity getCapacity() {
return maxCapacity;
}

public TuitionFee getTuitionFee() {
return tuitionFee;
}

@Override
protected void validateRegistration(Long studentId, Payment payment) {
if (registeredStudents.size() >= maxCapacity.getValue()) {
Expand All @@ -27,7 +35,7 @@ protected void validateRegistration(Long studentId, Payment payment) {
if (!studentId.equals(payment.getNsUserId())) {
throw new IllegalArgumentException("결제한 사용자와 일치하지 않습니다.");
}
if (this.tuitionFee.getAmount() != payment.getAmount()) {
if (!this.tuitionFee.isSameAmount(payment.getAmount())) {
throw new IllegalArgumentException("결제 금액과 일치하지 않습니다.");
}
}
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/nextstep/courses/domain/Period.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package nextstep.courses.domain;

import java.time.LocalDate;
import java.util.Objects;

public class Period {
private final LocalDate startDate;
Expand All @@ -21,4 +22,18 @@ public LocalDate getStartDate() {
public LocalDate getEndDate() {
return endDate;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Period)) return false;
Period period = (Period) o;
return Objects.equals(startDate, period.startDate) &&
Objects.equals(endDate, period.endDate);
}

@Override
public int hashCode() {
return Objects.hash(startDate, endDate);
}
}
43 changes: 35 additions & 8 deletions src/main/java/nextstep/courses/domain/Session.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,53 @@

import nextstep.payments.domain.Payment;

import java.util.HashSet;
import java.util.Set;
import java.util.List;
import java.util.stream.Collectors;

public abstract class Session {
protected Long id;
protected String name;
protected Period period;
protected Image coverImage;
protected SessionStatus status;
protected Set<Long> registeredStudents = new HashSet<>();
protected Students registeredStudents;

public Session(Long id, String name, Period period, Image coverImage, SessionStatus status) {
this.id = id;
this.name = name;
this.period = period;
this.coverImage = coverImage;
this.status = status;
this.registeredStudents = new Students();
}
public List<Long> getStudentIds() {
return registeredStudents.getStudents().stream()
.map(Student::getId)
.collect(Collectors.toList());
}

public Students getRegisteredStudent() {
return registeredStudents;
}

public Image getCoverImage() {
return coverImage;
}

public String getName() {
return name;
}

public Period getPeriod() {
return period;
}

public SessionStatus getStatus() {
return status;
}

public Long getId() {
return id;
}

public void register(Long studentId, Payment payment) {
Expand All @@ -26,11 +57,7 @@ public void register(Long studentId, Payment payment) {
}

validateRegistration(studentId, payment);
registeredStudents.add(studentId);
}

public boolean isRegistered(Long studentId) {
return registeredStudents.contains(studentId);
registeredStudents.addStudent(new Student(studentId));
}

protected abstract void validateRegistration(Long studentId, Payment payment);
Expand Down
30 changes: 30 additions & 0 deletions src/main/java/nextstep/courses/domain/Student.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package nextstep.courses.domain;

public class Student {
Long id;
public Student() {
}
public Student(Long id) {
this.id = id;
}

public Long getId() {
return id;
}

public boolean contains(Long studentId) {
return id.equals(studentId);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return id != null && id.equals(student.id);
}

@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}
}
33 changes: 33 additions & 0 deletions src/main/java/nextstep/courses/domain/Students.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package nextstep.courses.domain;

import java.util.HashSet;
import java.util.Set;
import java.util.Collections;

public class Students {
private Set<Student> students = new HashSet<>();

public void addStudent(Student student) {
if (contains(student)) {
throw new IllegalArgumentException("이미 등록된 학생입니다.");
}
students.add(student);
}

public void removeStudent(Student student) {
students.remove(student);
}

public boolean contains(Student student) {
return students.stream()
.anyMatch(s -> s.equals(student));
}

public int size() {
return students.size();
}

public Set<Student> getStudents() {
return Collections.unmodifiableSet(students);
}
}
21 changes: 20 additions & 1 deletion src/main/java/nextstep/courses/domain/TuitionFee.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package nextstep.courses.domain;

import java.util.Objects;

public class TuitionFee {
private final int amount;

Expand All @@ -10,7 +12,24 @@ public TuitionFee(int amount) {
this.amount = amount;
}

public int getAmount() {
public boolean isSameAmount(int amount) {
Copy link
Author

Choose a reason for hiding this comment

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

#728 (comment)
#728 (comment)
이 두 부분이 약간 상충되는 것 같아 질문드립니다.
어떤 부분은 get을 쓰고 어떤 부분은 이렇게 함수를 만드는데 구분할 수 있는 방법은 무엇일까요?

Copy link
Member

Choose a reason for hiding this comment

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

좋은 질문입니다! 👍

로직 흐름에서 도메인 객체의 책임에 해당하는 동작이라면,
객체에 메시지를 보내는 방식을 사용합니다.

예를 들어 사용자 나이 관련 로직이 있다고 가정해보겠습니다.

// 나이 증가
var age = member.getAge(); // 게터 사용
member.setAge(age + 1);

member.increaseAge(); // 메시지 전달 (게터 사용X)

// 성인 여부로 특정 기능 사용을 판단하는 로직
boolean isAdult = member.getAge() >= 19; //  게터 사용
boolean isAdult = member.isAdult(); // 메시지 전달 권장 (게터 사용X)

// 나이가 증가했는지 검증 (게터 사용)
var member = new Member(age = 10);
member.increaseAge();
member.getAge() == 11 // true 기대, 게터로 충분한 검증 가능

// 외부 응답 규격 DTO 생성 (게터 사용)
new MemberResponse(member.id, member.age);

단순히 값을 꺼내서 검증하거나, 외부로 값을 전달하기 위한 목적이라면
게터를 사용하는 것이 자연스럽다고 생각해요

Copy link
Member

Choose a reason for hiding this comment

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

그럼에도 불구하고 게터만으로 객체의 상태를 검증하기 어려운 경우,
테스트를 목적으로 하는 메서드를 public 대신 protected로 선언하는 방법도 있습니다.

보통 테스트 클래스는 테스트 대상 클래스와 같은 패키지에 위치하므로, protected 메서드에도 접근할 수 있어서요.
이러면 테스트의 편의성을 확보하면서도, 외부에서의 불필요한 접근은 제한할 수 있게됩니다 😄

Copy link
Author

Choose a reason for hiding this comment

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

@nooose 아하 이해했습니다. 도메인 객체 책임에 해당하는지를 생각해보면 되겠군요!!
상세한 설명 감사합니다 🙏

return this.amount == amount;
}

public int getValue() {
return amount;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TuitionFee)) return false;
TuitionFee that = (TuitionFee) o;
return amount == that.amount;
}

@Override
public int hashCode() {
Comment on lines +23 to +32
Copy link
Member

Choose a reason for hiding this comment

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

리뷰 반영 잘 되었네요 😄

return Objects.hash(amount);
}
}
Loading