[C++] OOP 디자인의 개념
🌈 프로그래밍/C++

[C++] OOP 디자인의 개념

반응형

 

안녕하세요? 수구리입니다!

 

오늘의 포스팅은 오랜만에 다시 C++ 책으로 돌아왔습니다!

 

이번 포스팅을 읽고 나시면 객체 지향이 뭔지에 대한 감이 잡히실 겁니다!

 

즉, C++에만 해당되는 내용이 아니라,

 

객체 지향 관련 언어를 공부하시는 분들도 읽어봐도 좋을 것 같습니다~

 

 

  • 이번 장에서는 객체 개념을 디자인에 적용하는 방법에 대하여 알아보자!
  • 먼저 C 언어에서 따르던 절차형 프로그래밍부터 간략히 살펴본 다음, 객체지향 프로그래밍을 알아볼 예정이다.
  • 이 장에서는 지루하더라도 코드보다는 개념을 중심으로 설명

5.1 절차형 사고방식

  • 프로시저는 일종의 추상화 기법으로 프로그래머를 비롯한 여러 사람이 코드를 읽기 쉽게 한다.
  • 이 프로그램은 무슨 일을 하지?라는 기본 질문을 바탕으로 답을 자연어로 생각한다면, 절차적으로 생각하기 마련이다.
  • 일정 단계에 따라서 진행하는 프로그램을 작성하려면 절차형 접근 방식이 적합하다.
  • 하지만, 대규모 프로젝트 or 많은 이벤트가 발생하는 프로그램에서는 부적합.

5.2 객체지향 철학

  • 객체지향의 접근 방식 : 현실 세계의 어떤 대상을 모델링 하지?이다.
  • 물리적인 대상에 대한 모델 단위로 나눈다.
  • 물리적인 대상을 클래스, 컴포넌트, 프로퍼티(속성), 동작의 관점에서 분석

5.2.1 클래스

  • 어떤 대상의 유형을 정의하는 속성을 정리한 것
  • 객체란 어떤 클래스에 속하는 구체적인 인스턴스이다.

5.2.2 컴포넌트

  • 현실에 존재하는 복잡한 대상은 사실 수많은 컴포넌트로 구성되어 있다.
  • 절차형 프로그래밍 : 복잡한 작업을 작은 프로시저 단위로 쪼갬
  • 객체지향 프로그래밍 : 작은 컴포넌트 단위로 구분하는 사고방식
  • 컴포넌트는 본질적으로 클래스와 같음. 클래스보다는 작지만 구체적이라는 점이 다름.

5.2.3 프로퍼티

  • 객체는 프로퍼티(속성)로 구분한다.
  • 클래스 프로퍼티의 값은 그 클래스에 속한 모든 객체가 똑같이 가지지만, 객체 프로퍼티의 값은 그 클래스에 속한 객체마다 다르다.
  • 즉, 프로퍼티는 객체의 특성을 표현한다.

5.2.4 동작

  • 동작(행위) 객체가 하는 일 또는 그 객체로 할 수 있는 일을 표현
  • 동작도 프로퍼티와 마찬가지로 클래스 관점과 객체 관점으로 구분됨.
  • OOP에서는 한 기능을 수행하는 코드를 프로시저가 아닌 클래스의 단위로 묶는다.
  • 클래스의 동작은 클래스 메서드로 구현한다.

5.3 객체 관점에서 바라보기

  • 소프트웨어를 객체 관점으로 개발하는 데 적용할 수 있는 접근 방식

 

1. 프로그램 전반에 가독성과 유지보수성을 높이는 방법

- 객체를 단순히 데이터와 기능을 묶어주는 수단으로 여긴다.

 

2. 처음부터 모든 것을 객체로 표현하는 방법
- 다소 추상적인 개념을 표현해야 하는 경우가 있음.

 

  • 가장 바람직한 방법은 위의 두 가지를 적절히 조합해야 함.
  • 절차형 프로그램에 객체가 가미된 것처럼 보일 수 있다.
  • 반대로 너무 극단적인 방법으로 OOP만 추구하여 int 마저도 객체로 표현할 수도 있다.
  • 결국 타협점을 찾아야 함.

5.3.1 과도한 객체화

  • 객체지향 시스템을 창의적으로 디자인하는 것과 사소한 것까지 객체로 만드는 것은 매우 다르다.
  • 객체는 어디까지나 프로그래머가 코드를 관리하는 데 도움을 주기 위한 것임을 명심하자!
  • 단지 구체적인 이유 없이 객체지향적으로 보이기 위해서 객체를 사용하는 것은 바람직하지 않음.

5.3.2 지나친 일반화

  • 객체를 너무 일반화하는 것도 문제가 될 수 있다.
  • 만들 필요가 없는 것까지 만드는 '과도한 객체화'보다 더 나쁘다.
  • 객체를 과하게 일반화를 한다면 구체적인 대상을 표현하기 힘들다. 오히려 혼란만 가중시킴
  • 예를 들어 미디어라는 객체를 생각하고 이 클래스의 프로퍼티로 데이터를 정의하여 사진, 동영상, 음악, 일기 등 다양한 종류의 데이터를 담는다고 하면 너무 지나치게 일반화를 한 것이다.

5.4 객체 관계

  • 서로 다른 클래스가 공통적인 속성을 같거나 최소 두 클래스가 서로 관련이 있는 경우가 존재한다.
  • 서로 공통적으로 갖는 속성이 충분히 존재할 수 있다.
  • 이번 절에서는 has-a 관계와 is-a 관계에 대하여 알아보자.

5.4.1 has-a 관계

  • has-a : A는 B를 갖는다 or A에 B가 있다.
  • 한 객체가 가른 객체의 일부분이라고 생각하면 쉽다.
  • 컴포넌트는 대부분 has-a 관계로 표현 why? 객체의 구성 요소를 표현하기 때문.
  • 예를 들어 동물원 객체는 원숭이 컴포넌트를 갖는다.
  • 또 다른 예시로 UI에서 볼 수 있는 버튼을 가진 창을 예로 들 수 있다.

5.4.2 is-a 관계

  • OOP의 핵심 개념 중 하나인 상속 대한 것이다.
  • 이는 파생, 서브클래스, 확장 등으로 다양하게 표현.
  • 상속은 객체가 계층 구조로 이루어졌다는 점을 모델링한 것.
  • 즉, 계층 구조가 is-a 관계이다!
  • 공통 기능(공통점)을 베이스 클래스(상위 클래스)로 묶어서 다른 클래스가 확장할 수 있게 만들어야 함.

1. 상속 기법

  • 다른 클래스를 상속한 파생 클래스(자식 클래스)를 상위 클래스(부모 클래스)와 구분하는 방법에는 여러 가지가 있다.

 

  • 기능 추가
    • 파생 클래스는 기능을 더 추가해서 부모 클래스를 보완
    • ex) 동물이라는 클래스를 상속받는 원숭이 클래스는 동물 클래스의 정의된 메서드뿐만 아니라 원숭이 클래스만 갖는 메서드가 있음
  • 기능 변경
    • 파생 클래스는 부모 클래스가 가진 메서드를 변경 or 무시할 수 있음.
    • 만약 베이스 클래스를 추상 클래스로 정의하면 이를 상속하는 모든 파생 클래스는 베이스 클래스에 구현되지 않은 메서드를 모두 구현해야 함. (10장에서 계속..)
  • 프로퍼티 추가
    • 파생 클래스에 새로운 프로퍼티를 추가할 수 있음.
    • ex) 펭귄은 부리 크기라는 프로퍼티도 갖는다.
  • 프로퍼티 변경
    • 프로퍼티도 오버라이드가 가능하다.
    • but 베이스 클래스의 속성을 가리기 때문에 바람직하지 않을 수 있다.
    • 은닉 메커니즘과 관련 10장에서 계속..

 

2. 다형성과 코드 재사용

  • 다형성(Polymorphism)?
    • 프로퍼티와 메서드의 표준 셋으로 정해두면 그 형식에 맞는 객체라면 어느 것이든 서로 바꿔서 적용할 수 있다는 개념
    • 이는 OOP에서 멋진 개념이다. why? 상속이 제공하는 장점을 제대로 살리기 때문.
  • 상속은 다형성뿐만 아니라 단순히 기존에 작성한 코드를 재사용하기 위한 목적으로도 사용

5.4.3 has-a 관계와 is-a 관계 구분

  1. 구현할 클래스의 용도를 분석하고 기존 클래스에 있는 기능을 단순히 이용하기만 하는지?
  2. 기존 기능을 변경하거나 새 기능을 추가하는지?

리스 코프 치환 원칙(LSP)

  • 동작을 바꾸지 않고도 베이스 클래스를 대신해서 파생 클래스를 사용할 수 있어야 한다.
  • 이를 적용하면 has-a와 is-a 관계를 쉽게 구분 가능.

5.4.4 not-a 관계

  • OOP(Object Oriented Programming)에 대한 의욕이 넘쳐 불필요한 클래스 및 파생 클래스 관계를 만들어내지 않도록 주의
  • 객체지향 방식으로 계층을 구성하려면 억지로 관계를 형성하지 말고 기능 관점에서 관계를 표현하자!
  • 불필요한 상속관계를 만들지 않으려면 디자인 초안부터 간단히 그려보자.
  • 모든 클래스와 파생 클래스마다 어떤 프로퍼티와 메서드가 적절한지 적어본 뒤, 프로퍼티와 메서드가 없다면 디자인 수정 고려

5.4.5 클래스 계층

  • 각 파생 클래스를 구현하다 보면 여러 공통점을 발견하기 마련이다.
  • 공통부분을 부모 클래스로 옮기는 게 좋다.
  • 구현할 때는 현실세계에서 볼 수 있는 관계와 공통 기늠 관점으로 분류한 관계 사이의 균형을 적절히 조절해야 한다.
  • 현실에서는 공통점이 있지만 코드의 기능 면에서는 아무런 공통점이 없을 수도 있기 때문이다.

제대로 구성된 객체지향 계층의 특성

  • 기능적으로 의미 있는 관계에 따라 클래스를 구성
  • 공통 기능을 베이스 클래스로 설정하여 코드의 재사용 용이
  • 부모의 기능을 과도하게 오버 라이딩하는 파생 클래스가 없음.

5.4.6 다중 상속

  • 다중 상속을 통해 베이스 클래스를 여러 개 둘 수 있다.
  • 하지만 꼭 필요한 상황을 제외하고 단점이 많기 때문에 각별히 주의하자.

다중 상속을 반대하는 이유

  1. 시각적으로 표현하기가 어렵다.
  2. 구조의 명확성이 깨질 수 있다.
  3. 다중 상속은 구현하기 어렵다.

5.4.7 믹스인 클래스

  • 믹스인 클래스는 '이 클래스가 할 수 있는 일이 또 뭐가 있나?라는 질문의 '~도 할 수 있다'라고 답하는 클래스이다.
  • 일종의 공유 관계이다.
  • 사용자 인터페이스 코드에서 흔히 볼 수 있음.
  • 믹스인 클래스와 베이스 클래스의 차이는 생각하는 방식에 있다.
  • 이는 매우 제한된 용도로만 사용하기 때문에 다중 상속으로 표현할 때보다는 이해하기 쉽다.

5.5 추상화

  • OOP의 디자인 핵심 요소로 디자인에 바드시 적용해야 할 정도로 편리함.

5.5.1 인터페이스와 구현

  • 추상화의 핵심은 인터페이스와 구현을 분리하는 데 있음.
  • 구현 : 실제로 작성한 코드
  • 인터페이스 : 작성한 코드를 다른 사람이 사용하기 위한 수단
  • OOP의 인터페이스는 외부에서 접근할 수 있는 클래스의 프로퍼티와 메서드로 구성.

5.5.2 외부에 공개할 인터페이스 결정

  • public, protected, private와 같은 접근 제한자(접근 지정자)는 객체가 아닌 클래스에 대해 적용된다는 점에 주의!
  • 클래스 메서드는 같은 클래스에 대한 객체의 private 프로퍼티나 private 메서드에 접근할 수 있음.

1. 사용자 고려하기

  • 외부 인터페이스를 디자인할 때, 사용자를 고려해야 한다.
  • 개인적으로만 사용한다면 언제든지 얼마나 수정해도 상관은 없지만,
  • 다른 사람이 사용하게 된다면 일종의 계약서처럼 움직이게 된다.
  • 계약서 관점에서 본 인터페이스는 마음대로 변경할 수 없기 때문이다.

2. 용도 고려하기

  • 코드를 문서화 또는 외부에 노출할 기능을 결정하기 전에 해당 인터페이스의 용도에 대해서 고려해야 한다.

API

제품의 기능을 다양한 용도로 활용 또는 제품의 기능을 확장하기 위해 외부에 제공하는 인터페이스
내부 인터페이스가 계약서라면 API는 법전의 법률과 가깝다.
원하는 바를 최대한으로 수용하고 신중하게 디자인해야 한다.
사용성, 유연성의 절충점을 잘 찾아야 한다. 물론 사용하기 쉽게 구성해야 한다.
금방금방 사용자들이 이해하고 사용이 가능해야 하기 때문이다.

 

유틸리티 클래스 또는 라이브러리

범용성을 핵심으로 고려해야 함.
클래스나 라이브러리는 다양한 문맥에서 사용하기 때문에 구체적인 활용 범위를 최대한 고려해서 디자인에 반영

 

서브시스템 인터페이스

주요 서브시스템끼리 연동하는 인터페이스를 디자인할 때도 있음.
서브시스템을 다룰 때 가장 먼저 고려할 점은 그 서브시스템의 핵심 목적이 무엇이냐는 것임.
너무 세부사항에 빠지지 않도록 함.

 

컴포넌트 인터페이스

API 보다는 규모가 작은 인터페이스를 정의할 일이 있음.
서브시스템 인터페이스와 마찬가지로 벗어난 기능을 외부에 노출하지 않도록 주의

 

3. 미래 고려하기

  • 미래에 사용될 수도 있기 때문에 미래도 고려해 준다.
  • 서브시스템 인터페이스와 마찬가지로 각 클래스의 주목적을 염두하고 이를 벗어난 기능을 외부에 노출하지 않도록 주의

5.5.3 바람직한 추상화 디자인

  • 추상화를 잘하려면 결국 다양한 경험 해야 한다.
  • API에서 부족한 점은 없는지에 대해서 끊임없이 개선하려고 노력해야 한다.
  • 추상화가 잘된 인터페이스는 public 메서드로만 구성된다.
  • 모든 코드는 구현 파일에 있고 클래스 정의 파일에는 없어야 한다.
  • 즉, 클래스 정의가 있는 인터페이스 파일은 변경할 일이 없어야 한다.
  • 프로퍼티는 모두 메서드로 변환한다. 즉, 외부 코드에서 클래스에 직접 접근하여 데이터를 조작하면 안 된다.

5.6 요약

  • 이번 장에서는 OOP 디자인의 개념을 알아보았다. 이 개념들은 거의 모든 객체지향 언어에 적용할 수 있다. 객체 사이의 관계는 반드시 이해하고 넘어가자.
  • 또, 이를 잘 설명할 수 있다면 재사용성이나 간결성에 도움이 될뿐더러 협업하는데 큰 도움이 된다.
  • 다음 장에서는 재사용을 고려한 디자인 방법에 대해서 소개할 예정이다.
반응형