상속의 원리
먼저 추상클래스인 scene 클래스가있따.
public abstract class Scene
{
protected SceneController controller;
public Scene(SceneController controller)
{
this.controller = controller;
}
public abstract void Start();
public abstract void Update();
public abstract void End();
protected ConsoleColor ConvertColor(string colorText)
{
string subText = colorText.Substring(1);
return Enum.TryParse<ConsoleColor>(subText, true, out var color) ?
color :
ConsoleColor.Gray;
// 입력된 문자가 잘못입력되어 매칭되는 색상 없으면 Gray
// 위 true 매개변수가 대소문자 상관없게 하는거
}
........ 생략
}
그리고 그걸 상속받는 추상클래스 DungeonScene 이 있다고할때,,
public abstract class DungeonScene : Scene // 상속을 표시해주면
{
protected DungeonController dungeonController; //이제 여기서 scene의 protected 된 맴버는 안적어줘도됌.
protected DungeonPlayer dungeonPlayer; // 여기서 protected 처리한 맴버들은 이걸 상속받은 애들이 또 이용할수있음.
protected PlayerData playerData;
protected DungeonData monsters;
public DungeonScene(SceneController controller) : base(controller)
{
playerData = GameManager.Instance.PlayerData;
dungeonController = GameManager.Instance.DungeonController;
dungeonPlayer = new(playerData);
monsters = new DungeonData();
}
public override void Start()
{
}
public override void Update()
{
}
public override void End()
{
}
public abstract void show();
public abstract DungeonScene HandleInput(int input);
}
그렇다면 DungeonScene을 상속받은친구는
생성자에 sceneController 만 받아주고,
나머지는 dungeonScene 의 protected 맴버를 그대로 작성해서 기능들을 꺼내쓸수있다.
public class DungeonStartScene : DungeonScene
{
public DungeonStartScene(SceneController controller) : base(controller)
{
}
public override void Start()
{
}
public override void Update()
{
show();
int input = int.Parse(Console.ReadLine());
HandleInput(input);
}
public override void End()
{
}
public override void show()
{
Console.Clear();
Console.WriteLine("[던전입구]");
Console.WriteLine();
Console.WriteLine("던전을 고르시오");
Console.WriteLine();
Console.WriteLine("1. 뉴비던전");
Console.WriteLine("2. 중수던전");
Console.WriteLine("3. 고수던전");
Console.WriteLine();
Console.WriteLine("0. 뒤로가기");
Console.WriteLine();
Console.WriteLine();
Console.WriteLine("원하시는 행동을 입력해주세요.");
Console.Write(">>");
}
public override DungeonScene HandleInput(int input)
{
switch (input)
{
case 1:
dungeonController.SetDungeon("1");
controller.ChangeScene<DungeonBattleScene>();
break;
case 2:
dungeonController.SetDungeon("2");
controller.ChangeScene<DungeonBattleScene>();
break;
case 3:
dungeonController.SetDungeon("3");
controller.ChangeScene<DungeonBattleScene>();
break;
case 0:
controller.ChangeScene<TownScene>();
break;
}
return this;
}
}
}
------------------
라이브강의
객체지향과 클래스
객체지향 프로그램 OOP
정의: 현실세계를 객체라는 단위로 나누어 모델링하고
그 객체들 사이의 상호작용을 통해 프로그램을 구성하는 방식
데이터(속성)와 기능(행동)을 하나의 단위인 객체로 묶어 프로그램을 구성하는 방식
절차지향을하다보면
기능이 많아지면 코드가 복잡해지고
같은 코드를 여기저기서 중복해서 쓰게되고
코드가 길어질수록 수정하거나 유지보수하기 어려워진다.
객체: 속성과 기능을 함께 가진 단위
class Player
{
private int hp;
private int Attack;
public void Attack()
{
Log("공격")
}
}
객체지향장점: 유지보수쉬움, 재사용성, 확장성, 현실세계모델링에 직관적(게임,시뮬레이션등에 유리)
절차지향: 프로그램을 기능단위로 나누워 순서대로 실행하는 방식
객체지향: 데이터를 중심으로 그 데이터와 고나련된 기능ㄹ 묶어서 객체 단위로 처리하는 방식
절차지향: main()에서 순차적으로 기능호출(c)
객체 지향: car 객체를 만들고, Drive() 메서드를 호출 ( c#, Java등)
접근제어자
public 어디서든 접근가능
private 같은 클래스 내부에서만 접근가능
protected 상속받은 클래스에서 접근가능 ->
상속하게되면
자시들도 부모의 변수, 메서드에 접근해줄수있게된다.
생성자/소멸자
생성자
객체생성이 자동으로 호출되는 메서드
클래스 이름과 동일하며 반환형이 없음
소멸자
c#에는
clr 이라는 가상머신이 자동으로 메모리를 해제하는 가비지컬렉터 기술이 있다.
객체를 자동으로 수거해간다.
c#에서는 거의 쓸일이없다.
여하튼 객체가 destroy될때 자동으로 호출되는 친구다.
~Player()
{
Console.WriteLine(${Name} 소멸됨");
}
근데 가비지컬렉터가 수거안해가는 애들이있음.
세이브파일, 푸켓통신, 파일시스템등 출시할때 사용해가는 애들이있음
수거안한상태에서 그냥 쓰다보면 메모리에 남게되는데
이때 쓴다.
IDisposable.Dispose();
는 자주쓴다.
using 문법으로 즉시 호출가능 -> 리소스를 예측 가능하게 해제
가비지콜렉터가 수거안하는 애들을 직접 강제로 해제할때 사용한다.
파일닫기, db 종료, 핸들반환 등 필수작업에 사용
프로퍼티
필드의 접근을 캡슐화하는 기능
get 과 set 을 통해 읽기/쓰기 제어
일반 멤버변수대신 쓰는데
맴버변수는 값을 저장하는변수
프로퍼티 값을 읽고쓰는 인터페이스(함수랑 비슷)
맴버변수는 직접 접근하지만
프로퍼티는 get/set을 통해 간접 접근
사용예
get/set 안에 간단한 로직 가능하다
예외처리등이 가능하다.
private int _hp:
public int HP
{
get { return _hp;}
set
{
if(value>=0)
-hp = value;
}
}
싱글톤도 프로퍼티라는 개념을 많이쓴다.
public static Singltone Instance;
public static SingleTon Instance
{
get
{
if (_instance == null)
{
_instance.Init(); // 초기화 함수 호출
}
return _instance;
}
}
이런식으로 마찬가지로 안에 초기화 함수 호출 조건을 써줄수있음
심화때는
public static Singleton<T>
이런식으로 제너릭으로 쓰고
class GameManger : singtle<GameManager>
이런식으로 상속으로 코드 재사용가능하다.
구조체 (Struct)
클래스와 유사한 값타입
메모리에 스택에 저장됌
유니티 Vector3 사용
데이터가 단순하고 삽입삭제가 매우 빈번하게 일어나는 경우 사용된다.
변수의 크기가 작을때 쓰면 된다.
16바이트인가 넘어가면 클래스로 넘어가버린다.
struct Point
{
public int x; //4byte
public int y; // 4byte
}
Point p1 = new Point { x =1, y=2};
Point p2 = p1; // 참조가아닌 복사가 일어남
직접 내가 구성해서 쓸일은 거의없고
대게, Unity 에서 가져와 사용될것
구조체는 값타입이니까 가비지컬렉터의 수거대상은 아니다.
클래스는 메모리영역중에 힙메모리에 할당된다. 가비지컬렉터는 힙메모리를 관리하는 클래스임
가비지컬렉터 연산들이 많음. new 키워드로 생성한 객체들이 그 예다.
구조체를 사용하게되면 스택에 쌓인다. 가비지가 수거를 안하기때문에
속도가 매우 빠르다는 장점이있다.
그렇기에 매우 빈번하게 사용되는 경우 아주 좋다는 뜻.
스택에 쌓인애들은 스코프를 나가면 사라진다.
구조체는 주로 Vector3, Color, Point 같은 데이터 묶음에서 사용됌.
객체지향4대요소
1.캡슐화
데이터를 외부로부터 숨기고, 필요한 기능만 외부에 노출한다.
필드데이터는 private
외부에서는 메서드나 프로퍼티를 통해서만 접근
외부에서 직접 변수에 접근하지못하도록하여 잘못된 값들어가는것을 방지
혼자개발할때는 상관없지만, 팀플,회사단위일때 인력들이 수시로 바뀐다.
접근할수있는상황이라면 실수가일어날 수있다..
내부구현숨김
컬렉션중에 list를 많이쓰는데 동적할당하는...해당 클래스
list는 클래스이며, list의 add, remove, insert 말고도 다양한 메서드들이있다.
그렇지만 내부사정을 알필요없이 잘쓰고있다.
인터페이스단순화
외부사용자는 내부구조를 몰라도 필요한 기능만 호출하면된다.
유지보수성향상
내부구현을 바꿔도 외부와의 인터페이스가 고정되어있으면 시스템 안정성 유지가된다.
재사용성과 확장성 확보
외부에서 기능을 안전하게 사용가능,
재사용과 테스트가 쉽다.
궁극적인 목표는 외부로부터 보호, 그리고 외부에서는 간단하게 사용.
예시//
using System.Collections.Generic;
Main 함수에
List<int> testList = new List<int>();
testLists.Add(5);
testLists.Add(10);
testLists.Remove(5);
캡슐화가 잘되어있어서 Add, Remove 함수들의 구조를 몰라도 그냥쓸수있음.
2.상속
부모 클래스의 속성과 기능을 자식클래스가 물려받는다.
코드 재사용성을 높이려고
공통기능은 상위클래스에, 구체기능은 하위클래스에
목적: 중복제거
계층적 설계
상위 클래스에서 만든함수를
하위클래스에서 다시 재정의안하고 바로 사용가능하고
밖에서도 하위클래스 객체가 있다면, 그 객체를 통해서도 상위클래스 메서드를 이용할수도있다.
3.다형성
같은 객체가 상황에 따라 다르게 동작한다
Animal 상속받은 dog, cat 클래스가 있고 speak 메서드에 호출이다르다면
Animal dog = new Dog();
Animal cat = new Cat();
dog.Speak(); // 멍멍
cat.Speak(); // 야옹이
override, virtual, interface 사용
하나의 인터페이스로 다양한 객체를 다룰 수있음.
switch, if 같은걸로 분기처리하는것을 최소화할수있음
class Scene :
MainMenu : Scene
Status : Scene
Shop : Scene
Inventory :Scene
Dungeon : Scene
List<scene> Scenes { MainMenu, Status, Shop, Inventory, Dungeon}
Scenes [Mainmenu].Change; 이런식으로 쓸수있다는것..
이렇게함으로서 switch, if 같은걸 덜쓸수있음.
해당 클래스가 고장나면 그 클래스만 고칠 수 있으면 좋음..
abstract class 는 내부구현이 하나도안되어있고
추상화만 되어있고 안에는 정의만 되어있음.
추상메서드도 마찬가지.
virtual은 구현되어있긴할수있지만 override로 변경가능
추상 메서드도 override로 쓸수있음
4.추상화
복잡한 내부구현을 감추고
필요한 기능만 인터페이스로 제공
상속은 상하관계가 명확할때쓰면좋고
인터페이스는 다른시스템의 서비스를 받고싶다할때는
인터페이스를 사용해서 관리하면 좋다.
예를들어 몬스터와 플레이어 클래스가있다.
그리고 몬스터를 상속하는 또 각종 몬스터 서브클래스들도 있다.
그리고 마지막으로 몬스터와 플레이어가 상속받은 IDestroy 라는 인터페이스가있다
인터페이스 IDestroy는 호출시에 파괴시킨다는 메서드를 가지고있다.
IDestroy 타입의 몬스터와 플레이어 모두 계약한 해당 파괴 메서드를 구현한 구현체라면
IDestroy.Destroy()를 통해 몬스터와 플레이어 모두 같은 타입으로 인식하고 파괴시킬수있다.
여기에다 인터페이스는 다중상속이된다.
IHalf 라는 인터페이스안의 반으로 쪼개는 Half() 메서드가 계약되어있다면
이번엔 몬스터만 IHalf 인터페이스를 다중상속받았다.
그리고
IHalf.Half() 를 호출시키면
이번엔 몬스터클래스 객체만 파괴된다.
이때, 몬스터 서브클래스들도 이 몬스터를 상속받았기때문에
해당 실행문들을 모두 적용받을수도있다.
이처럼 인터페이스는 다중상속받아서
전체에 가하는 실행을 선택적으로 받을수있게 해준다.
------
프로퍼티 람다식문법
public DungeonPlayer DungeonPlayerInstance => dungeonplayer;
이것은 get만 사용하는 문법으로
public DungeonPlayer DungeonPlayerInstance
{
get { return dungeonPlayerInstance; }
}
와 같다. 이것은 다른데서 읽어올 수는 있지만
해당값에 새로운것을 할당할수는없다. set이 없으므로
이부분은 보완 필요
------------------
const 는 상수처리 ->런타임내에 변화안됌
public const string Root = " 어쩌고"
'Unity 개발 공부' 카테고리의 다른 글
| [내배캠] 본캠 18일차. 그래프 알고리즘, 다익스트라 (1) | 2025.04.30 |
|---|---|
| [내배캠] 본캠 17일차. 알고리즘, 정렬알고리즘, 선형 탐색알고리즘 (0) | 2025.04.29 |
| [내배캠] 본캠 14일차. 데이타 클래스와 서브클래스, 배열과 리스트 (0) | 2025.04.24 |
| [내배캠] 본캠 13일차. 문자열 처리, 깃허브세팅법,제너릭T와 패턴매칭, 생성자 (5) | 2025.04.23 |
| [내배캠] 본캠 12일차.스테이트 머신, 스테이트패턴, FSM, 제너릭 T, 디자인패턴종류 (0) | 2025.04.22 |