개요
이번 장에서는 객체지향 프로그래밍에서 핵심 개념인 협력, 객체, 클래스에 대해 소개하고, 이를 고려한 프로그램 구조와 클래스 구현에 대해 다룹니다.
협력, 객체, 클래스
객체지향 프로그래밍을 시작할 때 가장 먼저 고민해야 하는 것은 무엇일까요?
- 대부분의 사람들은 클래스를 결정한 후에 클래스에 어떤 속성과 메서드가 필요한지 고민합니다.
- 하지만 저자는 이런 방법은 객체지향과는 거리가 멀다고 말하며, 진정한 객체지향의 전환은 클래스가 아닌 객체에 초점을 맞출 때에만 얻을 수 있다고 합니다.
어떻게 하면 객체에 초점을 맞출 수 있을까요?
- 다음 두 가지에 집중하면 됩니다.
- 첫째, 어떤 클래스가 필요한지를 고민하기 전에 어떤 객체들이 필요한지 고민해야 합니다.
- 클래스는 공통적인 상태와 행동을 공유하는 객체들을 추상화한 것입니다. 따라서 클래스의 윤곽을 잡기 위해서는 어떤 객체들이 어떤 상태와 행동을 가지는지를 먼저 결정해야 합니다.
- 둘째, 객체를 독립적인 존재가 아니라 기능을 구현하기 위해 협력하는 공동체의 일원으로 봐야 합니다.
- 객체들의 모양과 윤곽이 잡히면 공통된 특성과 상태를 가진 객체들을 타입으로 분류하고 이 타입을 기반으로 클래스를 구현합니다.
추상화란?
- 객체의 복잡한 내부 동작을 단순화하고 필요한 부분에만 집중할 수 있도록 만드는 과정을 말합니다.
도메인의 구조를 따르는 프로그램 구조
- 구현에 앞서 도메인이라는 용어를 먼저 소개합니다.
- 소프트웨어는 사용자가 원하는 어떤 문제를 해결하기 위해 만들어집니다. 문제를 해결하기 위해 사용자가 프로그램을 사용하는 분야를 도메인이라 부릅니다.
객체지향 패러다임이 강력한 이유는 무엇일까요?
- 요구사항을 분석하는 초기 단계부터 앱을 구현하는 마지막 단계까지 객체라는 동일한 추상화 기법을 사용할 수 있기 때문입니다.
- 요구사항과 프로그램을 객체라는 동일한 관점에서 바라볼 수 있기 때문에 도메인을 구성하는 개념들이 앱의 객체와 클래스로 매끄럽게 연결될 수 있습니다.
클래스 구현하기
- 클래스를 구현할 때 가장 중요한 것은 클래스의 경계를 구분 짓는 것입니다.
- 외부에서는 객체의 속성에 직접 접근할 수 없도록 막고 적절한 public 메서드를 통해서만 내부 상태를 변경할 수 있게 해야 합니다.
클래스의 내부와 외부를 구분해야 하는 이유는 무엇일까요?
- 객체가 자신의 상태와 행동을 스스로 제어할 수 있게 되며, 객체 간의 의존성을 최소화하여 유연한 코드 작성이 가능해집니다. (경계의 명확성이 객체의 자율성을 보장)
- 클래스의 내부 구현을 자유롭게 변경할 수 있도록 해주어 코드 유지보수성을 향상됩니다. (프로그래머에게 구현의 자유를 제공)
상속과 다형성
컴파일 시간 의존성과 실행 시간 의존성
- 어떤 클래스가 다른 클래스에 접근할 수 있는 경로를 가지거나 해당 클래스의 객체의 메서드를 호출할 경우 두 클래스 사이에 의존성이 존재한다고 말합니다.
차이에 의한 프로그래밍
- 상속은 객체지향에서 코드를 재사용하기 위해 가장 널리 사용되는 방법입니다.
- 상속을 이용하면 클래스 사이에 관계를 설정하는 것만으로 기존 클래스가 가지고 있는 모든 속성과 행동을 새로운 클래스에 포함시킬 수 있습니다.
- 부모 클래스와 다른 부분만을 추가해서 새로운 클래스를 쉽고 빠르게 만드는 방법을 차이에 의한 프로그래밍이라 말합니다.
다형성
- 다형성이란 동일한 메시지를 수신했을 때 객체의 타입에 따라 다르게 응답할 수 있는 능력을 의미합니다.
- 다형성은 컴파일 시간 의존성과 실행 시간 의존성을 다르게 만들 수 있는 객체지향의 특성을 이용해 서로 다른 메서드를 실행할 수 있게 합니다.
추상화와 유연성
추상화의 힘
- 추상화를 사용하면 세부적인 내용을 무시하면서도 상위 정책을 쉽고 간단하게 표현 가능합니다. 이를 통해 상위 개념만으로도 도메인의 중요한 개념을 설명할 수 있게 합니다.
- 추상화를 이용해 상위 정책을 표현하면 기존 구조를 수정하지 않고도 새로운 기능을 쉽게 추가하고 확장할 수 있습니다. 이는 설계를 유연하게 만들어줍니다.
추상 클래스와 인터페이스 트레이드오프
- 구현과 관련된 모든 것들은 트레이드오프를 고려해야 합니다.
- 작성하는 모든 코드는 합당한 이유가 있어야 합니다.
- 아주 작은 결정이라도 트레이드오프를 고려하지 않으면 결과가 크게 달라질 수 있습니다.
- 결정을 내리기 전에 고민하고 트레이드오프를 고려하세요.
트레이드오프가 무엇일까?
- 트레이드오프란 어떤 것을 선택하게 될 때, 이로 인해 포기하는 것들이 생기는 것을 의미합니다.
- 예를 들어, 시간을 절약하기 위해 코드의 가독성이나 유지보수성을 희생하는 것은 트레이드오프의 하나입니다.
코드 재사용
- 상속은 코드 재사용을 위해 많이 사용되지만, 최근에는 합성이 상속보다 더 좋은 방법이라고 이야기하는 경우가 많아졌습니다.
- 합성은 다른 객체의 인스턴스를 자신의 인스턴스 변수로 포함해서 재사용하는 방법을 말합니다
- 기능적인 관점에서 완벽히 동일하나 왜 많은 사람들이 상속 대신 합성을 선호하는 이유는 무엇일까?
상속
- 상속은 코드를 재사용하기 위해 많이 사용되지만, 두 가지 관점에서 설계에 안 좋은 영향을 미칩니다
- 첫 번째는 상속이 캡슐화를 위반한다는 것입니다.
- 상속을 이용하려면 부모 클래스의 내부 구조를 잘 알고 있어야 합니다. 부모 클래스의 구현이 자식 클래스에게 노출되기 때문에 캡슐화가 약화되고, 이는 자식 클래스가 부모 클래스에 강하게 결합되도록 만들기 때문에 부모 클래스를 변경할 때 자식 클래스도 함께 변경될 확률을 높입니다. 결과적으로, 상속을 과도하게 사용한 코드는 변경하기도 어려워집니다.
- 두 번째 단점은 설계를 유연하지 못하게 만든다는 것입니다.
- 상속은 부모 클래스와 자식 클래스 사이의 관계를 컴파일 시점에 결정하기 때문에 실행 시점에 객체의 종류를 변경하는 것이 불가능합니다.
합성
- 합성은 인터페이스에 정의된 메시지를 통해서만 코드를 재사용하는 방법으로, 상속이 가지는 두 가지 문제점을 모두 해결할 수 있습니다.
- 인터페이스에 정의된 메시지를 통해서만 재사용이 가능하기 때문에 구현을 효과적으로 캡슐화할 수 있고, 의존하는 인스턴스를 교체하는 것이 비교적 쉽기 때문에 설계를 유연하게 만듭니다.
- 상속이 클래스를 통해 강하게 결합되는 것과는 달리, 합성은 메시지를 통해 느슨하게 결합되므로, 코드 재사용을 위해서는 합성을 선호하는 것이 더 좋은 방법입니다.
- 그렇다고 해서 상속을 절대 사용하지 말라는 것은 아닙니다.
- 대부분의 설계에서는 상속과 합성을 함께 사용해야 합니다.
- 코드 재사용을 위해서는 상속보다 합성을 우선시해야 하지만, 다형성을 위해 인터페이스를 재사용하는 경우에는 상속과 합성을 함께 조합해서 사용해야 합니다.
마무리
오늘은 책을 스터디하면서 중요하다고 생각한 부분을 정리해 보았습니다.
2장을 읽고 나니 몇몇 구현 방식에 대해 왜 이렇게 해야하는 걸까? 라는 질문에 대한 답이 명확해졌고, 생각 못한 부분들에 대해 다시한번 고민해보게 되서 아직 많이 부족하다는 사실을 다시 한번 느끼게 되었네요.
이번 포스팅은 마무리하면서 다음 포스팅에서 뵙겠습니다.
참고
'스터디 > 오브젝트' 카테고리의 다른 글
6장 - 메시지와 인터페이스 (0) | 2023.04.17 |
---|---|
5장 - 책임 할당하기 (0) | 2023.04.10 |
4장 - 설계 품질과 트레이드오프 (0) | 2023.03.28 |
3장 - 협력, 책임, 역할 (2) | 2023.03.12 |
1장 - 객체, 설계 (0) | 2023.02.26 |