# 회원 도메인 개발

## 구현

![](https://389280719-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LxjHkZu4T9MzJ5fEMNe%2Fsync%2F8f8837b6a74b47bbd6f60a2ade3db9eaf638a78a.png?generation=1617694489096375\&alt=media)

{% tabs %}
{% tab title="Grade.java" %}

```java
public enum Grade {
  BASIC,
  VIP
}
```

{% endtab %}

{% tab title="Member.java" %}

```java
public class Member {

  private Long id;
  private String name;
  private Grade grade;

  public Member(Long id, String name, Grade grade) {
    this.id = id;
    this.name = name;
    this.grade = grade;
  }

  public Long getId() {
    return id;
  }

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

  public String getName() {
    return name;
  }

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

  public Grade getGrade() {
    return grade;
  }

  public void setGrade(Grade grade) {
    this.grade = grade;
  }
}
```

{% endtab %}

{% tab title="MemberRepository.java" %}

```java
public interface MemberRepository {

  void save(Member member);

  Member findById(Long memberId);
}
```

{% endtab %}

{% tab title="MemoryMemberRepository.java" %}

```java
public class MemoryMemberRepository implements MemberRepository {

  // 여러 곳에서 접근할 수 있는 동시성 이슈 때문에, 실무에서는 ConcurrentHashMap을 쓰는 게 맞다.
  private static Map<Long, Member> store = new HashMap<>();

  @Override
  public void save(Member member) {
    store.put(member.getId(), member);
  }

  @Override
  public Member findById(Long memberId) {
    return store.get(memberId);
  }
}
```

{% endtab %}

{% tab title="MemberService.java" %}

```java
public interface MemberService {

  void join(Member member);

  Member findMember(Long memberId);
}
```

{% endtab %}

{% tab title="MemberServiceImpl.java" %}

```java
public class MemberServiceImpl implements MemberService {

  // 인터페이스만 가지고 있으면 구현체가 없어서 NPE가 터진다.
  // 따라서 구현 객체(MemoryMemberRepository)를 선택해줘야 한다.
  private final MemberRepository memberRepository = new MemoryMemberRepository();

  @Override
  public void join(Member member) {

  }

  @Override
  public Member findMember(Long memberId) {
    return memberRepository.findById(memberId);
  }
}
```

{% endtab %}
{% endtabs %}

## 테스트

![](https://389280719-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-LxjHkZu4T9MzJ5fEMNe%2Fsync%2Fbc6afd116029e39ba5e5f20f9998e91df6992305.png?generation=1617694488500546\&alt=media)

이제 위의 다이어그램처럼 실제 객체를 참조하도록 만들어보자.

{% tabs %}
{% tab title="MemberApp.java" %}

```java
public class MemberApp {

  public static void main(String[] args) {
    MemberService memberService = new MemberServiceImpl();
    Member member = new Member(1L, "memberA", Grade.VIP);
    memberService.join(member);

    Member findMember = memberService.findMember(1L);
    System.out.println("new member = " + member.getName());
    System.out.println("findMember = " + findMember.getName());
  }
}
```

{% endtab %}
{% endtabs %}

```
new member = memberA
findMember = memberA
```

스프링 없이 순수 자바 코드로 만들었음에도 값이 제대로 나왔다.

{% tabs %}
{% tab title="MemberServiceTest.java" %}

```java
public class MemberServiceTest {

  MemberService memberService = new MemberServiceImpl();

  @Test
  void join() {
    // given
    Member member = new Member(1L, "memberA", Grade.VIP);

    // when
    memberService.join(member);
    Member findMember = memberService.findMember(1L);

    // then
    Assertions.assertThat(member).isEqualTo(findMember);
  }
```

{% endtab %}
{% endtabs %}

매번 메인 메서드에서 실행해볼 수 없으므로 테스트 코드를 구현해보자.

## 문제점

* 만약 다른 repository로 변경할 OCP 원칙을 준수할까?
* DIP를 잘 지키고 있을까?

```java
public class MemberServiceImpl implements MemberService {

  // 추상화와 구현 모두에 의존하고 있다.
  private final MemberRepository memberRepository = new MemoryMemberRepository();
}
```

의존 관계가 인터페이스 뿐만 아니라 구현까지 모두 의존하는 문제가 있다.
