본문 바로가기

General

[오브젝트] 2장 객체지향 프로그래밍

 

사내 오브젝트 스터디에 중간에 합류하게 되었다.

객체지향이 무엇인지 아직 잘 모르지만 내가 작성한 코드를 다른 분이 이어서 작업하시는 상황이 생겼을 때, "이해하기 쉽다. 변경하기 쉽다"라고 여길 수 있는 좋은 코드 작성하고 싶다는 욕심에 참여하게 되었다.

스터디에서 활용하는 교재 <오브젝트>는, "객체에게 적절한 역할과 책임을 부여하는 방법과, 유연하면서도 요구사항에 적절한 협력을 설계하는 방법"을 전달하는 것을 목적으로 한다. 

현재 스터디 진도는 10장 이지만, 교재 전체의 흐름을 이해하고, '결합도', '응집도', '캡슐화' 등 낯선 용어에 익숙해지기 위해 2장만 따로 정리해보려고 한다. 2장에서 다루는 영화 예매 시스템 예제는 교재 전반에 이용된다고 소개되어 있어서, 그래도 2장은 미리 봐가야겠다 싶었다.

 

객체와 추상화 Abstraction

객체는 추상화 기법이다. 추상화란, 조금 더 일반적인 개념들을 표현하는 것이다. 추상화는 요구사항 정책의 표현 수준을 조절할 수 있게 해준다. 필요에 따라 세부적인 내용은 생략하고 상위 수준의 중요한 내용만을 표현해서 애플리케이션의 전체 흐름을 쉽게 파악할 수 있다. 

디자인패턴, 프레임워크 모두, 추상화를 이용해 상위 정책을 정의하는 객체지향 프로그래밍의 메커니즘을 활용해 와 같이 재사용 가능한 설계를 추구한다.

 

객체를 '지향'한다는 것의 의미

객체지향 프로그래밍 패러다임이 등장하기 전에는 데이터와 기능을 독립적으로 보고, 분리하여 관리했다. 객체지향 프로그래밍에서는 상태(state)와 행동(behavior)를 동시에 가질 수 있는 독립적인 단위를 사용한다. 이렇게 연관된 데이터와 기능을 객체 내부에 한 데 묶어 관리하는 것을 캡슐화라고 한다.

객체를 '지향'한다는 것은 다음의 두 가지에 집중하는 것을 의미한다.

  • 객체는 독립적으로 보지 말고, 협력하는 공동체의 일원으로 바라보는 것에 집중한다. 여기서 협력이란, 시스템의 기능 구현을 위해 인스턴스들이 서로 요청과 응답을 주고 받으며 상호작용 하는 것을 의미한다. 훌륭한 협력이 훌륭한 객체를 낳는다.
  • 어떤 클래스가 필요한지에 앞서 어떤 객체가 필요한지 먼저 고민한다. 어느정도 객체의 윤곽이 잡혔다면, 공통적인 상태와 행위를 가진 객체들을 분류하여 이를 기반으로 클래스를 구현하면 된다. 훌륭한 객체가 훌륭한 클래스를 낳는다.

 

'자율적인' 객체

객체지향의 핵심은 자율적인 객체들의 공동체를 구성하는 것이다. 

그런데 자율적이라는 건 무슨 뜻일까? 두 객체가 있다고 가정해보자. 한 객체가 다른 객체의 퍼블릭 인터페이스의 특정 행동을 수행하도록 요청한다면, 이 때 요청을 받은 객체는 해당 요청을 처리하고 응답을 할 것이다. 이때 요청을 받은 객체는 해당 요청의 목적을 달성할 수 있다면, 어떤 방법이든 상관없이 자율적으로 결정하여 자신만의 방법으로 그 요청을 수행할 수 있다. 이것이 객체의 자율성이다. 

객체의 자율성을 보장하기 위해서는, 클래스의 경계가 명확하게 설정되어야 한다. 다시 말해, 어떤 부분을 외부에 공개할지 또는 공개하지 않을지 명확하게 결정되어야 한다.

대부분의 객체지향 프로그래밍 언어에서는 public, protected, private과 같은 접근수정자를 사용해 공개여부를 설정할 수 있다. 이러한 접근수정자는 클래스 작성자가 클래스의 외부와 내부를 명확하게 설정할 수 있게 해준다. 접근을 제어함으로써, 각 객체를 스스로 판단하고 상태를 관리할 수 있는 자율적인 객체로 만들 수 있는 것이다. 

외부에서 접근 가능한 부분은 퍼블릭 인터페이스, 내부에서만 접근 가능한 부분은 구현이라고 부른다. 예를 들어, 한 인스턴스에서, 메서드의 가시성은 public으로, 변수의 가시성은 private,  설정해볼 수 있다. 이 때 메서드는 퍼블릭 인터페이스이고, 변수는 구현이다. 이렇게 객체의 상태는 공개하지 않고, 행동만 공개하는 방식이 일반적이다. 

클래스의 경계를 명확하게 설정되어 있다면, public 영역 외에 숨겨놓은 내부 구현은 마음대로 변경할 수 있다. 이를 구현 은닉(Implementation hiding)이라고 부르기도 한다. 변경 가능성이 있는 세부적인 구현 내용을 private 영역 안에 은닉하여, 추후 변경에 따른 혼란을 최소화할 수 있다. 클래스 작성자가 아닌 클래스 사용자 입장에서도, 실수로라도 숨겨진 부분에 접근하지 않을 수 있고, 요구사항 구현을 위해 파악해야할 지식의 총량이 줄어든다는 장점이 있다.

 

객체와 의존성 Dependency

한 클래스가 다른 클래스에게 메시지를 보내면(클래스의 객체의 메서드를 호출하면), 두 클래스 사이에 의존성이 존재한다고 표현한다.

의존성을 컴파일타임 의존성런타임 의존성으로 구분해서 생각해보자. 코드 상에서 보이는 컴파일타임 의존성과, 실행 시점에 보이는 런타임 의존성은 서로 상이할 수 있는데, 서로 다르면 다를수록 이해하기 어려운 코드가 된다. 

다시 말해, 의존성을 낮추어 재사용성과 유연성을 높이면, 이해하기 어려운 코드가 된다. 반대로 의존성을 높이고 유연성을 낮추면, 이해하고 디버깅하기는 쉬워지지만 확장가능성이 낮아진다. 

"낮은 의존성" == "유연함" == "높은 확장 가능성" == "재사용하기 쉬움" == "이해하기 어려움" == "디버깅하기 어려움"

 

예제 용어정의

  • 영화(Movie): 제목, 상영시간, 가격 정보와 같은 기본정보
  • 상영(Screening): 상영일자, 시간, 순번과 같은 정보 - 실제로 관객들이 영화를 관람하는 사건. 사용자가 예매하는 대상
  • 할인조건(Discount Condition): 할인 여부를 결정하는 규칙 (영화당 다수의 할인조건 지정 가능)
    • 순서조건(Sequence Condition): 상영 순번을 이용해 할인여부를 결정
    • 기간조건(Period Condition): 영화 상영 시작시간을 이용해 할인여부를 결정 (요일/시작시간/종료시간)
  • 할인정책(Discount Policy): 할인 요금을 결정하는 방식 (영화당 최대 하나의 할인정책만 지정 가능)
    • 금액 할인정책(Amout Discount Policy): 일정 금액을 할인
    • 비율 할인정책(Percent Discount Policy): 일정 비율의 요금을 할인

 

그 외

  • 템플릿메서드 패턴
    • 기본적인 알고리즘의 흐름을 구현하고, 중간에 필요한 처리를 자식 클래스에게 위임하는 디자인패턴
  • 오버라이딩
    • 부모 클래스에 정의된 같은 이름, 같은 파라미터 목록을 가진 메서드를 자식 클래스엣 재정의
  • 오버로딩
    • 메서드 이름은 같지만 파라미터 목록이 다른 것

 

출처: 오브젝트(코드로 이해하는 객체지향 설계) - 조영호 님