경일게임아카데미 멀티 디바이스 메타버스 플랫폼 개발자 양성과정 20220603 2022/04/04~2022/12/13

Jinho Lee·2022년 6월 3일
0

경일 메타버스 20220603 9주차 4일 수업내용. C++ 프로그래밍 문법, 객체 지향적 설계

추상 클래스(Abstract Class)

  • 추상 클래스인스턴스를 만들 수 없는 클래스로 상위 타입을 정의하는 데 사용한다.

  • 클래스 내부에 가상 함수를 만들고 = 0;으로 초기화 해주면 추상 클래스가 된다.

  • 예시와 같은 추상 클래스의 순수 가상 함수는 하위 타입으로 하여금 오버라이딩을 강제하는 효과가 있다.

  • 구체 클래스(Concrete Class) : 추상 클래스의 반대, 인스턴스를 만들 수 있는 클래스.

  • 2022. 06. 03 추상클래스 예시 코드

객체 지향 프로그래밍에서의 동적 할당

  • 클래스 타입의 객체를 동적 할당할 때, 생성자와 소멸자를 호출해야 한다.

  • 할당에는 new 연산자를, 해제에는 delete 연산자를 사용한다.

    • 배열의 할당과 해제는 new[]와 delete[] 연산자를 사용한다.

    • new는 실제로 malloc으로 메모리를 얻어오고, 클래스 타입의 경우 생성자를 호출한다.

    • delete는 실제로 클래스 타입의 경우 소멸자를 호출하고, 그 후 delete가 호출되어 free로 메모리를 반환한다.

    • 결국 동적 할당은 malloc과 free를 사용한다.

    • new와 delete에서는 암시적으로 생성자와 소멸자를 호출하는 점이 다르다.

  • new / delete 연산자의 쌍은 꼭 맞춰줘야 한다.

    • 동작 방식이 서로 다르기 때문이다.
  • 2022. 06. 03 객체 지향 프로그래밍에서의 동적 할당 예시 코드

가상 소멸자(Virtual Destructor)

  • 보통 객체 지향에선 업캐스팅하여 객체를 다루게 되는데, 이 경우 객체의 소멸이 제대로 안 이루어진다.

    • 상위 타입으로 다루고 있기 때문
  • 올바르게 소멸자를 호출하고 싶다면 가상 소멸자(Virtual Destructor)를 정의해야 한다.

  • 상위 타입이 될 여지가 있는 클래스라면 꼭 가상 소멸자를 정의해주도록 하자.

struct Base
{
  Base() { std::cout << "Constructor"; }
  virtual ~Base() { std::cout << "Destructor"; }
};

struct Derived : Base
{
  Derived() { std::cout << "Constructor"; }
  ~Derived() { std::cout << "Destructor"; }
};

Base* b = new Derived();
delete b; // 이제는 올바르게 소멸된다.
// ~Base()가 가상 소멸자(가상 함수)이기에
// b->~Derived() 호출 후 b->~Base() 호출
// virtual이 없었다면?
// b->~Base()만 호출

객체 지향적 설계

  • 객체 지향 프로그래밍이 주류가 된 이유

    • 다형성에 의한 높은 확장성

    • 캡슐화를 통해 직접적이고 직관적으로 모델링 가능

      • 메소드를 통해 기능을 표현

      • 필드는 그 기능을 구현하기 위한 데이터를 표현

      • 추상화를 통해 사용자 입장에서는 불필요한 세부 사항을 숨긴다.

책임 주도 설계(Responsibility-driven Design)

협력

  • 실생활 예

    • 협력은 도움의 요청과 그 응답으로 구성

    • 협력의 성공은 요청을 받은 개인이 얼마나 요청을 성실히 이행할 수 있는가에 달려 있다.

  • 객체는 소프트웨어의 기능을 구현하기 위해 협력

  • 책임은 적절한 역할을 가진 객체에 의해 수행

  • 객체 지향 설계는 적절한 객체에게 적절한 책임을 부여하는 것에서 시작된다.

  • 책임은 객체 지향 설계의 품질을 결정하는 가장 중요한 요소

  • 역할유연하고 재사용 가능한 협력 관계를 구축하는 데 중요한 설계 요소

객체(object)

  • 상태(state)행동(behavior)을 함께 지닌 실체

    • 상태 → 필드(field)

    • 행동 → 메소드(method)

좋은 객체란?

  1. 객체는 충분히 협력적이어야 한다.

    • 여러가지 책임을 가진 전지전능한 객체(god object)는 내부적인 복잡도에 의해 자멸한다.

    • 복잡도에 의해 자멸한다 == 유지 보수하기가 힘들다.

  2. 객체는 충분히 자율적이어야 한다.

    • 요청에 응답하는 방식응답 여부는 객체 스스로 판단하고 결정해야 한다.

    • 객체의 자율성객체의 내부와 외부를 명확하게 구분하는 것으로부터 나오게 된다.

      • 객체의 내부는 객체 스스로 관리하고 외부에서 일체 간섭할 수 없도록 차단해야 한다.

      • 객체의 외부에서는 접근이 허락된 수단(메소드)을 통해 의사소통을 하게 된다.

역할

  • 여러 객체가 동일한 역할을 수행할 수 있다.
  • 역할대체 가능성을 의미한다.
  • 각 객체는 책임을 수행하는 방법을 자율적으로 선택할 수 있다.
  • 요청에 대해 서로 다른 방식으로 응답할 수 있는 능력을 다형성(polymorphism)이라고 한다.
  • 하나의 객체가 동시에 여러 역할을 수행할 수 있다.

책임

  • 객체가 어떤 행동을 하는 유일한 이유 : 다른 객체로부터 요청을 수신했기 때문

  • 요청을 처리하기 위해 객체가 수행하는 행동책임이라고 할 수 있다.

  • 소프트웨어를 객체 지향적으로 설계할 때는

    1. 소프트웨어의 기능을 명세

    2. 그 기능을 수행하기 위한 협력 관계를 구축

  • 책임은 객체가 무엇을 해야 하는가를 설명한다.

  • 책임은 수행 방법을 제한할 정도로 너무 구체적이어도 안된다.

  • 협력의 의도를 명확하게 표현하지 못할 정도로 추상적이어서도 안된다.

  • 책임역할이라고도 할 수 있다. 책임 == 역할

메시지

  • 객체 지향에서 협력메시지를 전송하는 객체메시지를 수신하는 객체 사이의 관계로 구성된다.

    • 메시지를 보내는 객체 : 송신자(Sender)

    • 메시지를 받는 객체 : 수신자(Receiver)

  • 메소드(Method) : 객체가 수신된 메시지를 처리하는 방법

    • 메시지를 수신한 객체는 실행 시간에 메소드를 선택할 수 있다.

    • 외부의 요청이 무엇인지를 표현하는 메시지와 요청을 처리하기 위한 구체적인 방법인 메소드분리하는 것은 객체의 자율성을 높이는 핵심 메커니즘

형성에 대한 재해석

  1. 서로 다른 객체들이 다형성을 만족시킨다 == 객체들이 동일한 책임을 공유하는 것

  2. 다형성은 메시지 송신자의 관점에서 동일한 책임을 수행하는 것

  3. 객체 지향 프로그래밍다형성을 이용해 협력을 유연하게 만들 수 있다. ⇒ 강력하다.

메시지의 장점

  • 송신자와 수신자 사이의 결합도를 낮춤으로써 설계를
    1. 유연하고
    2. 확장 가능하고
    3. 재사용 가능하게 만든다.

객체 지향 프로그래밍 정리

  1. 객체 지향이란 시스템상호작용하는 자율적인 객체들의 공동체로 바라보고 객체를 이용해 시스템을 분할하는 방법이다.

  2. 자율적인 객체상태와 행위를 함께 지니며 스스로 자기 자신을 책임지는 객체를 의미한다.

  3. 객체는 시스템의 행위를 구현하기 위해 다른 객체와 협력한다. 각 객체는 협력 내에서 정해진 역할을 수행하며 역할은 관련된 책임의 집합이다.

  4. 객체는 다른 객체와 협력하기 위해 메시지를 전송하고 메시지를 수신한 객체는 메시지를 처리하는 데 적합한 메소드를 자율적으로 선택한다.

  5. 코드를 담는 클래스의 관점에서 메시지를 주고받는 객체의 관점으로 사고의 중심을 전환해야 한다.

  6. 중요한 것은 어떤 클래스가 필요한가가 아니라 어떤 객체들이 어떤 메시지를 주고받으며 협력하는가다. 클래스는 객체 간 협력 관계를 코드로 옮기는 도구에 불과하다.

  7. 객체 지향의 핵심적절한 책임을 수행하는 역할 간유연하고 견고한 협력 관계를 구축하는 것이다.

  • 주의 : 상속은 남발하지 말 것. 상속이 깊어질 수록 코드를 분석하기 어려워진다.

SOLID 원칙

  • 객체 지향 설계의 바탕이 되는 5가지 원칙
  1. 단일 책임 원칙(Single Responsibility Principle)

    • 각 소프트웨어 모듈은 변경의 이유하나여야 한다.

    • 책임에 대해서 명확히 이해하고 있다면 자명하다.

    • 객체마다 단 하나의 책임이어야 유지 보수하기 편하다.

  2. 개방-폐쇄 법칙(Open-Closed Principle)

    • 객체는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.

    • 기존 코드를 수정하기보다는 반드시 새로운 코드를 추가하는 방식으로 시스템의 행위를 변경할 수 있도록 설계해야만 소프트웨어 시스템을 쉽게 변경할 수 있다는 것이다.

      • 캡슐화와 관련이 깊다.
  3. 리스코프 치환 원칙(Liskov Substitution Principle)

    • 하위 타입에 관한 원칙이다.

    • 프로그램 P에서 T 타입의 객체 자리에 S 타입의 객체로 모두 치환하더라도 P의 행위가 변하지 않는다면 S는 T의 하위 타입이다.

      • is-a 관계 : S is a T
        예) Jinho is a Person → Jinho는 Person의 하위 타입
  4. 인터페이스 분리 원칙(Interface Segregation Principle)

    • 어떤 타입다른 타입의존한다고 할 때, 필요한 인터페이스만을 분리해 의존하게 설계하라.

    • 결합도메시지 수준으로 낮추라는 것.

    • 이를 통해 설계가 유연해지고, 확장 가능하고, 재사용 가능하게 된다.

  5. 의존성 역전 원칙(Dependency Inversion Principle)

    • 고수준 정책을 구현하는 코드는 저수준 세부사항을 구현하는 코드에 절대로 의존해서는 안되며, 세부사항이 정책에 의존해야 한다.

    • 다형성을 적극적으로 활용하라는 얘기다.

스스로 짧게 정의한 OOP

객체 지향 프로그래밍(Object-oriented programming, 이하 OOP)이란 함수 위주의 방법론인 절차 지향 프로그래밍을 사용함에 있어서 확장성의 부족과 유지 및 보수의 어려움이 문제가 되어, 이를 해결하기 위해 제안된 방법론이다. OOP는 프로그램 등의 시스템을 분석하는데 있어서 그 구성을 자율적인 객체의 상호작용 및 관계로 이해하여 이를 바탕으로 객체 단위로 분할하여 목표하는 기능을 구현하는 프로그래밍 방법이다.

여기서 객체란 보유하는 데이터 등의 상태와 수행하는 기능 등의 행동을 함께 지니는 실체로, 프로그래밍에서는 다루는 메모리 자체라고 볼 수 있다고 이해하고 있다.
객체가 자율적이라함은 객체의 내부와 외부가 명확히 구별되어 있어, 객체의 외부에서는 미리 허락된 방법을 제하고는 객체의 내부에 간섭할 수 없고, 구별된 내부는 객체 스스로 관리하도록 되어있음을 의미한다.

참고 자료

0개의 댓글