티스토리 뷰

한 줄에 점을 하나만 찍는다

 코드를 작성할 때 한 라인에 점이 여러 개 생기면, 설계에 대한 고민을 해보라는 지침이다.

숨은 의미

 단순히 라인에 존재하는 점의 개수를 헤아려 줄이라는 의미는 아니다. 점을 찍는 행위는 필드나 메소드를 통해 인스턴스에 접근하는 행위를 의미한다. 점의 개수가 많다는 것은 대상 객체의 내부에 깊이 접근하겠다는 의도를 드러내게 되고, 이는 호출자와 피호출자 사이에 강한 결합도가 형성되었다는 것을 의미한다.

예시

public class PaymentService {

    private MemberRepository memberRepository;

    public void payment(Long memberId, int accountSequenceNumber, Statement statement) {
        Member member = memberRepository.findById(memberId);
        member.getAccounts().get(accountSequenceNumber).getStatements().add(statement);
        ...
    }
}

 위 코드는 회원의 특정 계좌에 입출금내역을 추가하기 위한 로직을 담고 있다. Member 클래스의 인스턴스가 getter 메소드를 반복해 호출해, 거래내역을 표현하기 위한 Statement List 를 찾아내어 추가하는 구조로 구성되어 있다.

 

 문제는 이런 패턴을 반복해서 작성할 경우, 연계된 클래스의 레이아웃이 변경되는 순간 모든 코드에 영향을 줄 수 있다는 것이다. Member, Account, Statement 클래스가 서로 강한 결합으로 연결되어, 독립적인 클래스 인스턴스의 기능을 할 수 없도록 방해한다.

설계에 대해 고민해 보자

 클래스간의 결합도가 높아지는 가장 큰 원인은 보통 도메인 구조의 이해부족에서 시작할 때가 많다. 위 코드에서 '회원의 특정 계좌를 찾아 입출금내역을 추가한다.' 라는 문장은 요구사항으로는 자연스럽다. 그런데 문장을 조금 쪼개서 생각해 볼 필요가 있다. 보통의 뱅킹 프로세스에서는 입출금 내역을 추가하는 시점에, 해당 계좌를 알고있는 경우가 일반적이다. 설계상 회원의 몇 번째 계좌를 찾아라 라는 프로세스를 한 번에 구현할 필요가 없다는 의미이다.

public class AccountService {

    private AccountRepository accountRepository;

    public void payment(String accountNumber, Statement statement) {
        Account account = accountRepository.findByAccountNumber(accountNumber);
        account.addStatement(statement);
    }
}

 위 코드에서 Member 클래스는 보이지 않는다. 회원의 계좌번호는 이미 앞선 프로세스(조회결과 화면 또는 송금 화면)을 통해 파악이 되어있을 것이다.

 

 처음 이 원칙을 접하는 경우에 점을 하나만 찍어야 한다는 문장에 사로잡힌 나머지, 지역변수를 활용해 문장을 쪼개거나 로직을 풀어서 최대한 점을 없애기 위해 노력해야 하는 경우가 많다. 그런데 이 규칙을 지키기 위해 가장 먼저 생각해 보아야 할 것은, 요구사항과 인스턴스들의 역할이다.

객체에 메시지를 보내라

 '한 줄에 점을 하나만 사용해라' 라는 생활체조 원칙은 디미터의 법칙이 이야기하는 '낯선 사람과 대화하지 말아라. 친구하고만 대화해라' 라는 문장과 통하는 내용이다. 좋은 객체지향 설계일수록 getter와 setter의 사용을 지양하게 된다.

 

 getter 또는 setter를 호출하는 행위는 점을 찍음으로써, 객체의 내부 필드에 접근하도록 한다. 이는 현재 사용중인 객체와 대화하지 않고, 내부에 존재하는 다른 객체에 접근해 대화를 하겠다는 것을 의미한다. 

public class AccountService {

    private AccountRepository accountRepository;

    public void payment(String accountNumber, Statement statement) {
        Account account = accountRepository.findByAccountNumber(accountNumber);
        account.addStatement(statement); // O
        account.getStatements().add(statement); // X
    }
}

 클래스의 필드에 접근할 때 List나 Map과 같은 Collection 클래스를 getter로 불러와 처리를 하는 경우가 많다. 이렇게 자료구조에 접근을 할 때에도 생활체조 원칙을 위반하는 경우가 많은데, 이런 경우에는 일급컬렉션과 같은 도메인 오브젝트 설계를 활용하는 것이 좋다.

마치며

 객체지향 생활체조 원칙에 대해 작성할 수록 앞부분에서는 코딩스킬을 개선해준다는 느낌을 받았다면, 뒤로 갈수록 근본적인 설계에 대한 고민을 하게 되는 것 같다. 처음에 도메인과 요구사항에 대한 충분한 고민을 통해, 객체의 역할과 책임을 잘 나누어 본다면, 좋은 설계를 기반으로 하는 깔끔한 코드를 작성할 수 있을 것이다.

댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday