ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • '오브젝트' 책 보고 공부하기 - ⑨ 유연한 설계
    JAVA공부/JAVA 2023. 6. 18. 19:05

    1) 개방 폐쇄 원칙

    개방폐쇄의 원칙의 정의는 다음과 같다.

    "소프트웨어 개체(클래스, 모듈, 함수,등등)는 확장에 대해 열려 있어야 하고, 수정에 대해서는 닫혀 있어야 한다."

     

    여기서 말하는 수정과 확장에 대한 얘기는  런타임의존성, 컴파일의존성에 대한 이야기이다.

    단순히 해당클래스의 메서드명, 필드명등에 대한 이야기가 아니라.

     

    컴파일시점에 추상화 클래스에 의존해서 런타임 의존성에 의해 확장에 대한 유연성을 높이자는 이야기이다.

    변하지 않는 부분과, 변하는부분을 나누고 변하는 부분을 추상화하는 것이 개방폐쇄 원칙을 지키는 핵심이 된다.

     

     


     

    2) 생성 사용 분리

     

    생성하는 클래스와 사용하는 클래스를 분리하자.

    객체를 직접 생성하는 것은 결합도가 높아질 수 있지만, 객체 생성을 피할 수는 없다.

     

    객체 생성을 클라이언트로 옮기거나, 클라이언트 마저 책임을 옮기고 싶다면,

    클라이언트에서 객체를 생성할 클래스를 만드는 것이다.

    이를 순수한 가공물(PURE FABRICATION)이라 하는데

    사실 이 객체는 도메인개념과 분리되어있으며, 기계적인 개념으로 들어간다.

     

    추상화를 이용해 애플리케이션을 구축하고, 도메인을 추가하다가 해당 개념으로 되지 않는 경우 인공적인 객체를 창조하자.

    실세계를 모방해야한다는 것에 억지로 이끌릴 필요가 없다.

    우리가 애플리케이션을 만드는 것은 사용자들이 원하는 기능을 제공하기 위해서이며, 이는 실세계를 모방, 시뮬레이션

    하기위함이 아니다.

     

     


     

    3) 의존성 주입

     

    외부의 독립적인 객체가 인스턴스를 생성한 후 이를 전달해서 의존성을 해결하는 방법을 의존성 주입이라고 한다.

    주입방법에는 3가지가있다.

    - 생성자 주입

    사용클래스 생성시 생성자의 인자로 전달

    - setter 주입

    이미 생성된 객체에 대해 setter메서드를 이용해 의존성 주입

    - 메서드 주입

    메서드 호출 시 메서드 인자로 의존성 주입

     

    그외 방법으로는 SERVICE LOCATOR라는 방법이 존재한다.

    이 방법은 의존성을 해결할 객체들을 보관하는 일종의 저장소를 만드는 방법인데,

    의존성을 숨기기 때문에 (SERVICE LOCATOR 객체 내부에 무슨 의존성을 만드는지 확인)

    단위테스트도 작성도 어렵고, 어디서 의존성을 생성하고, 무엇을 생성하는 지, 해당 의존성을 변경하기가 어렵게 된다.

    즉, 숨겨진 의존성이 발생하여 코드 내부구현을 이해해야하며 캡슐화를 위반하게된다.

    되도록이면 지양하는 것이 좋지만, 의존성 주입을 사용하지 못하는 경우나 깊은 호출계층에 계속해서 동일한 객체를

    전달해야하는 경우 어쩔 수 없이 사용하는 경우도 있다.

     

    핵심은 명시적인 의존성이 숨겨진 의존성보다 좋다는 것 이다.

     


     

    4) 추상화와 의존성 역전

     

    추상화를 통해 의존하자. 하위 클래스 수준에 의존하게 되면 하위 클래스 변경시에 이를 의존받는 상위 수준의 클래스가

    영향받는 것을 방지할 수 있다. 구체 클래스는 의존성이 시작점이지 목적지가 되서는 안된다.

     

    의존성 역전 원칙(Dependeny Inversion Principle, DIP)은 다음과 같다.

    1. 상위 수준의 모듈은 하위수준의 모듈에 의존해서는 안된다. 모두 추상화에 의존하여야 한다.

    2. 추상화는 구체적인 사항에 의존해서는 안된다. 구체적인 사항이 추상화에 의존해야한다.

     

    의존성 역전원칙은 인터페이스 소유권에 대해서도 이야기 할 수 있는데,

    추상화된 클래스에 대해서만 패키징하고, 재사용될 필요가 없는 클래스를 별도의 독립적인 패키지에 모아놓는다면,

    특정한 컨텍스트로 부터 완벽하게 독립시킬 수 있다. (마틴파울러가 말한 SEPARATED INTERFACE패턴)

     

    전통적인 설계 패러다임은 인터페이스의 소유권을 클라이언트 모듈이 아닌 서버 모듈에 위치시키고

    객체지향 애플리케이션은 클라이언트에 위치시킨다.

     

     


     

    5) 유연성에 대한 조언

     

    무조건 유연하고, 재사용 가능한 설계가 항상 좋지만은 않다. 단순하고 명확한 설계를 가진 코드는 읽기 쉽고

    이해하기도 편하다. 

    어떤 변경에 대비하기 위해 설계를 복잡하게 만들었는가? 유연성이 필요한가 - 제한된 정보에서는 심리학에 가깝다.

     

    변경은 예상이 아니라 현실이 되어야 한다.

    미래에 변경이 일어날 수도 있다는 막연한 불안감은 불필요한 복잡한 설계를 낳는다.

     

    설계가 유연할수록 클래스 구조와 객체 구조사이의 거리가 멀어지며, 단순성과 명확성이 희생된다.

     

    설계를 유연하게 만들려면 역할, 책임, 협력에 초점을 맞춰야한다.

    객체들의 메시지 전송자의 관점에서 동일한 책임을 수행하는지 여부를 판단한 후 공통 추상화를 도출 할 수 있다.

     

    너무 성급하게 객체 생성부터 진행하지 말자. 

    객체 생성과 관련된 불필요한 세부사항이 객체에 결합된다.

    책임을 할당하고 마지막 단계로 사용할 책임을 가진 객체와 생성을 책임질 객체를 만들자.

    이를 무시하고 객체를 성급하게 생성하게되면, 설계동안 머릿속에 기억해야할 객체 수가 너무 많아져서

    설계가 완료되기 까지 오랜시간이 걸릴 수 있다.

     

Designed by Tistory.