[최범균님 디자인 패턴] 객체지향과 디자인 패턴 : 객체지향

절차 지향과 객체 지향


절차 지향


  • 데이터 중심의 사고

    • 무분별한 getter 의 사용
  • 함수 중심의 코드 작성

객체 지향


  • 메시지 중심의 사고

객체


객체의 핵심은 기능을 제공하는 것


  • 어떤 데이터를 가지고 있는 지로 정의되지 않는다

  • 객체의 정의는 그 객체가 제공해야할 기능들로 정의된다

인터페이스와 클래스


  • 오퍼레이션: 객체가 제공하는 기능

시그니처

  • 기능 식별 이름 (메서드명)
  • 파라미터 및 파라미터 타입
  • 기능 실행 결과 값 (리턴 타입)

메서드 시그니쳐


많이 사용되는 단어로 다음의 두 가지 구성 요소로 구성된다

  • 메서드 이름 (method’s name)
  • 매개 변수 유형 (parameter types)

이렇게 정의한 이유는 오버로딩과 관련지어서 생각해야 한다

오버로딩이란 이름은 같은데 매개 변수가 다른 것이다

자바 컴파일러는 메서드 시그니처로 이름은 같은데 매개 변수가 다르면 오버로딩으로 구분한다

메시지


  • 메시지를 보낸다 : 메서드를 호출한다

객체의 책임과 크기


  • 객체는 자신만의 책임이 있다

예시

  • 암호화 처리 객체: 제공받은 데이터를 암호화해서 다른 파일에 보내는 책임
  • 파일 읽기 객체: 파일에서 데이터를 읽어와 제공하는 책임
  • 파일 쓰기 객체: 파일에 데이터를 쓰는 책임

객체가 얼마나 많은 기능을 제공할 것인가

  • 객체가 갖는 책임의 크기는 작으면 작을 수록 좋다

    • 객체가 제공하는 기능의 개수가 적다는 걸 의미
  • 객체가 갖는 책임의 크기는 작아질 수록 변경의 유연함을 얻을 수 있다

    • 다시말해, 요구사항에 따른 변경이 있을 때 유연하게 대처가 가능하다

SRP : 단일 책임 원칙


  • 객체는 단 한개의 책임만을 가진다

  • 기능의 세부 내용이 변경될 때 변경해야할 곳이 한 곳으로 집중된다

  • 예) 파일 읽어오는 방법을 변경할 떄 파일 읽기 책임을 가진 객체만 수정

JPA 저자 영한님 말씀:
“ 여러분 좋은 설계는요.. 변경이 될때 발견할 수 있어요
요구사항 바뀌어서 변경이 일어날 때 바꾸어야할 코드가 많다? 이건 안 좋은 설계에요 “

의존


image

한 객체가 다른 객체를 이용한다

  • 멤버 변수로 사용한다
  • 메서드 내부에서 사용한다

내 생각: import 문에 있는 객체를 기준으로 그 객체를 의존하는 것

예를 들어 A클래스가 B클래스를 의존하면 변경에 따른 전파는 반대 방향으로 전이된다

순환 참조가 안 좋은 이유


image

A, B, C 클래스가 서로 의존하고 있을 떄

변경으로 인한 영향이 자기 자기한테 올 수 있다

순환 의존이 발생하지 않도록 하는 원칙 중 하나로 DIP가 있다

의존의 양면성


어떤 요구사항에 대해서는 의존 관계의 순방향 대로 영향이 미칠 수 있다. 그에 따라 코드상 양방향 모두 바뀌게 된다

AuthenticationHandler 클래스가 Authenticator 클래스에 의존하고 있을 때
단순히 코드의 영향으로만 볼 때는 의존 방향의 역방향으로 영향이 가지만
요구사항의 종류에 따라

의존을 하고 있는AuthenticationHandler 의 변경 사항으로 인해 Authenticator 가 변경 될 수 있다

예) 로그를 남겨달라는 요구 : page. 44

캡슐화


  • 객체가 내부적으로 기능을 어떻게 구현하는지를 감추는 것

절차지향으로 작성된 코드의 문제점


  • 데이터 중심의 프로그래밍 → 데이터의 구조나 쓰임새가 변경되면 그것을 사용하는 코드들도 모두 수정해야 한다

캡슐화된 기능 구현


  • 데이터를 최대한 외부로 노출(getter)하지 않고 기능 내부에 감춘다
    • (getter) 대신 메시지를 보내라

캡슐화의 결과는 내부 구현 변경의 유연성 획득


  • 내부 구현이 변경되더라도, 기능을 사용하는 곳의 영향을 최소화 할 수 있다
    • 기능 구현 변경의 유연함을 얻을 수 있다 (내부 구현 로직을 변경하기 쉽다)

캡슐화를 위한 두 개의 규칙


  • 데이터 중심적인 코딩 멈춰 !

  • Tell, Don`t Ask

    • 데미테르의 법칙 (Law of Demeter)

Tell, Don't Ask : 데이터를 물어보지 않고 기능을 실행해달라고 요구

데미테르 법칙 : 위 사항을 따를 수 있도록 만들어 주는 규칙

  • 메서드에서 생성한 객체의 메서드만 호출
  • 파라미터로 받은 객체의 메서드만 호출
  • 필드로 참조하는 객체의 메서드만 호출

말이 어렵다 요약하면 의존하고 있는 객체의 메서드를 오직 한 번에 하나만 호출한다

예를 들어 something.getA().getB().getValue() 가 위반한 것 !

신문 배달부와 지갑


  • 신문배달부가 고객의 지갑이나 주머니를 털어서 돈을 가져가서 계산함

  • 신문배달부 입장에서 굳이 고객이 어떤 방식으로 돈을 보관하는지 알 필요가 없음, 요금을 받아가기만 하면 된다

비즈니스적인 의미로 이상함

객체지향 설계 과정

  • 기능을 찾고 세분화하여 알맞은 객체에게 할당 (최대한 캡슐화)
    • 해당 객체에 기능 구현하는 데 필요한 데이터를 추가 (순서는 바뀌어도 된다)
  • 객체 간에 어떻게 메시지를 주고받을 지 결정

케이가 냈었던 질문


문제 Cat - User - Market클래스 관계를 가진 코드가 있습니다.

여러분들은 츄르(고양이 간식)를 마켓에서 사는 기능을 가진 buyChur() 를 구현하려고 합니다.

이 때 요구사항은

  1. 고양이가 배가 고플 때만 사며,

  2. 유저가 츄르를 보유하지 않을 때만 사며,

  3. 마켓에 츄르 재고가 존재해야 합니다. 여러분들은 어떻게 코드를 작성할건가요?

나의 해결책

image

  • 고양이 (Cat)
  • 주인 (User)
  • 마켓 (Market)

고객인 고양이는 배가 고픈지 일정 시각마다 타이머가 체크하여, 배가 고플때 추르를 달라고 한다.

이 때 고양이 입장에서는 추르를 받기만 하면 될 뿐, 자신에게 추르를 주는 대상이 주인인지 마켓인지는 관계 없다

실제 추르는 서버인 Market에 있지만 중간에 대리인인 (프록시) 주인이 중계자 역할로 추르를 소지하고 있다

주인은 추르를 갖고 있으면 고양이가 굳이 마켓에 갈 필요 없이 자신이 대신하여 건네준다

(물론 실제 고양이는 마켓과 거래를 할 수 없다…)

때문에 주인은 고양이의 요청에 대한 캐싱을 통한 접근 제어 역할을 한다




© 2020.12. by 따라쟁이

Powered by philz