I'm FanJae.
[20260506] C# (캡슐화, 상속, 다형성) 본문
1. 캡슐화 (Encapsulation)
(1) 정의
- 객체 내부의 데이터와 동작을 하나로 묶고, 외부에서 내부 데이터에 직접 접근하지 못하도록 제한하는 것이다.
- private을 쓰는 것이 캡슐화가 아닌, 객체의 상태가 잘못 변경되지 않도록 접근 경로를 통제하는 것이다.
(2) 캡슐화가 필요한 이유
public class Player
{
public int Hp;
}
player.Hp = -100; // 잘못된 값
- 이와 같이 만들면, 외부에서 잘못된 값을 넣는 문제가 발생한다.
(3) 올바른 캡슐화의 적용
public class Player
{
private int hp;
public int Hp
{
get { return hp; }
private set
{
if (value < 0)
hp = 0;
else
hp = value;
}
}
public void TakeDamge(int damage)
{
if(damage < 0) return ;
Hp -= damage;
}
}
- 위와 같이 hp 필드를 private으로 숨기고,외부에서는 Hp 프로퍼티를 통해 값만 읽어올 수 있다.
- 값 변경은 TakeDamage()와 같은 내부 메서를 통해서 이루어지게 만든다.
(4) 정리
- 캡슐화는 다음과 같은 상황에서 사용할 수 있다.
① 외부에서 객체의 상태를 마음대로 바꾸면 안될때
public class Player
{
public int Hp;
}
player.Hp = -100;
player.Hp = 999999;
② 값 변경 전에 검증이 필요할 때
public class Player
{
private int hp;
public int Hp
{
get { return hp; }
private set // 값 변경 전 검증 과정
{
if (value < 0)
hp = 0;
else
hp = value;
}
}
}
2. 상속(Inheritance)
(1) 정의
- 상속은 기존 클래스의 공통 기능을 물려받아서 새로운 클래스를 만드는 것을 의미한다.
- 부모 클래스는 공통 기능을 제공하며, 자식 클래스는 그 기능을 그대로 사용하거나 필요에 따라 확장할 수 있다.
(2) 상속이 필요한 이유
- 예를 들어, Warrior, Mage, Archer라는 클래스가 있다고 한다.
public class Warrior
{
public string Name;
public void Move()
{
Console.WriteLine("이동한다.");
}
}
public class Mage
{
public string Name;
public void Move()
{
Console.WriteLine("이동한다.");
}
}
public class Archer
{
public string Name;
public void Move()
{
Console.WriteLine("이동한다.");
}
}
- 이들은 모두 공통적인 필드인 Name 과 Move() 를 가지고 있다.
- 상속을 사용하면 공통 부분을 부모 클래스로 분리할 수 있다.
① 상속을 적용한 예시
public class Character
{
public string Name;
public void Move()
{
Console.WriteLine("이동한다.");
}
}
public class Warrior : Character
{
public void Attack()
{
Console.WriteLine("검으로 공격한다");
}
}
public class Mage : Character
{
public void Attack()
{
Console.WriteLine("마법으로 공격한다");
}
}
public class Archer : Character
{
public void Attack()
{
Console.WriteLine("활로 공격한다");
}
}
- 위와 같이 : 상속할 클래스명 을 적어주는 방식으로 사용할 수 있다.
- 이제 Warrior, Mage, Archer 는 Name, Move()를 그대로 사용할 수 있다.
- 정리하면 상속은 다음과 같은 목적으로 사용할 수 있다.
1. 공통 기능을 한 곳에 모으기 위하여
2. 중복 코드를 줄이기 위하여
3. 자식 클래스에서 기능을 확장하기 위하여
4. 다형성을 활용하기 위하여
- '다형성'에 대한 것은 잠시 뒤에 다룬다
(3) 상속의 올바른 사용처
- 상속은 주로 is-a 관계일 때 사용한다.
Character
-> Warrior
-> Mage
-> Archer
- Warrior는 Character이다. 와 같은 관계를 표현하기 적당하다.
- 이처럼 여러 클래스가 공통된 성격과 기능을 공유할 때 상속을 사용할 수 있다.
(4) 상속 사용 시 주의할 점
- 상속은 부모 클래스와 자식 클래스의 관계가 강하게 묶인다.
- 즉, 부모 클래스의 구조가 바뀌면 자식 클래스에도 영향을 미친다.
- 따라서, 상속 구조가 깊으면 코드의 추적이 어려워진다.
(5) 포함과 상속의 차이
① 포함(Composition)
- 포함은 한 클래스가 다른 객체를 자신의 멤버로 가지고 사용하는 관계를 의미한다.
- 포함은 주로 has-a 관계로 표현된다.
public class Weapon { }
public class Player
{
private Weapon weapon;
}
- 플레이어는 무기를 가지고 있다. (Player has a Weapon)
- 하지만 플레이어와 무기의 관계를 ‘상속’으로 표현하는 것은 매우 어색하다.
② 상속(Inheritance)
- 반면, 상속은 자식 클래스가 부모 클래스의 기능과 속성을 물려받아 확장받는 관계이다.
- 상속은 주로 is-a 관계로 표현된다.
public class Character { }
public class Warrior : Character
{
}
- 전사는 캐릭터이다. (Warrior is a Character)
- 캐릭터와 전사의 관계는 ‘상속’으로 표현하기에 문제가 없다.
3. 다형성(Polymorphism)
- 해당 항목에 대해 얘기할 때, 오버라이딩(Overriding)의 개념이 등장한다.
- 오버라이딩은 별도 링크로 정리하였다.
- 해당 링크 : C# ( 오버로딩 & 오버라이딩 차이점 )
[20260506] C# ( 오버로딩 & 오버라이딩 차이점 )
1. 메서드 오버로딩 (Method Overloading) (1) 정의- 메서드 오버로딩은 동일한 함수의 이름을 사용해서 매개 변수의 타입이나 갯수 또는 순서를 다르게 하여 같은 이름의 메서드를 여러 개를 선언하거
fanjae.tistory.com
(1) 정의
- 같은 타입으로 객체를 다루지만, 실제 객체의 타입에 따라 서로 다른 동작이 실행되게 하는 OOP 개념이다.
- 보통 부모 클래스 타입의 변수로 자식 클래스 객체를 참조하고, 자식 클래스에서 override 한 메서드가 실행되는 방식으로 나타난다.
Character warrior = new Warrior("홍길동");
Character mage = new Mage("법사");
warrior.Attack();
mage.Attack();
- 여기서 변수 타입은 둘 다 Character 다.
- 하지만 실제 객체는 각각 다르다.
warrior 변수 타입 -> Character, warrior 실제 객체 -> Warrior
mage 변수 타입 -> Character, mage 실제 객체 -> Mage
(2) 다형성이 필요한 이유
- 다형성은 여러 자식 클래스를 하나의 부모 타입으로 묶어서 관리한다.
- 하지만 실제 동작은 각 자식 클래스에 맡기기 위해 필요하다.
① 기존 코드의 수정
public class Character
{
public string Name;
public void Move()
{
Console.WriteLine("이동한다.");
}
}
public class Warrior : Character
{
public void Attack()
{
Console.WriteLine("검으로 공격한다");
}
}
public class Mage : Character
{
public void Attack()
{
Console.WriteLine("마법으로 공격한다");
}
}
public class Archer : Character
{
public void Attack()
{
Console.WriteLine("활로 공격한다");
}
}
- 위에서 언급되었던 코드에서 Attack()에 대한 오버라이딩을 진행하면 아래와 같다.
② Attack()의 오버라이딩 진행
public class Character
{
public string Name;
public void Move()
{
Console.WriteLine("이동한다.");
}
public virtual void Attack()
{
}
}
public class Warrior : Character
{
public override void Attack() // 부모 클래스 attack() 오버라이드
{
Console.WriteLine("검으로 공격한다");
}
}
public class Mage : Character
{
public override void Attack() // 부모 클래스 attack() 오버라이드
{
Console.WriteLine("마법으로 공격한다");
}
}
public class Archer : Character
{
public override void Attack() // 부모 클래스 attack() 오버라이드
{
Console.WriteLine("활로 공격한다");
}
}
- Warrior, Mage, Archer 가 부모 클래스의 Attack() 을 Override 하였다.
- Override 를 하면, 다음과 같은 형태로 사용할 수 있다.
③ 다형성이 적용된 Character
Character[] characters = new Character[]
{
new Warrior { Name = "WarriorFanJae" },
new Mage { Name = "MageFanJae" },
new Thief { Name = "ThiefFanJae"}
};
foreach(Character unit in characters)
{
unit.Attack(); // 다형성에 의해 각자 재정의된 Attack이 실행된다.
}
- 이렇게 한 번에 관리가 가능해진다.
- 다형성을 사용하지 않으면, 이렇게 된다.
④ 다형성이 적용되지 않은 상태 예시
Warrior[] warriors = new Warrior[]
{
new Warrior { Name = "WarriorFanJae" }
};
Mage[] mages = new Mage[]
{
new Mage { Name = "MageFanJae" }
};
Archer[] archers = new Archer[]
{
new Archer { Name = "ArcherFanJae" }
};
foreach(Warrior warrior in warriors)
{
warrior.Attack();
}
foreach(Mage mage in mages)
{
mage.Attack();
}
foreach(Archer archer in archers)
{
archer.Attack();
}
- 각 타입별 배열을 따로 관리하는 형태가 된다.
- 여기서 만약 직업군이 하나 더 추가된다면, 코드가 매우 많이 늘어난다.
(3) 다형성의 원리
- 부모 타입 변수에 자식 객체를 담는 것만으로 다형성이 되는 것은 아니다.
Character character = new Warrior();
- 부모 타입인 Character 변수로 자식 객체인 Warrior를 참조하는 구조다.
- 하지만 실제로 자식 클래스의 동작이 실행되려면, 부모 클래스의 메서드가 virtual로 선언되어 있고, 자식 클래스에 override 하는 과정이 필요하다.
public class Character
{
public string Name;
public void Move()
{
Console.WriteLine("이동한다.");
}
public virtual void Attack()
{
}
}
public class Warrior : Character
{
public override void Attack() // 부모 클래스 attack() 오버라이드
{
Console.WriteLine("검으로 공격한다");
}
}
Character character = new Warrior();
character.Attack();
- 이때 변수 타입은 character지만, 실제 객체는 Warrior이다.
- 따라서 런타임에서는 Warrior가 Overriding한 메서드를 호출한다.
- 이처럼 실행 시점에 실제 객체 타입을 기준으로 호출할 메서드를 결정하는 것을 동적 바인딩이라고 한다.
(4) 다형성 사용 시 주의해야 할 점
- 다형성은 부모 타입으로 여러 자식 객체를 다룰 수 있게 해준다.
- 하지만, 부모 타입에 정의된 멤버만 직접 사용할 수 있다.
Character character = new Warrior();
character.Attack();
character.UseSwordMasterSkill(); // Warrior의 고유스킬이라고 가정하자.
// 이것은 불가능하다.
- character 변수의 실제 객체는 Warrior이다.
- 하지만, 컴파일러는 이 변수를 Character 타입으로 보기 때문에 Warrior에만 있는 메서드를 바로 호출 할 수 없다. (필요시 형변환은 할 수 있지만, 권장되지는 않는다.)
'Unity > Unity 초격차캠프' 카테고리의 다른 글
| [20260507] C# ( 추상 클래스, 인터페이스 ) (0) | 2026.05.07 |
|---|---|
| [20260506] C# ( 오버로딩 & 오버라이딩 차이점 ) (0) | 2026.05.06 |
| [20260504] C# (프로퍼티, 생성자, 메서드 II) (0) | 2026.05.04 |
| [C# 리마인드] 콘솔 스네이크 게임 만들기 (0) | 2026.05.03 |
| [C#] 콘솔 월남뽕 게임 만들기 (1) | 2026.05.01 |