XR 플밍 - 4. 객체지향 프로그래밍과 데이터 구조 - (1-1) 객체 지향과 클래스 (3/24)

이형원·2025년 3월 24일
0

XR플밍

목록 보기
20/215

지금까지 프로그래밍 언어 기초를 배우면서 절차 지향 프로그래밍을 배웠다. 따로 절차 지향이라 말하진 않았지만, 콘솔 프로젝트 등을 진행하면서 순서가 정해진 대로 게임이 진행한 것을 보았다. 하지만, 아무래도 절차 지향 프로그래밍이 지닌 문제들 - 기능 추가 및 게임을 크게 만들 수록 불편함을 느끼는 부분 등이 많이 체감되었다.

이와 같은 한계점을 개선한 프로그래밍 패러다임인 '객체 지향 프로그래밍'을 배워보고자 한다.

1. 객체 지향 프로그래밍이 왜 필요한가?

앞선 콘솔 프로젝트에서 절차 지향 프로그래밍 방식으로 게임을 만들어 보았다. 게임 자체를 만드는 작업은 유익한 경험이었지만, 그 당시 사용한 방식으로는 아무래도 기능 구현에 여러 불편함이 발생했었다.

크게 불편함을 느낀 부분은 아래 세 가지와 같았다.

  1. 추가적인 기능을 넣어야 할 때 코드 수정이 어렵다.
  2. 복잡한 상황을 구현하기 어렵다.
  3. 코드가 길어지면 관리하기가 어렵다. 어디선가 문제가 터지면 코드를 전부 확인해 봐야 함.

이러한 불편한 상황을 객체 지향 프로그래밍으로 진행하면 어떠한 차이점이 보일까. 아래와 같은 예시 상황을 생각해보자.

자판기 프로그램을 만든다고 하자. 자판기 프로그램을 절차 지향 프로그래밍 방식으로 제작하면 아래와 같이 만들어질 것이다.

하지만 이와 같은 방식으로 프로그램을 만들었을 때 크게 보이는 문제점은 두 가지다.

  1. 만약 이 프로그램을 실행했을 때 문제가 생겼을 때 어느 부분을 살펴봐야 할까? 명확하지 않기 때문에 코드 전체를 확인해야 한다
  2. 해당 프로그램에 새로운 기능을 넣으려면 어떻게 해야 할까? 상당히 난감하다.

반면에 이를 객체 지향 프로그래밍으로 제작하면 아래와 같이 제작할 수 있다.

이와 같은 방식의 장점은 아래와 같다.

  1. 문제가 생기면 어느 부분을 확인하면 될지 판단이 쉬워진다. (디버깅에 유리)
  2. 특정 기능을 다른 곳에서도 쓸 수 있다.
    ex) 자판기의 금액 입력기를 노래방 기계나 무인 세탁소에 활용
  3. 이미 구성된 것-클래스를 조합하여 대규모의 프로젝트를 만드는 데 유리하다
  4. 클래스 관리 영역 등에서 역할 분담이 가능하다

이와 같은 점을 생각해 보며 객체 지향 프로그래밍에 대해 알아보자.

2. 객체 지향 프로그래밍의 등장

  • 절차지향과 객체지향

-절차지향 : 프로그램의 순차적인 처리를 위주로 설계하는 방법론
-객체지향 : 서로 상호작용하는 객체를 기본단위로 구성하는 방법론

물리적인 하드웨어의 발전이 빠르게 진행되었으며, 소프트웨어의 중요성이 빠르게 올라갔다.
기존 절차지향의 방식으로는 복잡한 구조에 대한 설계가 힘들어졌으며 객체지향 방식이 대안이 되었다.

3. 객체(Object) 지향이란?

객체 개념(Object)을 클래스(Class)란 설계도로 만드는 과정객체 지향 프로그래밍이다.

이를 조금 더 풀어 설명하면,

  1. 우리 게임에는 이런 개념이 필요하다, 라는 객체를 정의한다.
  2. 이걸 프로그래밍에 녹여내기 위해, 각 객체의 정보/행동으로 나누어서 클래스화한다.
  3. 클래스를 실체화하는 것을 인스턴스화(Instanciate) 한다고 한다.

가령, 플레이어라는 클래스를 만든다고 해 보자.

클래스에서 정보를 변수로 표현하고, 행동을 함수로 표현한다.
ex) 플레이어의 레벨, 공격력 : 변수(명사) / 플레이어의 공격 행동 : 함수(동사)

게임에는 플레이어만 있는 것이 아닐 것이므로 몬스터 등 상호작용하는 클래스를 늘려나갈 것이다.

이와 같이 각 클래스를 설계하고 그 클래스 간의 상호작용을 통해 프로세스를 만들어가는 과정이 객체 지향 프로그래밍이다.

4. 클래스(class)

  • 클래스 (class)

데이터와 관련 기능을 캡슐화할 수 있는 참조 형식으로, 객체지향 프로그래밍에 객체를 만들기 위한 설계도이다. 이를 통해 만들어진 객체는 인스턴스라 한다.
참조 : 원본을 가리키고 있음, 즉 원본의 주소를 가지고 있음

  • 클래스의 구성
class 클래스이름
{ 
	(클래스내용) 
}

클래스 내용으로는 변수와 함수가 포함 가능하다. 또한 해당 변수 및 함수의 접근 허용 여부를 선택할 수 있는데, 이를 접근 제한자라고 한다.

-외부에 접근 허용 : public을 앞에 붙인다.
-외부에 접근 비허용 : 앞에 아무것도 안 쓰거나. private을 앞에 붙인다

5. 객체 지향의 장단점

  • 장점

    1.객체 단위로 관리하기 때문에 디버깅이 유리함
    2.클래스 단위로 모듈화 시켜 관리하므로 대규모 프로젝트에 적합
    3.코드의 재사용성이 좋음

  • 단점

    1. 설계에 시간이 많이 소비되며 신중해야 함
      똑같은 기능을 설계하는 데에도 정답이 없으므로 사람마다 설계하는 방식이 달라짐, 정답이 없음. 왜 이렇게 만들었는지 근거를 댈 수 있으면 좋다.

6. 객체 지향의 4특징

-캡슐화 : 객체를 상태와 기능으로 묶는 것. 객체의 내부 상태와 기능을 숨기고, 허용한 상태의 기능만의 엑세스 허용

-상속 : 부모 클래스의 모든 기능을 가지는 자식클래스를 설계하는 방법.

-추상화 : 관련 특성 및 엔터티의 상호 작용을 클래스로 모델링하여 시스템의 추상적 표현을 정의.

-다형성 : 부모클래스의 함수를 자식클래스에서 재정의하여 자식클래스의 다른 반응을 구현.

해당 상세 내용을 확인해보자

6.1) 캡슐화(Encapsualtion)

객체를 정보와 기능으로 묶는 것을 의미한다
객체의 내부 정보와 기능을 숨기고 허용한 정보와 기능만의 엑세스를 허용한다

  • 캡슐화의 사용 의미1

캡슐화된 클래스는 외부에서 사용하기 위한 인터페이스만을 제공하여 복잡성이 감소한다.
캡슐화된 클래스는 내부적으로 어떻게 구현되었는지 몰라도 사용 가능하다 - Public으로 된 부분만 노출

  • 캡슐화의 사용의미2

캡슐화된 클래스는 외부에서 원하지 않는 사용법으로부터 보호할 수 있어 오용된 사용을 방지한다

6.2) 상속

하위 객체가 상위객체에서 정의된 모든 내용(속성과 기능)을 상속받아 반복되는 코드를 재활용함

상위 객체를 정의하면, 하위 객체에 해당 내용 상속할 수 있다.

ex) 몬스터를 정의 -> 몬스터의 하위 객체로 오크, 슬라임, 드래곤 등을 정의

6.3) 추상화

객체에서의 공통적인 속성과 기능을 추출하고, 불필요한 세부사항들은 제거하여, 객체의 가장 본질적인 부분만을 표현한다

         이 부분이 추상화된 부분이다ㄱ
ex) 롤 예시 - <모든 챔피언은 q, w, e, r(궁극기), 점멸, 회복 키 등을 가지고 있다.> 다만 챔피언별 스킬은 다르다

6.4) 다형성

객체의 속성이나 기능이 상황에 따라 여러가지 형태를 가질 수 있다.

ex) 같은 몬스터인데 체력 및 공격력이 다른 몬스터를 구현

7. Solid 5원칙 - 객체를 잘 만드는 방법

  1. 단일 책임 원칙 (Single Responsibility Principle)

    하나의 클래스는 하나의 기능만을 담당하도록 한다. -> 단순하게 말하자면 기능(책임)을 분리하라는 것

ex) 플레이어가 스킬이나 인벤토리에 대한 책임까지 들 필요는 없다. 스킬 클래스와 인벤토리 클래스를 따로 만들자.

  1. 개방 폐쇄 원칙(Open Closed Principle)

    확장에 대해서는 열려 있어야 하며 수정에 대해서는 닫혀 있어야 한다.

ex) 몬스터라는 클래스 내에서 몬스터 정보를 정의하는 것보다, 추가적으로 오크 소스랑 슬라임 소스를 새로 만들어서 확장하는 방식으로 만드는 게 좋다. -> 기존 코드를 수정하지 않고 확장하도록 한다.

  1. 리스코프 치환 원칙(Listov Substitution Principle)

    자식 객체는 언제나 부모타입으로 교체될 수 있어야 한다.

ex) class Vehicle 이라는 부모 객체를 만들었는데, class Car : Vehicle이나 class Train : Vehicle 같은 걸 만든 게 아니라, class Shop : Vehicle로 만들면 안 된다.

  1. 인터페이스 분리의 원칙(Interface Segregation Principle)

    하나의 큰 인터페이스보다 용도에 맞는 인터페이스를 잘게 분리해야 한다.

ex) 예를 들어 몬스터가 피격되어 죽는 프로세스를 Die()로 따로 만들지 않고, 플레이어의 공격으로 쓰러졌다고 프로세스를 만들었다고 하자. 몬스터가 그냥 떨어져서 죽는다는 행동을 만들었을 때 또 다시 죽는 프로세스를 만들어야 한다 -> Die()로 따로 만들어서 코드의 반복을 피하도록 한다.
행동을 통째로 너무 길게 만드는 대신에 행동 인터페이스를 분리해서 만든다.

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

    고수준의 모듈은 저수준의 모듈의 구현에 의존해선 안 된다.(부모가 자식한테 의존하면 안 된다)
    부모에 만들 수 있으면 부모에 만들어야 한다.

ex) 몬스터가 모두 이름이 있는데, 이걸 부모 객체에서 정의하지 않고 자식 객체에서 정의를 하면 이름 변수를 몬스터마다 써 줘야 한다.


이와 같이 클래스의 원칙이 있으며, 이들 전부를 만족할 필요는 없지만 많이 지켜질 수록 잘 만든 클래스라 할 수 있다.

8. 생성자

반환형이 없는 클래스 이름의 함수를 생성자라 한다.
클래스의 인스턴스를 만들 때 호출되는 역할로 사용하며 "new" 키워드를 통해서 사용

  • 예시
class Monster
{
    public string _name;
    public int _level;

    public Monster(string name, int level)
    {
        _name = name;
        _level = level;
    }
}

이와 같이 선언하면 메인에서 이와 같이 작성할 수 있다.

static void Main(string[] args)
{
	Monster monster = new Monster("오크", 5);
}

여기서 아래와 같이 작성하고 생성자를 선언할 수도 있다.

class Monster
{
    public string _name;
    public int _level;
}
static void Main(string[] args)
{
	Monster monster = new Monster();
}

이와 같이 작성했을 경우, 기본생성자(내부가 공백)인 생성자가 자동으로 생성된다.

9. namespace, partial class

  1. 다른 namespace로 작성된 class가 있을 때, Using (namespace 이름) 으로 class를 호출할 수 있다.


클래스명이 같아도 namespace가 다르면 문법 오류가 발생하지 않는다.

메인으로 호출 또한 가능하다.

  1. partial class로 작성된 동일 이름의 두 클래스는 같은 클래스처럼 사용할 수 있다.
    (*주의 : 하나의 클래스가 너무 많은 기능을 지니고 있지 않은지 유의하도록 한다.)
profile
게임 만들러 코딩 공부중

0개의 댓글