[개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴]을 읽고 정리한 글 입니다.
[1] 절차 지향과 객체 지향
절차 지향 (Procedural Oriented)
- 프로시저(procedure)로 프로그램을 구성하는 기법
- 각 프로시저들이 데이터를 사용하여 기능 구현
- 필요에 따라 다른 프로시저 사용
- 여러 프로시저가 동일한 데이터 공유
- 구현은 쉽지만, 이후 프로그램 규모가 커짐에 따라 변화된 요구사항 반영이 어려움 (코드의 수정이 어렵)
객체 지향 (Object Oriented)
- 데이터와 프로시저를 객체(Object) 단위로 묶음
- 객체는 다른 객체에 기능을 제공하기 위해 프로시저 사용
- 프로시저는 자신이 속한 객체의 데이터에만 접근 가능 다른 객체에 속한 데이터에는 접근 불가
- 구현 및 설계는 다소 어렵지만, 변화된 요구사항 빠르게 반영 가능 (프로그램 수정이 상대적으로 쉽다)
[2] 객체 (Object)
객체의 핵심: 기능 제공
- 실제로 어떻게 해당 기능을 구현한지는 알 수도 없고, 알 필요도 X
- Just 어떠한 기능을 제공한다는 사실이 중요할 뿐!
인터페이스와 클래스
- 오퍼레이션(operation): 객체가 제공하는 기능
- 인터페이스(interface): 객체가 제공하는 모든 오퍼레이션 집합
- 시그니처(Signature)
- 기능 식별 이름
- 파라미터 및 파라미터 타입
- 기능 실행 결과 값
- 타입(type): 서로 다른 인터페이스를 구분할 때 사용되는 명칭
메시지
- 오퍼레이션 실행을 요청(request)하는 것을 ‘메시지(message)를 보낸다.’고 표현
[3] 객체의 책임과 크기
- 객체의 책임(responsibility): 객체가 제공하는 기능
- 객체가 책임을 갖는다 == 객체가 역할(role)을 수행한다.
- 한 객체가 갖는 책임을 정의한 것: 타입/인터페이스
- 객체별 책임 할당은 어떻게..?
-
프로그램을 만들기 위해 필요한 기능 목록을 정리하라!
-
이 기능을 어떻게 객체에 분배할지 고민하라
이 때, 객체가 갖는 책임의 크기는 작을수록 좋다
⇒ 단일 책임 원칙
(Single Responsibility Principle; SRP)
: 변경의 유연함을 얻기 위한 가장 기본 원칙
[4] 의존
- 한 객체가 다른 객체를 생성하거나 다른 객체의 메서드를 호출할 때, 이를 그 객체에 의존(dependency)한다고 표현함.
- 파라미터로 전달받는 경우도 의존으로 볼 수 있음
- 다른 타입에 의존한다? ⇒ 의존하는 타입에 변경이 발생할 때 나도 함께 변경될 가능성이 높다
- 순환의존이 발생하지 않도록 해야함 ⇒
의존 역전 원칙
(Dependency inversion principle; DIP)
의존의 양면성
- 의존은 클래스 상호간에 영향을 줌
- 내가 변경되면 나에게 의존하고 있는 코드에 영향을 준다.
- 나의 요구가 변경되면 내가 의존하고 있는 타입에 영향을 준다.
[5] 캡슐화
- 객체지향의 장점: 한 곳의 구현 변경이 다른 곳에 변경을 가하지 않도록 해준다 ⇒ 캡슐화를 통해 해당 장점 살림
- 캡슐화(encapsulation): 객체가 내부적으로 어떻게 구현하는지를 감추는 것
절차 지향 방식 코드
- 절차 지향 방식은 데이터를 직접적으로 사용하기 때문에, 데이터의 구조나 쓰임새가 변경되면 이로 인해 데이터를 사용하는 코드들도 연쇄적으로 수정이 발생함.
캡슐화 된 기능 구현
- 구조나 쓰임새가 변경되더라도 내부 구현만 변경될 뿐, 기능을 사용하는 코드는 변경되지 않는다.
캡슐화의 결과는 내부 구현 변경의 유연성 획득
- 앞선 내용에 따르면, 캡슐화를 통해서 내부 기능 구현 변경의 유연함을 얻을 수 있음
캡슐화를 위한 두 개의 규칙
-
Tell, Don’t Ask
- 데이터를 물어보지 않고, 기능을 실행해 달라고 말하라.
-
데미테르의 법칙(Law of Demeter)
- 메서드에서 생성한 객체의 메서드만 호출
- 파라미터로 받은 객체의 메서드만 호출
- 필드로 참조하는 객체의 메서드만 호출
public void processSome(Member member) {
if (member.**getDate().getTime()** < ... ) {
...
}
}
파라미터로 전달받은 member의 getDate() 메서드를 호출한 뒤, 다시 getDate()가 리턴한 Date 객체의 getTime() 메서드 호출함 ⇒ 데미테르 법칙 위반
- 데미테르 법칠을 지키지 않는 전형적인 증상
-
연속된 get 메서드 호출
value = someObject.getA().getB().getValue();
-
임시 변수의 get 호출이 많음
A a = someObject.getA();
B b = a.getB();
value = b.getValue();
코드가 흩어져 있을 경우 발견하기 어려울 수 있음
[6] 객체 지향 설계 과정
- 객체 지향 설계의 단계
- 제공해야 할 기능을 찾거나 세분화하고, 그 기능을 알맞은 객체에 할당한다.
-
기능을 구현하는데 필요한 데이터를 객체에 추가한다.
객체에 데이터를 먼저 추가하고 그 데이터를 이용하는 기능을 넣을 수도 있다.
-
기능은 최대한 캡슐화해서 구현한다.
- 객체 간에 어떻게 메시지를 주고받을 지 결정한다.
- 과정1과 과정2를 갭라하는 동안 지속적으로 반복한다.