유니티 C# OOP와 SOLID에 대해 알아보자 #디자인패턴

OOP란?

Object-Oriented Programming의 약자로 한글로 설명하면 객체 지향 프로그래밍이라는 뜻입니다.

현실 세계의 객체를 모델링하여 소프트웨어를 개발하는 방법론입니다.

 

현실 세계의 객체라..?🤔

객체라고 단어가 약간 이해하기 어려울 수 있습니다. 객체라는 것은 데이터와 기능을 하나의 단위로 묶은 것을 뜻합니다. 예를 들어, 김진우라는 객체가 있다고 가정하겠습니다.

 

김진우라는 객체는 이름은 김진우이며, 키 177, 25살, 남자라는 데이터를 가지고 있습니다.

그리고 저는 밥 먹고, 걷고, 공부하는 등의 기능을 실행할 수 있습니다.

 

김진우라는 객체에서 데이터를 코드로 표현하면 다음과 같이 나타낼 수 있습니다.

public class Person {
    public string Name { get; set; }
    public float Height { get; set; }
    public int Age { get; set; }
    public string Gender { get; set; }
}

 

김진우라는 객체에서 기능을 코드로 표현하면 다음과 같이 나타낼 수 있습니다.

public class Person {
    /* Data */
    
    public void Walk() {
        //걷기
    }
    
    public void Work() {
        //공부
    }
    
    public void Rest() {
        //휴식
    }
}

 

정리하면, 객체라는 것은 데이터와 기능을 묶은 단위이며 현실세계의 대부분의 물체는 객체라고 나타낼 수 있습니다.

OOP는 그러한 객체를 프로그래밍한다고 생각하면 될 것 같습니다.

 


OOP의 특성 및 장점

그렇다면 왜 OOP라는 개념이 생겨나게 되었을까요?

유니티에서 사용하는 언어는 C#이며 객체지향 언어입니다. 코딩을 처음 배울 때 배우는 언어인 C의 경우 절차지향 언어로 OOP를 지원하지 않습니다. 

 

객체지향 - 데이터와 기능을 객체 단위로 묶음
- 클래스와 객체를 사용하여 프로그램을 구조화
절차지향 - 함수와 데이터가 분리되어 있으며, 기능 중심
- 함수와 변수를 사용하여 프로그램을 구조화

 

(좌) 객체지향 / (우) 절차지향

 

객체지향의 경우 데이터와 기능이 객체 단위로 묶여서 사용됩니다.

절차지향의 경우 데이터를 담는 부분과 기능을 수행하는 함수가 따로 분리되어 사용됩니다.

 

코드를 보고 싶다면 아래 토글을 열어주세요🔽

더보기

객체지향의 경우 데이터와 기능을 Class안에 담고 있습니다.

public class Person {
    public string Name { get; set; }
    public int Age { get; set; }
    
    public void Speak(string message) {
        Console.WriteLine(Name + " says: " + message);
    }
    
    public void Walk() {
        Console.WriteLine(Name + " is walking.");
    }
}

public class Program {
    public static void Main(string[] args) {
        // 사람 객체 생성
        Person person = new Person();
        person.Name = "Alice";
        person.Age = 30;
        
        person.Speak("Hello!");
        person.Walk();
    }
}

 

절차지향의 경우 데이터와 함수가 분리되어 있습니다.

#include <stdio.h>
#include <string.h>

// 데이터 구조체
typedef struct {
    char name[50];
    int age;
} Person;

void Speak(Person *p, const char *message) {
    printf("%s says: %s\n", p->name, message);
}

void Walk(Person *p) {
    printf("%s is walking.\n", p->name);
}

int main() {
    Person person;
    strcpy(person.name, "Alice");
    person.age = 30;
    
    Speak(&person, "Hello!");
    Walk(&person);
    
    return 0;
}

 

절차 지향 언어는 클래스라는 개념이 없기에 데이터와 기능을 묶는 것이 어렵습니다.

절차 지향의 경우 함수 중심으로, 함수의 집합으로 구성됩니다. 

 

정리를 하자면, 객체 지향 프로그래밍 개념이 나오게 된 이유는 개발의 복잡성을 관리하고, 코드의 재사용성과 확장성, 유지보수성을 높이기 위해 등장하게 된 개념입니다.


 

객체지향(OOP) 프로그래밍을 지키기 위한 원칙

  1. 캡슐화 (Encapsulation)
    • 관련된 데이터와 메서드를 하나의 단위로 묶음
    • 데이터를 외부로부터 보호하고, 오직 메서드를 통해서만 접근 가능하게 함
    • 캡슐화를 통해 데이터의 유효성을 유지하고 코드의 재사용성을 높임
  2. 상속 (Inheritance)
    • 기존 클래스의 특성을 다른 클래스가 상속받아 확장할 수 있음
    • 상속을 통해 코드의 재사용성을 높일 수 있으며, 클래스 간의 계층 구조를 형성
  3. 다형성 (Polymorphism)
    • 동일한 인터페이스를 사용하여 서로 다른 구현체(실제로 구현한 클래스를 의미)를 사용할 수 있음
    • 다형성을 통해 코드의 유연성을 높이고, 코드의 가독성을 개선할 수 있음
  4. 추상화 (Abstraction)
    • 공통된 특성이나 동작을 추출하여 일반화하는 과정
    • 추상화를 통해 객체들 간의 공통된 특성을 분리하여 코드를 간결하게 만들 수 있음

SOLID 원칙이란?

SOLID 원칙은 객체지향 프로그래밍에서 소프트웨어의 설계와 구현을 개선하기 위한 다섯 가지 원칙의 약어입니다.

 

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

하나의 클래스는 하나의 책임만 가져야 합니다. 


2. 개방-폐쇄 원칙 (Open-Closed Principle, OCP)

클래스, 모듈, 함수는 확장에는 열려 있어야 하지만 수정에는 닫혀 있어야 합니다. 즉, 기존의 코드를 수정하지 않고도 새로운 기능을 추가할 수 있어야 합니다. 


3. 리스코프 치환 원칙 (Liskov Substitution Principle, LSP)

자식 클래스는 부모 클래스의 역할을 완전히 대체할 수 있어야 합니다. 즉, 부모 클래스의 인스턴스 대신에 자식 클래스의 인스턴스를 사용해도 동일한 결과가 나와야 합니다. 


4. 인터페이스 분리 원칙 (Interface Segregation Principle, ISP)

사용하지 않는 인터페이스에 의존하도록 강요되지 말아야 합니다. 즉, 인터페이스를 클라이언트의 요구에 따라 분리하여 불필요한 의존성을 제거합니다.


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

고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 모두 추상화에 의존해야 합니다. 즉, 추상화에 의존하고, 구체적인 구현체에는 의존하면 안 됩니다. 


 

 

디자인패턴

 

더 자세한 내용을 참고하고 싶다면 유니티 코리아 유튜브를 시청하시면 좋을 것 같습니다.

30분부터 보면 되겠습니다!