일상

오브젝트 세미나(결합도가 낮은 설계)

ri5 2022. 9. 17. 17:38

복잡하게 얽힌 의존성들

결합도가 낮은 설계는 서로 의존하는 정도가 낮으면 결합도가 낮다고 한다. 하지만 이러한 추상적인 의미는 실무하는데 있어서 크게 메리트가 없는 내용이다. 내가 결합도가 높은 코드를 짰을 때에도 이러한 조건에 충족이 되기 때문이다. 아래의 예제를 보면 

public class RevervationServiceImpl implements RevervationService{
    public Receipt reservation(List<Ticket> tickets) {
    	for (Ticket ticket : tickets) {
            if(ticket.getMovieType == MovieType.WEEKEND) {
            
            } else if(movieType == MovieType.WEEKDAY) {
                ```
            } else {
                ```
            }
        }
    }
}

 

이러한 서비스 로직이 인터페이스로 구현되어 있고 구현 클래스로 구현하였다면 정통적인 관점으로는 결합도가 낮은 코드이다.

서로 알고 있는 정보가 티켓 정보말고는 없기 때문이다. 하지만 변경 관점에서 봤을 때는 이야기가 달라진다.

 

변경 관점에서 의존성이 높은 코드는 코드가 변경이 됐을 때 의존하고 있는 코드가 변경되는 정도에 따라서 의존성이 높고 낮음을 알 수 있다. 클래스 이름이 바뀔 때 임포트하고 있는 모듈이 바뀌고 메소드가 바뀔 때 리턴 타입이 바뀔 수 있다. 이처럼 변경될 수 있는 가능성이 있는 것이 의존성이 있는 것이다.

 

변경 관점에서 결합도는 이코드가 바뀔 때 의존하고 있는 코드가 얼마나 자주 바뀌는지 생각하면 쉽게 이해할 수 있다. 이코드가 10번 바뀔 때 의존하고 있는 코드도 8번 바뀐다면 2번 바뀐 코드보다 결합도가 높은 것이다. 이처럼 변경이 일어났을 때 의존하고 있는 코드가 얼마나 바뀔지 생각하고 설계한다면 결합도를 낮게 설계할 수 있다.

 

코드를 개발하다보면 그냥 구현해야하는 구현체가 있고 추상화해야하는 인터페이스가 있다. 우리는 흔히 개발할 때 추상화를 먼저 시키고 인터페이스를 선언한다음에 구현체를 구현을 한다. 그렇게 구현하다 보면 결국 인터페이스를 계속 바꾸게 된다. 이렇게 설계를 했다면 우리는 잘못된 설계로 추상화를 한 것이다.

 

결합도가 낮추는 방법은 추상화에 의존하게 설계를 하는 것이다. 추상화를 어떻게 하냐고 물어본다면 안정적인 애만 뽑아 놓고 의존성을 바뀌지 않는 안정적인 부분만 의존하도록 해야한다. 그것이 추상화이다.

 

이것은 실무적인 이야기로 풀어 내자면 내가 이 코드를 수정했을 때 다른 사람 작업하는데 있어서 영향을 주지 않았으면 좋겠다. 라는 의미로 생각하면 된다. 결국 코드는 서로 의존할 수 밖에 없기 때문에 의존성을 최대한 낮춰서 다른 사람에게 영향을 주지 않기 위해서 결합도를  낮추는 것이다. 

 

결합도를 낮추기 위한 캡슐화

그렇다면 왜 인터페이스나 private한 메소드를 숨기고 캡슐화를 하는 걸까? 방금 전 설명했듯이 인터페이스는 잘 바뀌지 않는 기능을 퍼블릭하게 외부로 노출하여 사용하도록 설계를 한다. 캡슐화는 그 반대로 자주 바뀔 수 있는 정책이나 요구사항들을 숨기는 것이다. 그래야 다른 사람이 해당 모듈을 사용했을 때 코드 변경으로 인한 피해를 입지 않을테니까

 

Getter와 Setter를 쓰지말라는 이유

게터와 세터를 사용하게 되면 우리는 해당 객체가 무슨행동을 하는지 무슨 역활인지 모르고 그냥 모든 데이터를 가져다 쓰고 바꿀 수 있는 데이터 형태의 객체 인 것이다. 방대한 시스템을 사용한다고 할 때 맥가이버의 칼처럼 다양하게 데이터를 바꿀 수 있는 객체를 쓴다면 많은 메서드들이 해당 객체에 많이 의존하게 된다는 것이다.

결합도를 낮춘 결제 설계

절차적인 코드는 프로세스와 데이터가 나뉘기 때문에 결합도가 높을 수 밖에 없다. 데이터에 의존된 기능을 구현했기 때문에 데이터가 바뀌기라도 한다면 전체적인 코드 구조를 다 건들여야하는 것이다. 객체지향은 이와달리 데이터 자체를 뒤로 미루고 행동을 먼저 설계를 한다. 
행동을 뒤로 미루게 됨으로써 데이터에 의존이 적은 설계를 하계되는 것이다. 아래 처럼 메서드를 다 정하고 구현체가 아닌 추상화된 메서드에만 의존함으로써 결합도가 낮은 설계를 하는 것이다.

 

캡슐화도 결국 자주 바뀔 수 있는 기능을 감추고 변하지 않는 부분만 외부로 노출하면서 변화로써 생길 수 있는 이펙트를 최소화하기 위해 설계를 하는 것이다. 

결론

결국 객체지향 설계는 변화하는 비지니스 로직에 대응하기 위해 나오는 이론이다. 이전에는 이렇게 만큼 인터넷 기술이 발전하지 않았기 때문에 이러한 디자인 패턴 없이 개발이 되어 왔지만 점점 소프트웨어가 고도화되고 변화함으로써 이러한 발전에 대응하기 위해 디자인 패턴이 나온 것이다. 소프트웨어가 변화함으로서 생길 수 있는 사이드 이펙트르 최소화하기 위해 나온 이론이다.

소프트웨어가 변화하지 않는 것은 죽은 것이다. 지속되는 발전에 의해 계속 비지니스 로직이 변경되고 스타트업 같이 Lean하게 일해야 하는 환경이면 더 미친 듯이 바뀔 것이다. 그렇기에 비지니스적으로 중요한 부분을 객체지향적으로 짜는 것이 좋다. 모든 코드를 객체지향적으로 코스트는 코스트대로 소모하고 결과도 좋지 않을 것이다.

 

정리 후기

디자인 패턴을 공부하기 전에 객체 지향이 무엇이고 왜 필요한지에 대해 공부를 할 수 있는 좋은 시간이였다. 개발자가 먼저 개발을 하기 전에 도메인을 공부하라는 이유도 시스템을 도메인에 맞게 잘 설계하기 위해서는 해당 서비스의 도메인을 잘 알아야 할 수 있다는 것을 알 수 있었다. 이전에는 서비스 개발을 하면서 ORM을 사용하고 있었지만 복잡한 비지니스 로직을 설계할 때 객체 지향에 대한 이해도가 낮아 JPA를 통해 할 수 있는 객체 지향적 이점을 잘 활용하지 못했었다. 하지만 스터디를 통해 객체지향과 JPA를 다시 공부하게 되면서 이전보다 더 복잡한 요구사항을 객체 지향적으로 더 쉽게 풀어내는 날 볼 수 있었다.  조영호님이 집필하신 오브젝트와 객체지향의 사실과 오해를 읽어보지는 못했지만 이번 세미나 내용 정리를 통해 흥미가 생겨 추후에 해당 책들을 구매하고 정리를 해보는 시간을 가져야겠다.