[최범균님 디자인 패턴] 객체지향과 디자인 패턴 : 객체지향
in 책과 강연 on 최범균님 디자인 패턴
절차 지향과 객체 지향
절차 지향
데이터
중심의 사고- 무분별한
getter
의 사용
- 무분별한
함수 중심의 코드 작성
객체 지향
메시지
중심의 사고
객체
객체의 핵심은 기능을 제공하는 것
어떤 데이터를 가지고 있는 지로 정의되지 않는다
객체의 정의는 그 객체가 제공해야할 기능들로 정의된다
인터페이스와 클래스
- 오퍼레이션: 객체가 제공하는 기능
시그니처
- 기능 식별 이름 (
메서드명
) - 파라미터 및 파라미터 타입
- 기능 실행 결과 값 (
리턴 타입
)
메서드 시그니쳐
많이 사용되는 단어로 다음의 두 가지 구성 요소로 구성된다
- 메서드 이름 (method’s name)
- 매개 변수 유형 (parameter types)
이렇게 정의한 이유는 오버로딩과 관련지어서 생각해야 한다
오버로딩이란 이름은 같은데 매개 변수가 다른 것이다
자바 컴파일러는 메서드 시그니처로 이름은 같은데 매개 변수가 다르면 오버로딩으로 구분한다
메시지
- 메시지를 보낸다 : 메서드를 호출한다
객체의 책임과 크기
- 객체는 자신만의 책임이 있다
예시
- 암호화 처리 객체: 제공받은 데이터를 암호화해서 다른 파일에 보내는 책임
- 파일 읽기 객체: 파일에서 데이터를 읽어와 제공하는 책임
- 파일 쓰기 객체: 파일에 데이터를 쓰는 책임
객체가 얼마나 많은 기능을 제공할 것인가
객체가 갖는 책임의 크기는 작으면 작을 수록 좋다
- 객체가 제공하는 기능의 개수가 적다는 걸 의미
객체가 갖는 책임의 크기는 작아질 수록 변경의 유연함을 얻을 수 있다
- 다시말해, 요구사항에 따른 변경이 있을 때 유연하게 대처가 가능하다
SRP : 단일 책임 원칙
객체는 단 한개의 책임만을 가진다
기능의 세부 내용이 변경될 때 변경해야할 곳이 한 곳으로 집중된다
예) 파일 읽어오는 방법을 변경할 떄
파일 읽기
책임을 가진 객체만 수정
JPA 저자 영한님 말씀:
“ 여러분 좋은 설계는요.. 변경이 될때 발견할 수 있어요
요구사항 바뀌어서 변경이 일어날 때 바꾸어야할 코드가 많다? 이건 안 좋은 설계에요 “
의존
한 객체가 다른 객체를 이용한다
- 멤버 변수로 사용한다
- 메서드 내부에서 사용한다
내 생각:
import
문에 있는 객체를 기준으로 그 객체를 의존하는 것
예를 들어 A클래스가 B클래스를 의존하면 변경에 따른 전파는 반대 방향으로 전이된다
순환 참조가 안 좋은 이유
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() 를 구현하려고 합니다.
이 때 요구사항은
고양이가 배가 고플 때만 사며,
유저가 츄르를 보유하지 않을 때만 사며,
마켓에 츄르 재고가 존재해야 합니다. 여러분들은 어떻게 코드를 작성할건가요?
나의 해결책
- 고양이 (
Cat
) - 주인 (
User
) - 마켓 (
Market
)
고객인 고양이
는 배가 고픈지 일정 시각마다 타이머가 체크하여, 배가 고플때 추르를 달라고 한다.
이 때 고양이 입장에서는 추르를 받기만 하면 될 뿐, 자신에게 추르를 주는 대상이 주인
인지 마켓
인지는 관계 없다
실제 추르는 서버인 Market
에 있지만 중간에 대리인인 (프록시) 주인이 중계자
역할로 추르를 소지하고 있다
주인은 추르를 갖고 있으면 고양이가 굳이 마켓에 갈 필요 없이 자신이 대신하여 건네준다
(물론 실제 고양이는 마켓과 거래를 할 수 없다…)
때문에 주인은 고양이의 요청에 대한 캐싱을 통한 접근 제어 역할을 한다