[C#] 클래스와 객체

Flaming Bun·2025년 4월 16일

C#

목록 보기
11/35

🔥 객체지향 프로그래밍의 특징

객체 지향 프로그래밍은 코드 내의 모든 것을 객체(Object)로 표현하고자 하는 프로그래밍 패러다임이다.

특징

  • 캡슐화

    • 관련된 데이터와 기능을 하나의 단위로 묶는 것을 의미한다.
    • 클래스를 사용하여 데이터와 해당 데이터로 조작하는 메서드와 함께 캡슐화하여 정보를 은닉하고, 외부에서 직접적인 접근을 제한함으로써 안정성과 유지보수성을 높인다.
  • 상속

    • 상속은 기존의 클래스를 확장하여 새로운 클래스를 만드는 메커니즘이다.
    • 부모 클래스의 특성과 동작을 자식클래스가 상속받아 재사용할 수 있다.
    • 코드의 중복을 줄이고, 클래스 간 계층 구조를 구성하여 코드의 구조화와 유지보수를 용이하게 한다.
  • 다형성

    • 다형성은 하나의 인터페이스나 기능을 다양한 방식으로 구현하거나 사용할 수 있는 능력을 의미한다.
    • 하나의 메서드 이름이 다양한 객체에서 다르게 동작할 수 있도록 하는 것으로, 오버로딩과 오버라이딩을 통해 구현됩니다.
    • 유연하고 확장 가능한 코드 작성을 가능하게 하며, 코드의 가독성과 재사용성을 높입니다.
  • 추상화

    • 추상화는 복잡한 시스템이나 개념을 단순화하여 본질적이고 필요한 기능에 집중하는 것을 의미한다.
    • 클래스나 인터페이스를 사용하여 실제 세계의 개념을 모델링하고, 필요한 부분에 대한 명세를 정의한다.
    • 세부 구현 내용을 감추고 핵심 개념에 집중함으로써 코드의 이해와 유지보수를 용이하게 한다.

객체

  • 객체는 클래스로부터 생성된 실체로, 데이터와 해당 데이터를 조작하는 메서드를 가지고 있다.
  • 객체는 상테(데이터)와 행동(메서드)를 가지며, 실제 세계의 개체나 개념을 모델링한다.
  • 객체들 간의 상호작용을 통해 프로그램이 동작하고, 모듈화와 재사용성을 높인다.


🔥 구조체 VS 클래스

  • 구조체와 클래스는 모두 사용자 정의 자료 형식을 만드는 데 사용될 수 있다.
  • 구조체는 값 형식이며, 스택에 할당되고 복사될 때 값이 복사된다.
  • 클래스는 참조 형식이며, 힙에 동적 할당되고 참조로 전달되므로 성능 측면에서 다소 차이가 있습니다. (객체가 생성될 때 동적으로 메모리에 할당)
  • 구조체는 상속을 받을 수 없지만 클래스는 단일 상속 및 다중 상속이 가능하다.
  • 구조체는 작은 크기의 데이터 저장이나 단순한 데이터 구조에 적합하며, 클래스는 더 복잡한 객체를 표현하고, 다양한 기능을 제공하기 위해 사용된다.


🔥 접근 한정자

내부 필드, 메소드, 프로퍼티 등 외부에서 어떻게 접근 가능한지 수식해주는 한정자.

객체간의 상호 작용이 중심인 OOP에선 각 객체는 다른 객체에게 자신의 내부 사정을 공유하지 않는다.

다른 객체에게 공유해야 하는 멤버만 접근한정자를 이용하여 공개한다.

접근한정자는 멤버(필드, 메소드 등등)를 외부에 어떤 수준으로 공개할지 지정한다.

  • public: 모든 곳에서 접근 가능
  • protected: 외부에선 접근 불가, 파생클래스에서는 접근 가능
  • private: 클래스 내부에서만 접근 가능. 파생클래스에서도 접근 불가
  • internal: 같은 어셈블리(프로젝트) 내부에서만 접근 가능


🔥 가상 메서드

부모 클래스에서 정의되고 자식 클래스에서 override 한정자를 이용하여 재정의할 수 있는 메서드로
가상 메서드는 미리 virtual로 한정

class Animal
{
	public virtual void Init()
    {
    	Console.WriteLine("초기화");
    }
}

class Dog:Animal
{
	public override void Init()
    {
    	base.Init();
        Console.WriteLine("Dog 초기화");
    }
}


🔥 추상 클래스

특징

  • 메소드의 구현을 가질 수 있음(클래스와 같음)
  • 객체 생성 불가(인터페이스와 같음)
  • 추상 클래스는 abstract 키워드를 사용하여 선언되며, 추상 메서드를 포함할 수 있다.
  • 추상 메서드는 자식클래스에서 반드시 구현해야 한다.


🔥 SOLID 원칙

객체지향 프로그래밍에서 "유지보수 잘 되는 코드"를 만들기 위한 다섯 가지 원칙의 약자.
객체지향에서 클래스를 어떻게 설계하고 책임을 분리하면 좋을지 알려주는 핵심 가이드라인이다.

📌 SRP - 단일 책임 원칙 (Single Responsibility Principle)

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

좋은 예시) Monster 클래스는 Attack(), Die() 등 몬스터 기능만 담당
나쁜 예시) Monster 클래스에 SaveData() 등 저장까지 몬스터가 담당


📌 OCP - 개방/폐쇄 원칙 (Open/Closed Principle)

클래스는 확장에 열려 있고, 변경에는 닫혀 있어야 한다.
다형성과 확장을 위해 추상화 사용을 권장하는 원칙


📌 LSP - 리스코프 치환 원칙 (Liskov Substitution Principle)

자식 클래스는 부모 클래스를 대체할 수 있어야 한다.

자식 타입 객체가 부모 타입의 객체에서 가능한 기능을 수행할 수 있어야 한다.

Bird라는 부모 클래스에 가상 메서드 Fly()가 있다.
좋은 예시) Eagle : Bird -- Can Fly
나쁜 예시) Chick : Bird -- Can't Fly (LSP 위반)

이 경우에 Bird에서 Fly() 함수를 빼고 IFlyingBird 같은 인터페이스로 분리해야 한다.


📌 ISP - 인터페이스 분리 원칙 (Interface Segregation Principle)

인터페이스는 작게 나누어 클라이언트가 자신이 쓰지 않는 메서드에 의존하지 않게 해야 한다.
인터페이스는 작고 구체적으로 분리 해야한다.

인터페이스 단일 책임을 강조하는 것

좋은 예시) interface IAnimal { void Sleep(); void Eat(); }
나쁜 예시) interface IAnimal { void Sleep(); void Fly(); } // 모든 동물이 날지 않는다.


📌 DIP - 의존 역전 원칙 (Dependency Inversion Principle)

구현이 아닌 추상(인터페이스)에 의존해야 한다.
클래스는 구체적인 클래스가 아니라, 코드의 결합도를 낮추기 위해 인터페이스나 추상 클래스에 의존해야 한다.

좋은 예시)

interface IWeapon
{
   void Attack();
}

class Gun : IWeapon
{
   public void Attack() => Console.WriteLine("BAAANG!");
}

class Sword : IWeapon
{
   public void Attack() => Console.WriteLine("WHOOOSH!");
}

class Player
{
   private IWeapon weapon;

   public Player(IWeapon weapon) => this.weapon = weapon;

   public void Attack() => weapon.Attack();
}

나쁜 예시)

class Gun
{
    public void Shoot() => Console.WriteLine("Bang!");
}

class Player
{
   private Gun gun = new Gun();

   public void Attack() => gun.Shoot();
?}

0개의 댓글