객체지향 프로그래밍을 처음 배우면 잘 와닿지도 않을 뿐더러 어떻게 코드를 작성 해야 하는지도 감이 잘 안 잡히실 겁니다. 이 문제점을 잘 알기에 이번 시간에는 객체지향 프로그래밍이 무엇인지 알아보고 그 장점과 단점을 소개하겠습니다. 마지막으로 실제 어떻게 사용되는지 예시코드를 제공하겠습니다.
목차
[객체지향 프로그래밍이란?]
객체지향 프로그래밍은 코드를 설계하고 구현하는 방법 중 하나입니다. 이 뜻을 쉽게 풀어서 설명하겠습니다.
객체지향 프로그래밍은 현실 세계의 사물이나 개념을 프로그램 안에서 모델링하는 방식입니다. 여기서 사물이나 개념을 “객체”라고 합니다. 예를 들어, 게임에서 캐릭터, 총, 몬스터 등은 모두 객체가 될 수 있습니다. 이 객체들은 각각의 특징과 기능을 가지고 있습니다. 캐릭터는 이동하고 공격할 수 있고, 총은 발사할 수 있으며, 몬스터는 공격하고 피할 수 있습니다.
객체지향 프로그래밍에서 가장 중요한 개념 중 하나는 “클래스“입니다. 클래스는 객체의 틀이라고 생각할 수 있습니다. 클래스는 객체가 가져야 할 속성(특징)과 메서드(기능)를 정의합니다. 즉, 캐릭터 클래스는 캐릭터가 가져야 할 정보(체력, 공격력 등)와 할 수 있는 행동(이동, 공격 등)을 정의합니다.
그리고 객체 간의 상호작용도 객체지향 프로그래밍에서 중요합니다. 객체들은 서로 메시지를 주고받아 협력하며 작업을 수행합니다. 예를 들어, 캐릭터가 총을 발사하면 총 객체에게 발사 메시지를 보내어 총이 총알을 발사하도록 할 수 있습니다.
// 캐릭터 클래스 정의
public class Character
{
// 속성(특징) 정의
public int health;
public int attackDamage;
// 이동 메서드 정의
public void Move()
{
Debug.Log("캐릭터가 이동합니다.");
}
// 공격 메서드 정의
public void Attack()
{
Debug.Log("캐릭터가 공격합니다.");
}
}
// 총 클래스 정의
public class Gun
{
// 총알 발사 메서드 정의
public void Shoot()
{
Debug.Log("총이 총알을 발사합니다.");
}
}
// 몬스터 클래스 정의
public class Monster
{
// 몬스터가 공격하는 메서드 정의
public void Attack()
{
Debug.Log("몬스터가 공격합니다.");
}
// 몬스터가 피하는 메서드 정의
public void Dodge()
{
Debug.Log("몬스터가 피합니다.");
}
}
// 게임 실행 스크립트
public class Game : MonoBehaviour
{
void Start()
{
// 객체 생성
Character player = new Character();
Gun playerGun = new Gun();
Monster enemy = new Monster();
// 객체의 속성 설정
player.health = 100;
player.attackDamage = 10;
// 객체 간 상호작용
player.Move(); // 캐릭터 이동
player.Attack(); // 캐릭터 공격
playerGun.Shoot(); // 총 발사
enemy.Attack(); // 몬스터 공격
enemy.Dodge(); // 몬스터 피함
}
}
위 코드의 예시와 같이 객체들과 그 객체들이 할 수 있는 행동을 정의함으로 서로가 상호작용하는 것을 “객체지향 프로그래밍”이라 하는 것 입니다.
[객체지향 프로그래밍 장점]
- 코드의 구조화와 관리 용이성: 객체지향 프로그래밍은 코드를 객체 단위로 분할하고 각 객체가 독립적으로 존재하도록 구성합니다. 이는 코드의 구조화를 도와주며, 코드를 논리적으로 나누어 관리할 수 있게 합니다. 따라서 유지보수가 쉬워지고 버그를 찾기도 편리해집니다.
- 코드의 재사용성: 객체지향 프로그래밍에서는 클래스를 템플릿으로 사용하여 객체를 생성합니다. 이는 유사한 기능을 가진 객체를 간단히 생성할 수 있게 해줍니다. 예를 들어, 여러 종류의 몬스터가 있을 때 몬스터 클래스를 한 번 정의하고, 이를 상속하여 각각의 몬스터 객체를 생성할 수 있습니다.
- 유연성과 확장성: 객체지향 프로그래밍은 객체 간의 상호작용을 통해 코드를 작성합니다. 이는 기능을 확장하거나 변경할 때 기존 코드의 수정을 최소화할 수 있게 합니다. 예를 들어, 새로운 캐릭터나 무기를 추가하거나 기존의 기능을 수정할 때, 해당 객체의 클래스만 수정하면 되므로 다른 부분에 영향을 미치지 않습니다.
- 모듈화와 협업 용이성: 객체지향 프로그래밍은 각 객체가 독립적으로 존재하고 서로 협력하여 작업을 수행합니다. 이는 여러 프로그래머가 동시에 작업할 때 코드의 충돌을 최소화하고 모듈화된 형태로 작업할 수 있게 해줍니다. 따라서 대규모 프로젝트의 개발이 용이해집니다.
- 현실과의 유사성: 게임은 현실 세계의 요소들을 모방하여 만들어집니다. 객체지향 프로그래밍은 이러한 현실 세계의 모델링을 지원하므로 게임 개발에 적합합니다. 예를 들어, 캐릭터, 아이템, 몬스터 등의 게임 요소들을 객체로 표현하여 게임을 개발할 수 있습니다.
[객체지향 프로그래밍 단점]
- 복잡성과 학습 곡선: 객체지향 프로그래밍은 처음에는 이해하기 어려울 수 있습니다. 클래스, 객체, 상속, 다형성 등의 개념을 이해하고 활용하기 위해서는 시간과 노력이 필요합니다.
- 상속의 함정: 객체지향 프로그래밍에서는 상속을 사용하여 코드를 재사용할 수 있습니다. 하지만 상속을 남용하면 클래스 간의 강한 결합이 발생할 수 있고, 클래스 계층 구조가 복잡해질 수 있습니다. 이는 유지보수가 어려워지고 코드의 유연성이 감소할 수 있습니다.
- 높은 추상화 수준: 객체지향 프로그래밍은 현실 세계의 개념을 모델링하기 위해 높은 수준의 추상화를 사용합니다. 이로 인해 코드가 추상적이고 복잡해질 수 있습니다. 게임 개발에서는 종종 구체적인 요구사항에 맞춰 더 구체적인 접근이 필요할 수 있습니다.
[추상화란?]
추상화는 복잡한 시스템이나 개념을 단순화하여 중요한 부분에 집중할 수 있도록 하는 프로그래밍 개념입니다. 이는 일종의 개념화 과정으로, 구체적인 세부 사항을 숨기고 핵심적인 기능이나 특징에만 집중함으로써 코드를 더 간결하고 이해하기 쉽게 만듭니다.
은행 시스템을 만든다고 할때, 은행 시스템에는 고객, 계좌, 거래 등 많은 요소가 있을 겁니다. 이때 추상화를 이용하면 다음과 같이 생각할 수 있습니다:
- 고객(Customer): 개별 고객의 개인 정보나 계좌 상태는 신경쓰지 않고, 고객을 식별할 수 있는 고유한 ID나 이름만을 고려합니다. 예를 들어, 고객은 이름과 고유한 ID를 가지고 있으며, 계좌를 개설할 수 있습니다.
- 계좌(Account): 각 계좌는 고유한 계좌 번호와 잔액 정보를 갖고 있습니다. 실제로는 예금, 적금, 대출 등 다양한 종류의 계좌가 있을 수 있지만, 추상화 단계에서는 이러한 세부 사항은 고려하지 않고 단순히 계좌 번호와 잔액만을 고려합니다.
- 거래(Transaction): 고객이 입금, 출금, 이체 등의 거래를 수행할 때는 해당 거래의 유형과 금액만을 고려합니다. 실제로는 거래 시 발생하는 다양한 이벤트나 보안 등의 사항이 있겠지만, 추상화 단계에서는 이러한 세부 사항을 무시하고 거래의 유형과 금액만을 고려합니다.
[추상화를 하면 좋은점이 무엇일까?]
처음 개발을 시작할 때 의문이였던 것이 추상화를 하면 좋다는 건 알겠는데 어떻게 왜? 좋은지 확실이 감이 안왔었습니다. 저 같이 의문을 품었던 분들을 위해서 정확하게 설명 드리겠습니다.
먼저 캐릭가 이동하게 만들고 싶다고 할 때 핵심 동작인 캐릭터 이동을 추상화 한다고 하면, 한명의 캐릭터가 여러개의 이동 동작을 쉽게 할 수 있습니다. 글로만 읽어서는 이해가 쉽지 않으니 예시코드를 제공하겠습니다.
// 캐릭터 클래스 정의
public class Character
{
// 이동 메서드 정의 (인터페이스 활용)
private IMovable movementMethod;
// 이동 메서드 설정
public void SetMovementMethod(IMovable movementMethod)
{
this.movementMethod = movementMethod;
}
// 이동 메서드 호출
public void Move()
{
if (movementMethod != null)
{
movementMethod.Move();
}
else
{
Debug.LogError("이동 메서드가 설정되지 않았습니다.");
}
}
}
// 이동 메서드를 위한 인터페이스 정의
public interface IMovable
{
void Move();
}
// 기본 이동 메서드 구현
public class DefaultMovement : IMovable
{
public void Move()
{
Debug.Log("기본 이동 방식으로 캐릭터를 이동시킵니다.");
}
}
// 더 나은 이동 메서드 구현
public class BetterMovement : IMovable
{
public void Move()
{
Debug.Log("더 나은 이동 방식으로 캐릭터를 이동시킵니다.");
}
}
// 게임 실행 스크립트
public class Game : MonoBehaviour
{
// 캐릭터 객체 생성
Character player;
void Start()
{
// 캐릭터 객체 초기화
player = new Character();
// 기본 이동 메서드로 설정
player.SetMovementMethod(new DefaultMovement());
}
void Update()
{
// 이동 입력 처리
if (Input.GetKeyDown(KeyCode.Space))
{
// 캐릭터 이동
player.Move();
}
}
}
이렇게 인터페이스를 이용하여 이동이라는 개념을 추상화하면, player.SetMovementMethod(new DefaultMovement()); 이와 같이 플레이어에게 움직임을 동적으로 할당해주면 자유자재로 여러 이동을 사용할 수 있게되는 것입니다. 이것이 바로 추상화의 장점입니다. 하지만 단점을 설명했듯이 모든 동작에 추상화를 하려다 보면 더 복잡해지고, 코드의 가독성을 떨어트리기에 핵심적인 개념들을 추상화 하는 것이 매우 중요합니다.
[캐릭터와 NPC의 상호작용코드로 객체지향 이해하기]
// 상호작용 가능한 객체의 인터페이스 정의
public interface IInteractable
{
void Interact(Character character);
}
// 캐릭터 클래스 정의
public class Character
{
public void InteractWith(IInteractable interactable)
{
// 다른 객체와 상호작용
interactable.Interact(this);
}
public void Talk()
{
Debug.Log("캐릭터가 NPC와 대화합니다.");
}
public void PerformQuest()
{
Debug.Log("캐릭터가 NPC로부터 퀘스트를 받습니다.");
}
}
// NPC 클래스 정의
public class NPC : MonoBehaviour, IInteractable
{
public void Interact(Character character)
{
Debug.Log("NPC와 상호작용합니다.");
// NPC와 상호작용하는 특정 동작 수행
character.Talk();
character.PerformQuest();
}
}
// 게임 실행 스크립트
public class Game : MonoBehaviour
{
Character player;
NPC npc;
void Start()
{
player = new Character();
npc = new NPC();
}
void Update()
{
// 플레이어 입력에 따라 상호작용 실행
if (Input.GetKeyDown(KeyCode.E))
{
player.InteractWith(npc);
}
}
}
IInteractable
인터페이스는 캐릭터와 NPC가 상호작용할 수 있는 메서드Interact
를 정의합니다.Character
클래스는IInteractable
인터페이스를 구현하지 않지만, 다른 객체와 상호작용할 수 있도록InteractWith
메서드를 갖습니다. 이 메서드는IInteractable
인터페이스를 구현한 객체와 상호작용합니다.NPC
클래스는IInteractable
인터페이스를 구현하여 상호작용 가능한 객체로 만듭니다.Interact
메서드에서는 NPC와 상호작용하는 동작을 수행합니다.Game
클래스에서는 캐릭터와 NPC를 생성하고, 플레이어의 입력에 따라 캐릭터와 NPC가 상호작용하도록 합니다.
[마무리 글]
C#이나 유니티를 배우면 객체지향 이라는 개념은 꼭 알아두셔야 코드를 작성하실 때 완성도가 높은 코드를 작성하실 수 있습니다. 이를 제대로 이해하지 못하고 코드를 작성하다보면 코드양이 많아지게 되고 이것은 곧 기본기가 없는 개발자처럼 인식되기 때문에 이번에 제가 소개해드린 예시코드를 잘 숙지해 두시기 바랍니다. 또한 객체지향에 너무 몰입한 나머지 너무 과도하게 추상화나 코드 설계에 집중하다보면 오히려 생산성도 떨어지게 되고 복잡한 코드가 나올 수 있으니 주의 하시기 바랍니다.