Effective Java : 아이템26. 완벽공략 (GenericRepository)

    들어가기 전

    이 글은 인프런 백기선님의 강의를 복습하며 작성한 글입니다. 


    이 글의 요약

    • Generic을 이용해서 반복되는 부분을 줄이면 코드의 전체 양이 줄어든다
    • 코드의 전체 양이 줄어들면 유지보수 해야하는 영역이 줄어들기 때문에 손쉽게 유지보수 할 수 잇다. 

    완벽공략

    • p156, 마이그레이션 호환성을 위해 로 타입을 지원하고 제네릭 구현에는 소거 방식을 사용하기로 했다. (아이템 28)
    • p158, 제네릭 메서드 (아이템 30)
    • p158, 한정적 와일드카드 타입 (아이템 31)
    • Generic DAO 만들기 + GenericRepository 만들기

    이번 챕터에서 완벽공략은 위와 같이 나온다. 하지만 실제로는 다음 아이템을 공부하면서 작성할 수 있는 녀석들이기 때문에 여기서는 Generic Repository만 생성하도록 한다. 


    Generic Repository 만들기 

    여기서는 Generic을 이용해서 중복된 코드를 줄여주는 방법을 살펴보고자 한다. 코드는 다음과 같다.

    • Entity 인터페이스가 존재한다.
    • Entity 인터페이스 구현체 Account / Message가 존재한다.
    • Account / Message에 대한 AccountRepository / MessageRepository가 존재한다. 

    여기서 중요한 부분은 AccountRepository, MessageRepository의 코드는 거의 동일하다. 아래에서 볼 수 있듯이 전달되는 객체의 타입만 바꾸고 필드명만 살짝 바꾸면 된다. 틀린 부분은 엔티티에서 꺼내는 필드명이 다른 형태가 존재할 것이다. 이런 형태의 코드 구성에서는 제네릭 + 리플렉션을 이용해서 코드를 극단적으로 줄일 수 있다. 

    public class AccountRepository {
    
        private Set<Account> accounts;
    
        public AccountRepository() {
            this.accounts = new HashSet<>();
        }
    
        public Optional<Account> findById(Long id) {
            return accounts.stream().filter(a -> a.getId().equals(id)).findAny();
        }
    
        public void add(Account account) {
            this.accounts.add(account);
        }
    }
    
    
    public class MessageRepository {
    
        private Set<Message> messages;
    
        public MessageRepository() {
            this.messages = new HashSet<>();
        }
    
        public Optional<Message> findById(Long id) {
            return messages.stream().filter(a -> a.getId().equals(id)).findAny();
        }
    
        public void add(Message message) {
            this.messages.add(message);
        }
    
    }

    제네릭 및 리플렉션을 이용해서 코드를 줄이면, 전체 생성된 코드의 양이 매우 작아진다. 코드 양이 줄어든다는 것은 유지보수 해야할 부분이 적어지기 때문에 좀 더 편리하게 유지보수를 할 수 있게 된다. 


    Generic Repository의 생성 및 사용 방법

    • 기존의 AccountRepository를 가져와서 제네릭을 사용하도록 변경했다.
    • 이 때, 아무 타입이나 오면 안되기 때문에 한정적 타입 매개변수를 이용해서 Entity 하위 클래스만 들어오도록 했다. 
    public class GenericRepository<E extends Entity> {
    
        private Set<E> entities;
    
        public GenericRepository() {
            this.entities = new HashSet<>();
        }
    
        public Optional<E> findById(Long id) {
            return entities.stream().filter(a -> a.getId().equals(id)).findAny();
        }
    
        public void add(E entity) {
            this.entities.add(entity);
        }
    }

    사용할 때는 아래와 같이 Generic Repository를 상속받아서 사용하면 된다. 아래 코드는 Schdule이라는 엔티티가 추가되었을 때, Generic Repository를 이용해서 SchduleRepository를 구현한 케이스를 보여준다. 아주 극단적인 경우지만 ScheduleRepository는 상속 받은 부분 말고는 새롭게 생성된 코드가 없다. 

    public class ScheduleRepository extends GenericRepository<Schedule>{
    }
    

    댓글

    Designed by JB FACTORY