<객체지향 프로그래밍(Object-Oriented Programming, OOP)의 특징>
1) 추상화 (Abstraction)
● 관련 특성 및 엔터티의 상호 작용을 클래스로 모델링하여 시스템의 추상적 표현을 정의
● 클래스가 추상적인 표현을 정의하는 경우 자식에서 구체화시켜 구현할 것을 염두하고 추상화(abstract) 시킴
● 추상클래스를 상속하는 자식클래스가 추상화한 함수를 재정의하여 실체화한 경우 사용가능
● 클래스나 인터페이스를 사용하여 실제 세계의 개념을 모델링하고, 필요한 부분에 대한 명세를 정의
+예시
public abstract class Animal
{
// 추상적인 클래스에서 구체화 할 수 없는 함수는 하나이상의 추상함수를 선언하고 내용을 정의하지 않음
public abstract void Cry();
}
public class Bird : Animal
{
// 추상함수를 재정의하여 실체화한 경우 사용 가능 (반드시 넣어야함 자식클래스를 만들수있다)
public override void Cry()
{
Console.WriteLine("짹짹");
}
}
public class Dog : Animal
{
public override void Cry()
{
Console.WriteLine("멍멍");
}
}
public void Test()
{
// Animal animal = new Animal(); // error : 추상클래스의 인스턴스는 생성불가
Animal bird = new Bird(); // 추상클래스를 구체화시킨 자식클래스의 인스턴스는 생성가능
Animal dog = new Dog();
bird.Cry();
dog.Cry();
Katar katar = new Katar();
katar.Attack();
}
2) 캡슐화 (Encapsulation)
● 객체를 상태와 기능으로 묶는 것을 의미
* 정보은닉 : 객체의 내부 상태와 기능을 숨기고, 허용한 상태와 기능만의 액세스 허용
● 객체를 상태와 기능으로 묶는 것, 객체의 상태와 기능을 맴버라고 표현함(맴버변수==상태, 맴버함수==기능)
● 현실세계의 객체를 표현하기 위해 객체가 가지는 정보와 행동을 묶어 구현하며 이를 통해 객체간 상호작용
● C# 의 맴버로는 맴버변수(필드), 맴버함수(메서드), 상수, 속성(프로퍼티), 생성자, 이벤트, 연산자, 인덱서 등이 있음
+예시
public class Player
{
public int hp; // 외부에서 접근 가능
private int mp; // 접근제한자를 지정하지 않은 경우 기본접근제한자 private
public Player(int hp, int mp)
{
this.hp = hp;
this.mp = mp;
}
public void TakeDamage(int damage)
{
hp -= damage;
// 피격음 재생
// 넉백 등
}
public void UseMagic(int cost)
{
mp -= cost;
}
}
public static class Encapsulation
{
public static void Test()
{
Player player = new Player(100, 20);
player.TakeDamage(20);
player.UseMagic(10);
player.hp = 0; // public으로 선언한 경우 외부에서 접근 가능(의도에 벗어날 수 있음)
// player.mp = 0; // private로 선언한 경우 외부에서 접근 불가(public으로 의도한 사용만 허용)
}
}
3) 상속 (Inheritance)
● 부모클래스의 모든 기능을 가지는 자식클래스를 설계하는 방법 (가능하면 모든기능을 공유할수있는 것으로 부모클래스에 넣어주면 좋다)
● is-a 관계 : 부모클래스가 자식클래스를 포함하는 상위개념일 경우 상속관계가 적합함 // 생물학에 종분류같이 해주는게 좋음
+예시
internal class Inheritance
{
public static void Test()
{
Person person = new Person("Kim");
Student student = new Student("Lee", 1);
Worker worker = new Worker("Park");
// 부모클래스 Person을 상속한 자식클래스는 모두 부모클래스의 기능을 가지고 있음
person.SayName();
student.SayName();
worker.SayName();
// 자식클래스는 부모클래스의 기능에 자식만의 기능을 더욱 추가하여 구현 가능
student.Study();
worker.Work();
// 부모클래스에 자식 인스턴스를 담아둘 수 있음
Person studentInPerson = new Student("Choi", 2);
Person workerInPerson = new Worker("Jung");
// 자식클래스는 부모클래스의 모든 기능이 있기 때문에 부모의 기능 사용 가능 //부모자리를 대신하는 리스코프 치환이 가능하다
studentInPerson.SayName();
workerInPerson.SayName();
// 부모클래스에 담은 자식 인스턴스는 자식 인스턴스만의 기능을 사용할 수 없음
// studentInPerson.Study(); // error : studentInPerson은 Person에 담겨 있어 Study가 없음
// workerInPerson.Work(); // error : workerInPerson은 Person에 담겨 있어 Worker가 없음
// 자식클래스의 기능을 사용하고 싶다면 다시 자식클래스에 담아서 사용가능
Student castStudent = (Student)studentInPerson;
castStudent.Study();
// 부모클래스에 담겨있는 자식 인스턴스가 자식클래스로 다시 자식클래스에 담는 것을 정적형변환으로 할 경우 위험할 수 있음
// 담겨있던 인스턴스가 형변환이 불가한 경우 예외 발생
// Student castFail = (Student)workerInPerson; // error : 담겨있던 인스턴스가 형변환이 불가함
// is : 담겨있는 인스턴스가 형변환이 가능한지 확인
if (studentInPerson is Student)
{
Console.WriteLine("{0} 인스턴스는 Student로 형변환 가능");
Student cast = (Student)studentInPerson;
}
else
{
Console.WriteLine("{0} 인스턴스는 Student로 형변환 불가");
}
if (studentInPerson is Worker)
{
Console.WriteLine("{0} 인스턴스는 Worker로 형변환 가능");
Worker cast = (Worker)studentInPerson;
}
else
{
Console.WriteLine("{0} 인스턴스는 Worker로 형변환 불가");
}
// as : 담겨있는 인스턴스가 형변환이 가능하다면 형변환 결과를 불가하다면 null을 반환
Student asStudent = studentInPerson as Student; // 형변환
Worker asWorker = studentInPerson as Worker; // null 반환
}
}
4) 다형성 (Polymorphism)
● 부모클래스의 함수를 자식클래스에서 재정의하여 자식클래스의 서로 다른 반응을 구현
● 하나의 메서드 이름이 다양한 객체에서 다르게 동작할 수 있도록 하는 것으로, 오버로딩과 오버라이딩을 통해 구현됩니다.
● 하이딩(hiding) : 부모클래스의 함수를 자식클래스에서 같은 이름, 매개변수로 재정의하여 자식클래스가 우선되도록 함
● 오버라이딩(overriding) : 부모클래스의 가상함수를 자식클래스에서 override를 통해 재정의, 부모함수가 자식함수로 대체됨
+예시
public class Monster
{
public string name;
public Monster(string name)
{
this.name = name;
}
public void Move()
{
Console.WriteLine("{0}이 움직입니다.", name);
}
public virtual void TakeDamage() // 가상함수(virtual) : 자식에서 재정의할 경우 부모의 함수가 대체됨
// 부모클래스에서 이렇게 작동하되 자식클래스에서는 다르게 작동되길 원하면 사용
{
Console.WriteLine("{0}가 데미지를 받습니다.", name);
}
}
public class Dragon : Monster
{
public Dragon(string name) : base(name)
{
}
// 하이딩 : 부모클래스의 함수를 자식클래스에서 같은 이름, 매개변수로 재정의하여 자식클래스가 우선되도록 함
// 자식클래스를 부모클래스에 넣게될 경우 하이딩은 작동하지만 자식클래스가 우선적으로 되지는 않는다
public new void Move() // 부모클래스의 기능을 숨기는 것을 의도한 경우 new 키워드(없어도 동작하나 의도했다는 것을 명확하게 함)
{
Console.WriteLine("{0}이 날아서 움직입니다.", name);
}
// 오버라이딩
public override void TakeDamage()
{
base.TakeDamage(); // base : 부모클래스의 this를 자식클래스에서 가져옴
Console.WriteLine("{0}이 분노합니다", name);
}
}
public void Test()
{
Monster monster = new Monster("몬스터");
Dragon dragon = new Dragon("드래곤");
monster.Move();
dragon.Move();
Monster dragonInMonster = dragon;
dragonInMonster.Move();
dragonInMonster.TakeDamage();
Console.WriteLine();
}
<객체설계 5원칙> (지향 5원칙) |
● (S)단일 책임 원칙 : 객체는 오직 하나의 책임을 가져야 함(300줄이하 소스로) // 여러책임을 관리하는 객체를 수정할때 복잡함을 덜기위해 수정 및 관리의 용이성을 위해 분산으로 구조를 짜는걸 추천한다 ● (O)개방 폐쇄 원칙 : 객체는 확장에 대해서는 개방적이고 수정에 대해서는 폐쇄적이어야 함 // 어디에 써도 잘되는 객체면서 수정이 굉장히 적은 객체면 좋음 ● (L)리스코프 치환 원칙 : 자식클래스는 언제나 자신의 부모클래스를 대체할 수 있어야 함 // 부모 - 자동차, 자식 - 차종류들 //자식클래스에서 부모클래스에서 기대할수있는 것이 나와야한다 ● (I)인터페이스 분리 원칙 : 인터페이스(동작단위)는 작은 단위들로 분리시켜 구성하며, 사용하지 않는 함수는 포함하지 않아야 함 ● (D)의존성 역전 원칙 : 추상성이 높고 안정적인 고수준의 클래스는 구체적이고 불안정한 저수준의 클래스에 의존하지 않아야 함 // 부모클래스가 자식클래스에 의존하지 않는것 처럼 되는 상황을 만들면 안됨 ● (S)단일 책임 원칙 : 10줄짜리 50개와 500개 짜리 하나중 10줄짜리 50개가 더 나을수있음 |
클래스 (class) |
● 데이터와 관련 기능을 캡슐화할 수 있는 참조 형식 ● 객체지향 프로그래밍에 객체를 만들기 위한 설계도 //절차지향이란 위에서 부터 아래로 차례로 절차를 행하는 것을 말한다 ● 클래스는 객체를 만들기 위한 설계도이며, 만들어진 객체는 인스턴스라 함 ● 참조 : 원본을 가리키고 있음 == 원본의 주소를 가지고 있음 |
<클래스 구성>
◆ class 클래스이름 {클래스내용}
class 클래스이름
{클래스내용}
● 예시
class Student
{
public string name;
public int math;
public int english;
public float GetAverage()
{
return (math + english) / 2f;
}
}
static void Main(string[] args)
{
Student student = new Student(); //인스턴스의 변수에 접근하기 위해 변수에
student.name = "김감자";
student.math = 5;
student.english = 10;
student.GetAverage(); // 인스턴스의 함수에 접근하기 위해 변수에
}
1) 생성자
● 반환형이 없는 클래스이름의 함수를 생성자라 하며 클래스의 인스턴스를 만들 때 호출되는 역할로 사용
● 힙영역에 초기화 값을 생성하는 것을 말함
● 생성자를 포함하지 않아도 기본 생성자(매개변수가 없는 생성자)는 자동으로 생성됨
● 기본 생성자 외 매개변수가 있는 생성자를 포함한 경우 기본 생성자는 자동으로 생성되지 않음
● 예시
class Player
{
public string name;
public int hp;
public int mp;
// 기본 생성자는 다른 생성자를 포함하지 않은 경우만 기본 생성됨, 생성자로 초기화 하지않아도 문제는 없다
// 하나라도 생성하면 기본생성자는 생성안됨
/*public Player()
{
}*/
public Player(string name, int hp)
{
// 생성자에서 초기화 하지 않은 맴버 변수는 기본값으로 초기화됨
this.name = name;
this.hp = hp;
}
}
static void Test3()
{
Player player = new Player("김감자", 20);
}
<값형식 참조형식>
1) 값형식
● 스택영역에 저장, 정적으로 메모리에 할당
● 복사할 때 실제값이 복사됨, 블록이 끝날때 소멸
● struct는 값형식
2) 참조형식
● 힙영역에 저장, 동적으로 메모리에 할당
● 복사할 때 원본주소가 복사됨, 사용하지 않을때 가비지 컬렉터에 의해 소멸
● class는 참조형식
● 값형식과 참조형식 예제
3) 값형식과 참조형식의 차이
● 값형식 : 데이터가 중요, 값이 복사됨
● 참조형식 : 원본이 중요, 원본 주소가 복사됨
● 값형식과 참조형식 차이점 예제
struct ValueType
{
public int value;
}
class RefType
{
public int value;
}
static void Main2()
{
ValueType valueType1= new ValueType() { value = 10 };
ValueType valueType2 = valueType1; // 값에 의한 복사
valueType2.value = 20;
Console.WriteLine(valueType1.value); // output : 10으로 구조체는 처음 값이 변하지 않는다
RefType refType1 = new RefType() { value = 10 };
RefType refType2 = refType1; // 원본의 주소가 복사댐
// RefType refType2 = null; // 값을 지정안하고 기본값을 사용한다는 내용임
refType2.value = 20;
Console.WriteLine(refType2.value); // output : refType1, 2 둘다 20으로 바뀜
}
4) ref
● ref키워드를 통해 값형식을 원본으로 참조하는 방법을 지원함
● 예제
4) 박싱, 언박싱
● 박싱 : 값형식의 데이터를 참조형식으로 변환하는 방법
● 언박싱 : 참조형식의 데이터를 값형식으로 변환하는 방법
● 형변환된 값에 다른 값이 들어올경우 프로그램 오류가 발생한다
● 예제
static void Swep3(object left, object right)
{
object a = 1; // object에 넣는건 박싱
object b = true;
object c = 1.1;
object str = "감자";
int temp = (int)left; // 오브젝트의 형변환을 하는건 언박싱
right = left;
left = temp;
}
static void Test() // 이 경우 함수에 값을 특정할수없음
// 이렇게 runtime 도중 값을 할당하는 값은 heep영역에 할당해서 값을 불러오는식으로 해야함
// class에 짜야함
{
Console.Write("숫자를 입력하세요 : ");
string str = Console.ReadLine();
int[] life = new int[int.Parse(str)];
}
//출력하면 두자리가 바뀌어있음
static void Main3()
{
// 데이터가 중요한 상황 (원본 체력값이 변하지 않길바란다면)에서는 구조체로 작성하는게 좋음
RefType a = new RefType() { value = 10 };
RefType b = new RefType() { value = 20 }; // new RefType()라고 쓰면 힙영역에 저장된다
Swep(a, b);
Console.WriteLine(a);
Console.WriteLine(b);
}
일반화 (Generic) |
● 클래스 또는 함수가 코드에 의해 선언되고 인스턴스화될 때까지 형식의 사양을 연기하는 디자인 ● 거의 모든 부분이 동일하지만 일부 자료형만이 다른 경우 사용 |
● 예제
1) 일반화 자료형 제약
● 일반화 자료형을 선언할 때 제약조건을 선언하여, 일반화가 가능한 자료형을 제한
● 클래스 객체생성하는것 처럼 특정 연산에 대한 자료형으로 제약할수있음
● 예제
class StructClass<T> where T : struct { }
class ClassClass<T> where T : class { }
class ParentClass{ }
class ChildClass<T> where T: ParentClass { }
class InterfaceClass<T> where T : IComparable { }
class InterfaceClass1<T> where T : ParentClass, IComparable { }
class InterfaceClass2<T> where T : IComparable, IEnumerable { }
2.
public class Monster
{
public void Attack()
{
Console.WriteLine("Start");
}
}
public void Test3<T>(T param) where T : Monster
{
param.Attack();
}
'유니티개발 TIL' 카테고리의 다른 글
3주차 1일 TIL_TextRPG심화(던전메이커) (0) | 2025.07.14 |
---|---|
2주차 4~5일 TIL_C#기초_게임 만들기(TextRPG) (0) | 2025.07.11 |
2주차 2일 TIL_C#기초-2(조건문과 반복문, 매서드) (15) | 2025.07.09 |
2주차 1일 TIL_C#기초-1(자료형, 연산자) (4) | 2025.07.07 |
1주차 5일 TIL - 간단한 기능 모작(포켓몬스터-02), TIL정리 (1) | 2025.07.04 |