실습 참고: 링크
package hello.hellospring.domain;
public class Member {
private Long id; //id 식별자. 고객이 정하는 id가 아닌 시스템이 저장하는 id
private String name; //이름
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;
}
}
//MemberRepository [interface]
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.List;
import java.util.Optional;
public interface MemberRepository {
Member save(Member member); //회원이 저장소에 저장이 됨
Optional<Member> findById(Long id); //저장소에서 id로 회원을 찾음
Optional<Member> findByName(String name);
List<Member> findAll(); //저장된 모든 회원 리스트 반환
}
//MemoryMemberRepository [class]
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import java.util.*;
public class MemoryMemberRepository implements MemberRepository{ //MemberRepository interface를 implement한다.
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override //재정의
public Member save(Member member) {
member.setId(++sequence); // 멤버 아이디값 세팅
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
// 널이 반환될 가능성 있기 때문에 옵셔널로 감싸서 리턴.
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore(){
store.clear();
}
}
인터페이스를 구현한 클래스들.
store
: 각 멤버 엔티티는 key-value 쌍으로 store에 저장된다. 키는 id, 값은 그에 대한 엔티티.
sequence
: 각 인스턴스 생성에 쓰일 id.
save()
: 멤버의 인스턴스를 가져와 setId(sequence)한다. 그 후, store에 저장하는데 매핑 규칙에 의해 (Id, member) 순으로 store에 put한다. 마지막으로 멤버 인스턴스 리턴.
findById()
: id를 매개변수로 받는다. id가 store에 있는 경우, 해당 멤버 인스턴스를 리턴한다. 그런데 NULL이 리턴될 가능성이 있기 때문에 Optional로 감싼 것!
findByName()
: name을 매개변수로 받는다. store에 name과 같은 멤버를 리턴한다.
store.value()
: store에 있는 모든 멤버 인스턴스를 리턴한다는 의미
store.value().stream()
: 컬렉션 크기에 따른 기존 반복문 처리의 성능 저하를 개선했다. 스트림은 컬렉션 데이터를 선언형으로 하여 중첩 없이 병렬적 처리도 가능하게 되었다. store.value().stream()은 store에 있는 모든 인스턴스에 대해 스트림을 사용하겠다는 의미.
.flter(member -> member.getName().equals(name))
: 스트림에 filter를 적용해 매개변수 name과 같은 name 필드에 대한 멤버 인스턴스를 뽑아냄.
findAny()
: Stream에서 가장 먼저 탐색되는 요소 리턴.
findAll()
: store에 매핑된 멤버 객체들을 리스트화해서 리턴. values는 여기서 키-벨류. 키는 id, 벨류는 member임.
clearStore()
: store를 비우는 것. 여러 메서드, 클래스를 테스트할 때 store 중복되는 경우 있어서 오류 뜨는 것을 방지.
// MemberService
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import java.util.List;
import java.util.Optional;
public class MemberService {
private final MemberRepository memberRepository = new MemoryMemberRepository();
/*
* 회원 가입
*/
public Long join(Member member){
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> { //result가 있으면~, 존재하면~, == 널이 아니면
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
public List<Member> findMembers(){
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId){
return memberRepository.findById(memberId);
}
}
join()
: 멤버 객체를 매개변수로 받는다. 중복성 검사 후 멤버저장소에 추가한다.validateDuplicateMember()
: memberRepository 클래스의 findByName 메서드를 사용해 중복을 체크한다. 만약 중복이 있다면 exception을 throw한다.findMembers()
: memberRepository의 findById 메서드를 사용해 repository에 있는 모든 멤버를 List로 리턴한다.findOne()
: memberId를 매개변수로 받는다. 특정 Id에 대한 findById메서드를 실행해 해당 멤버를 리턴한다. 리턴이 null이 될 수 있기 때문에 optional로 감싸서 리턴.