ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • '오브젝트' 책 보고 공부하기 - ⑥ 메시지와 인터페이스
    JAVA공부/JAVA 2023. 6. 6. 01:40

    1) 협력과 메시지

     

    협력안에서 메시지를 전송하는 클라이언트, 메시지를 수신하는 객체를 서버라고 표현하는 클라이언트 - 서버 모델이있다.

    Switch는 클라이언트 Hitter는 서버이다. Swtich가 Hitter에 온도를 높이라고 요청하면 Hitter는 열에너지를 주게 된다.

    이 같은 경우는 우리가 Rest API 통신을 통해 많이 겪게 되는 상황인데, 프론트와 백엔드가 분리된 서버에서

    자주 마주칠 수 있는 상황이다. 

    Hitter는 또 다른 객체에게 클라이언트 입장으로 존재할 수 있다. 가령 전기에너지를 얻기 위해 다른 객체를 찾을 수 있을 것이다.

     

    조금 더 깊이 알기 위해 용어 정리를 하자.

     

    메시지: 객체들이 협력하기 위해 사용할 수 있는 의사소통 수단

    다른 객체에게 도움 요청하는 것을 메시지 전송 혹은 메시지패싱 이라고 부른다.
    이때 메시지 전송하는 객체를 메시지 전송자 라고 하며, 수신하는 객체를 메시지 수신자라고 한다.

    메시지는 오퍼레이션명인자로 구성 되어있다.

    hitter.raiseTemperature(count);

    hitter는 수신자, raiseTemperature는 오퍼레이션명, count는 인자이다.

     

    퍼블릭 인터페이스: 객체가 의사소통을 위해 외부에 공개하는 메시지 집합

    오퍼레이션: 퍼블릭 인터페이스에 포함된 메시지 (내부 구현코드를 제외한 메시지 관련된 시그니처) - ex] raiseTemperature

    메서드: 메시지 수신시 실제 실행되는 코드

    시그니처: 오퍼레이션(혹은메서드) 이름과 파라미터 목록을 합친 것 (실행코드 제외)

     


    2) 인터페이스와 설계품질

     

    꼭 필요한 오퍼레이션만을 인터페이스에 포함하는 것을 최소한의 인터페이스
    어떻게가 아닌 무엇을 하는지를 표현하는 추상적인 인터페이스 가 될 수록 좋은 인터페이스가 될 수 있다.

     

    퍼블릭 인터페이스의 품질에 영향을 미치는 여러가지 원칙과 기법

     

    디미터 법칙


    "오직 하나의 도트(.)만 사용하라" 라는 말로 요약된다. 확실히 일리가 있는게, 한번만 사용하게 되면 내부탐색을 통해

    구현을 하지 않으므로 훌륭한 억제제가 될 수 있을 것 같다. (결국 메시지화 하라는말과 일맥 상통하게 된다.)

     

    묻지말고 시키기

     

    객체의 상태에 대해 묻지 말고 시키라. 상태 기반으로 결정을 내리면 후에 객체의 상태를 변경시킨다면?? 

    개발 하다가 자주 있었던 일인데, 어디서 값이 변경된건지 찾기 위해 헤맨적이 한 두 번이 아니다.

    상태를 묻는 오퍼레이션보다 행동을 요청하는 오퍼레이션으로 대체해서 인터페이스를 향상시키자.

     

    의도를 드러내는 인터페이스

     

    내부의 구현방법을 드러내지 말자. 무엇을 하는지 알려주자

    (나도 메서드 이름을 지을때 By~~라는 것을 써서 내부구현내용을 최대한 알려주었는데, 생각해보니 더 복잡하게 이해할 수도 있을 것같다.)

    내부 구현이 바뀌게 되면 메서드 이름도 거기에 맞게 바뀌어야한다는 불필요함이 존재한다.

     


     

    3) 원칙의 함정

     

    설계는 트레이드오프의 산물이다. 적용하려는 원칙과 기법들을 모든 사례에 억지로 끼워맞추려고 한다면 부적합한 상황에서도 무질서한

    코드가 생성될 수 있다. 원칙을 어느부분에서 적용하고 적용하지 않을까, 어느부분에서 적용하면 유용하게될까를 고민하자.

     

    디미터의 법칙중. 하나의 도트만을 사용하라는 것은 list.stream.filter.~~~ 와 같은 곳에서도 적용하라는 것이 아니다.

    이 메서드 체이닝은 동일한 클래스의 인스턴스를 반환한다. builder패턴의 경우에도 동일하다.

    이처럼 기차 충돌과 같은 코드여도 외부에 정보를 노출하지 않는다면 디미터 법칙을 준수한 것이다.

     

    묻지않고 시켜라가 적용 될 경우가 있는데 자료구조의 객체의 경우 내부를 노출해야하는데, 결국에 내부정보를 Public하게 공유해야한다.

    객체에 시키는 것이 항상 가능하지 않다. 가끔은 물어보아야한다.

     

     


    4) 명령 - 쿼리 원칙

     

    필요에 따라 물어야 한다는 사실을 이해한다면 명령-쿼리 원칙을 알아보자.

    용어부터 알아보자.

    루틴: 어떤 절차를 묶어 호출 가능하도록 이름을 부여한 기능 모듈

    프로시저: 정해진 절차에 따라 내부 상태를 변경하는 루틴의 한 종류

    함수: 정해진 절차에 따라 필요한 값을 계산해서 반환하는 루틴의 한 종류

    명령 - 프로시저와 동일한 개념으로 보고 쿼리 - 함수와 동일한 개념으로 본다.

     

    명령과 쿼리를 분리해서 얻는 장점이 무엇일까??

    가장 큰 장점은 코드가 예측하기 쉬워진다는 것이다.

    가령 코드를 보면

    public int findItemNo(Someclass someclass) {
      someClass.setItemNo(5);
      return someClass.getItemNo();
    }

    분명 아이템 이름을 찾으라고 메서드를 명명해놓고 파라미터를 받는 객체의 상태를 변경한 후 이 값을 리턴한다.

    그렇다면 이 someClass 아무도 모르게 상태변경이 되어버린 것이다.

    이처럼 메서드를 통해 예측할 수 없는 결과가 나온다면 버그가 발생할 수도 있고 디버깅하기 위해 메서드들을 샅샅히 찾아보아야 한다.

     

    분리를 하자

    public void changeItemNo(SomeClass someClass, int itemNo) {
      someClass.itemNo = itemNo;
    }
    public int changeItemNo(SomeClass someClass) {
      return someclass.getItemNo();
    }

    물론 위의 코드들은 내부 구현을 통해 getter와 setter방식으로 짤 수 있겠지만. 요점은 메서드가 구현하는 기능에 대해서 명령 쿼리로 

    분리하자이다.

     

    컴퓨터에서 명령은 부수효과(중간에 상태가 변하는)를 가져다 주는데, 이 부수효과가 아예 존재하지 않다면 불변성(상태가 변하지 않음)

    만족하며 참조 투명성 즉, 해당 기능에 어디에 입력되어도 같은 결과를 반환하는 결과를 주게된다.

    객체지향은 상태변경이라는 부수효과를 기반으로 생각하기 때문에 참조투명성은 예외에 가깝다.

    그러나, 명령-쿼리 원칙으로 이 간극을 최대한 줄일 수 있다.

     

    참고*

    부수효과를 기반으로 하는 프로그래밍 방식을 명령형 프로그래밍

    부수효과가 존재하지않는 수학적인 함수를 기반으로 하는 함수형 프로그래밍이 존재한다.

     

    위에서 말했던 모든 내용들은 책임에 초점을 맞춰서 메시지를 먼저 선택하여 설계를 하게 된다면 지킬 수 있는 것들이다.

    책임을 우선시하여 설계를 시작하되 위의 원칙과 기법들을 생각하며 한번 다듬는것으로 생각한다면 좋은 설계가 나올 것 같다.

Designed by Tistory.