본 게시글은 오브젝트를 읽고 블로그 포스팅자가 정리한 글임을 밝힙니다
객체 지향 프로그래밍(OOP)
객체라는게 뭘까요?
우리가 현실 세계에서 많은 문제들을 토대로 프로그래밍하곤 합니다
예를 들어, Cafe 라는 도메인을 주제로 하게 되면
바리스타, 메뉴판, 커피, 손님 등 다양한 주체들이 존재하지요
이 주체들이 하는 행동을 소프트웨어로 표현한 것이 바로 객체입니다
보통은 주체들이 가진 Data로 설계하기 마련인데, 행동을 바탕으로 설계하는 것이
바로 객체지향적인 설계가 되는 것입니다
조금 더 쉽게 다시 설명해 볼까요?
카페에서 바리스타는 무엇을 하나요?
바로, 커피를 제조하죠!
그러면 다음과 같이 메세지를 날리는 것입니다
Baristar.makeCoffee() -> Coffee
바리스타는 손님이 요청한 주문서에 따라 커피를 만들것이고, 이 결과 값이 바로 커피가 되는 것입니다!
코드로 나타내면 이렇게요~!
class Baristar {
Coffee makeCoffee(Order order) {
return new Coffee(...);
}
}
코드로 보면 알 수 있듯이, 객체가 스스로가 주체가 되어서 행동하는 것입니다!
그럼 어떠한 기준으로 객체를 만들까요?
책에서 조영호님께서 설명해주시길
객체는 3가지 관점에서 바라보아야 합니다!
- 역할
- 책임
- 협력
이 중에서 가장 중요한 항목은 바로 책임(Resposibility)입니다
제가 앞선 OOP의 원칙 중 SOLID 원칙을 설명한 글이 있었는데요,
그 중에서 SRP(Single Responsibililty Principle)이 있었습니다
객체지향적으로 설계하기 위해서는 반드시 책임을 기준으로 객체를 분리해야 합니다
그런 다음에 객체들 간의 협력(관계)를 형성하는 것이죠 ex) 상속, 합성
책임이 많으면 많아질수록
응집도는 낮아지고, 결합도가 높아지게 됩니다ㅠㅠ
결합도가 높을 수록 변경에 취약한 코드가 되겠죠
결합도 : 객체간의 의존성이 과한 정도
응집도 : 모듈에 포함된 내부 요소들이 연관되어 있는 정도
예를 살펴보시면, 아까 카페를 언급했었죠
정말 극단적인 예지만, 모든 로직을 Cafe 라는 객체에 담게되면 어떨까요?
@Getter
@Setter
class Cafe {
private Coffee coffee;
private Barista barista;
private Order order;
private Customer customer;
}
코드 편의성 어노테이션으로 getter, setter 했습니다 ㅎㅎ
양해부탁드릴게요
위 객체의 문제점은 무엇일까요?
손님이 바뀔때마다 setter로 재설정하고
주문이 바뀔때마다 setter로 재설정하고
바리스타가 바뀔때마다 setter로 재설정하고
정말 data중심적인 설계인데요
이러한 data중심적인 설계는 확장에 닫혀있습니다
만약, Cafe를 구성하는 의자와 책상에 대한 정보를 담고 싶게 된다면?
만약, Cafe에 아르바이트생 이라는 정보가 추가된다면?
만약, 주문정책에 대한 정보가 추가된다면?
하나부터 열까지 모든 Code를 뜯어고치는 상황이 발생하게 됩니다
그렇기에 책임을 항상 생각하면서 객체를 설계해야 하는 것입니다
이 설계 방식이 바로
RDD(Responsibility Driven Design) - 책임 주도 설계 입니다!
RDD 방식으로 설계하기 위해서는
- 메세지가 객체를 결정하고
- 행동이 상태를 결정해야 합니다
여기서 메세지라는 것은 객체가 수행하는 행동을 의미하며, 행동은 메서드를 의미합니다
또한
디미터 법칙을 지키고, 객체에 묻지(getter) 말고 시키는 것이며, 명령과 쿼리를 분리하는 것입니다
하나의 함수안에 인스턴스 필드를 변경하고, 결과값을 리턴하게 되면
그 함수는 너무나 많은 일을 하고 있는 것입니다.
또한 로직 자체가 의존성이 강하게 엮이게 되는 것이죠
그래서 이를 분리하는 판단도 중요합니다!
그렇게 되면 우리는 확장에 열려있는 코드를 작성하게 되는 것이죠!
그렇다면 확장에 열려있는 코드는 무엇일까요?
변경에 유연한 코드
위에서 설명했듯이
응집도가 낮고, 결합도가 높은 코드는 변경에 매우 취약합니다
이러한 코드는 객체들간의 의존성이 중앙집권된 상태이지요
마치 위의 Cafe처럼요!
변경에 유연한 코드는 정반대의 성질을 띄고 있습니다
응집도가 높고, 결합도가 낮은 코드가 변경에 유연합니다!
또한 이러한 코드는
컴파일 타임 의존성과 런타임 의존성이 다른 코드지요
우리가 흔히 자주쓰는 인터페이스가 그 예입니다!
class Airplane {
private List<String> customerNames;
Airplane(List<String> customerNames) {
this.customerNames = customerNames;
}
}
다음과 같은 코드가 있을 때
Airplane객체를 생성함에 있어서
Airplane airplane = new Airplane(new ArrayList<String>());
Airplane airplane2 = new Airplane(new LinkedList<String>());
위와 같은 다양한 형태로 생성할 수 있죠!
컴파일 당시에는 List 인터페이스나, 런타임 단계에는 ArrayList와 LinkedList로 분리된 모습입니다!
위와 같은 설계가 확장에 유연한 설계입니다
의존성을 분리시킴으로써 상황에 맞는 대처가 가능하게끔 설계되는 것입니다!
그럼 그렇다고 해서 유연한 코드가 항상 올바를까요?
절.대.아.닙.니.다.
설계가 유연할 수록 코드를 이해하고 디버깅하는 것이 어렵습니다
반면에 유연하지 않는 코드는 이해하는 것이 쉽고 가독성이 좋죠
올바른 객체지향 설계자는 유연함과 가독성 중 잘 선택해야 합니다
설계를 할 때는 항상 Test Code를 믿고 과감하게 리팩토링해야합니다
그럼 어떠한 기준으로 리팩토링해야 할까요?
기준에 의한 리팩토링
먼저, 응집도 관련한 기준들이 몇가지 있습니다
앞서 말씀드렸듯이 응집도가 낮을수록 유연하지 않은 설계라 하였는데,
응집도가 높고 낮음을 판단하기 위한 척도가 필요하겠죠?
- 클래스가 하나 이상의 이유로 변경되야 한다면 응집도가 낮다
- 클래스의 인스턴스들을 초기화하는 시점에 경우에 따라 서로 다른 속성을 초기화 하면 클래스를 분리해야 한다
- 메서드들이 인스턴스 변수를 사용하는 정도가 높다면 응집도가 낮은 것이다
( 예시에 관한 코드는 스스로 생각해서 추후에 업로드 하겠습니다 )
다음으로는 코드를 재사용하기 위한 상속은 위험합니다
이는 반드시 리팩토링해야 될 대상입니다
왜냐하면, 부모 클래스가 바뀌면?
자식도 덩달아 수정해야 되는 불상사가 생기게 되죠 ㅠㅠ
상속은, 다형성을 위한 강력한 도구이지만 그만큼 의존성이 굉장히 강합니다
그래서 이를 합성으로 해결하는 것입니다!
합성된 객체의 필요한 메서드만 골라서 쓰는 것이죠~!
마지막으로 캡슐화(encapsulation)를 잘하는 것입니다.
단순히, 인스턴스 변수를 private으로 한다고 캡슐화가 아닙니다
여기서 말하는 캡슐화는 변하는 모든 것을 감추는 것입니다
가격에 대한 정책을 바로 객체로 구현했다고 해서
캡슐화가 된 것이 아닙니다
가격에 대한 정책은 시도 때도 없이 계속 변화하기 때문이죠
정책이 바뀌어도 로직은 유지되는 설계가 필요한 것입니다!
여담
드디어 편한 말로 쓸 수 있겠군요 ㅠㅠ
정리하게 정말 많지만 어떻게든 요약한 게시글이 되어서 퀄리티는 낮을 수 있어요ㅠ
오브젝트라는 책은 개발자가 자신을 되돌아보게되는 책인 것 같아요
객체지향설계가 중요한 것을 알면서도 업무를 처리하다보면 망각하고 절차지향적으로 짜게 되죠
그런면에서 내 자신을 되돌아보고, 훌륭한 지식들을 많이 얻게 되는 책이에요
조영호님이 직접 예시 코드와 함께 작성한 부분이 많아서
Java를 조금 다뤄봤다면 정말 이해가 쉬울거에요!
꼭 직접 구매하셔서 읽어보시는 것을 추천해요!ㅎㅎ
'Developer > 개발서적' 카테고리의 다른 글
Clean Code(클린코드) - 우리가 코드를 깨끗하게 작성해야 하는 이유 : (2) 테스트 코드도 코드다 (0) | 2019.12.17 |
---|---|
Clean Code(클린코드) - 우리가 코드를 깨끗하게 작성해야 하는 이유 : (1) 함수는 한가지 일만 제대로 하게 하라 (0) | 2019.12.11 |
Clean Code(클린코드) - 우리가 코드를 깨끗하게 작성해야 하는 이유 : 소개글 (0) | 2019.12.07 |
개발서적 - 객체지향의 사실과 오해 (0) | 2019.05.17 |