객체 지향 프로그래밍의 4가지 특징은 캡슐화, 상속, 다향성, 추상화이다.
캡슐화는 객체의 속성과 메서드를 하나로 묶고, 외부에서 직접 접근하지 못하게 보호하는 개념이다.
C#의 접근 제한자를 알고 가면 좋을 거 같다.

캡슐화 기본 예제
using System;
class Person
{
private string _name;
public string GetName() // Getter: 값을 가져오는 메서드
{
return _name;
}
public void SetName(string newName) // Setter: 값을 변경하는 메서드
{
_name = newName;
}
}
class Program
{
static void Main()
{
Person person = new Person();
person.SetName("Alice"); // 값을 설정
Console.WriteLine(person.GetName()); // 출력: Alice
}
}
private 필드 + public 속성 사용 (C# 스타일)
using System;
class Person
{
private string _name; // private 필드
public string Name // 속성(Property)
{
get { return _name; } // Getter
set { _name = value; } // Setter
}
}
class Program
{
static void Main()
{
Person person = new Person();
person.Name = "Bob"; // Setter 호출
Console.WriteLine(person.Name); // Getter 호출, 출력: Bob
}
}
private set을 사용한 읽기 전용 속성
using System;
class Car
{
public string Model { get; private set; } // 외부에서 수정 불가
public Car(string model)
{
Model = model; // 생성자를 통해 한 번만 값 설정 가능
}
}
class Program
{
static void Main()
{
Car car = new Car("Hyundai");
Console.WriteLine(car.Model); // 출력: Hyundai
// car.Model = "Kia"; // Model은 private set이므로 외부에서 변경 불가
}
}
상속은 부모 클래스의 기능을 자식 클래스가 물려받아 재사용할 수 있도록 하는 개념이다.
중복 코드를 줄이고 유지보수를 쉽게 하기 위해 필요하다.
부모 클래스의 기능을 확장하여 새로운 기능을 추가할 수 있다.
상속 기본 예제
using System;
// 부모 클래스 (Base Class)
class Animal
{
public string Name { get; set; }
public void Eat()
{
Console.WriteLine($"{Name} is eating.");
}
}
// 자식 클래스 (Derived Class) - Animal을 상속받음
class Dog : Animal
{
public void Bark()
{
Console.WriteLine($"{Name} is barking.");
}
}
class Program
{
static void Main()
{
Dog myDog = new Dog();
myDog.Name = "Buddy"; // 부모 클래스의 속성 사용
myDog.Eat(); // 부모 클래스의 메서드 사용
myDog.Bark(); // 자식 클래스의 메서드 사용
}
}
생성자(Constructor) 상속과 base 키워드 사용
using System;
class Animal
{
public string Name { get; set; }
// 부모 클래스 생성자
public Animal(string name)
{
Name = name;
}
public void Eat()
{
Console.WriteLine($"{Name} is eating.");
}
}
// 자식 클래스에서 부모 클래스의 생성자 호출 (base 사용)
class Dog : Animal
{
public Dog(string name) : base(name) // 부모 생성자 호출
{
}
public void Bark()
{
Console.WriteLine($"{Name} is barking.");
}
}
class Program
{
static void Main()
{
Dog myDog = new Dog("Charlie");
myDog.Eat(); // 부모 클래스 메서드
myDog.Bark(); // 자식 클래스 메서드
}
}
virtual과 override로 메서드 재정의(Overriding)
using System;
class Animal
{
public string Name { get; set; }
public Animal(string name)
{
Name = name;
}
// virtual: 자식 클래스에서 재정의 가능하도록 설정
public virtual void Speak()
{
Console.WriteLine($"{Name} makes a sound.");
}
}
class Dog : Animal
{
public Dog(string name) : base(name) {}
// override: 부모 클래스의 메서드를 재정의
public override void Speak()
{
Console.WriteLine($"{Name} barks.");
}
}
class Program
{
static void Main()
{
Animal myAnimal = new Animal("Generic Animal");
myAnimal.Speak(); // 부모 클래스의 메서드 실행
Dog myDog = new Dog("Max");
myDog.Speak(); // 자식 클래스에서 재정의한 메서드 실행
}
}
다향성은 "하나의 객체가 여러 형태를 가질 수 있다"는 개념입니다. C#에서 다형성은 주로 상속과 메서드 재정의(override)를 통해 구현된다. 다형성을 활용하면 부모 클래스의 타입으로 자식 클래스의 메서드를 호출할 수 있고, 자식 클래스에서 부모 클래스의 메서드를 재정의(Override)하여 다양한 동작을 구현할 수 있다.
다향성의 종류로
1. 컴파일 타임 다형성 (정적 다형성): 오버로딩(메서드 이름이 같지만 매개변수가 다른 경우)
2. 런타임 다형성 (동적 다형성): 오버라이딩(상속 관계에서 메서드를 재정의)
오버로딩 예제
using System;
class Calculator
{
// 두 정수 더하기 (정수 오버로딩)
public int Add(int a, int b)
{
return a + b;
}
// 두 실수 더하기 (실수 오버로딩)
public double Add(double a, double b)
{
return a + b;
}
// 세 정수 더하기 (매개변수 개수 오버로딩)
public int Add(int a, int b, int c)
{
return a + b + c;
}
}
class Program
{
static void Main()
{
Calculator calc = new Calculator();
// 두 정수 더하기
Console.WriteLine("정수 더하기: " + calc.Add(5, 3)); // 출력: 8
// 두 실수 더하기
Console.WriteLine("실수 더하기: " + calc.Add(5.5, 3.2)); // 출력: 8.7
// 세 정수 더하기
Console.WriteLine("세 정수 더하기: " + calc.Add(5, 3, 2)); // 출력: 10
}
}
오버라이딩 예제
using System;
class Animal
{
// 부모 클래스의 메서드 (기본 동작)
public virtual void Speak()
{
Console.WriteLine("Animal makes a sound.");
}
}
class Dog : Animal
{
// Dog 클래스에서 Speak() 메서드를 재정의
public override void Speak()
{
Console.WriteLine("Dog barks.");
}
}
class Cat : Animal
{
// Cat 클래스에서 Speak() 메서드를 재정의
public override void Speak()
{
Console.WriteLine("Cat meows.");
}
}
class Program
{
static void Main()
{
Animal myAnimal = new Animal();
myAnimal.Speak(); // Animal makes a sound.
Animal myDog = new Dog(); // Animal 타입으로 Dog 객체 생성
myDog.Speak(); // Dog barks. (다형성: 실제 객체는 Dog 클래스)
Animal myCat = new Cat(); // Animal 타입으로 Cat 객체 생성
myCat.Speak(); // Cat meows. (다형성: 실제 객체는 Cat 클래스)
}
}
추사화는 객체의 핵심적인 특징만을 공개하고, 불필요한 세부 구현을 숨기는 개념이다.
C#에서는 추상 클래스(abstract class)와 인터페이스(interface)를 사용하여 추상화를 구현할 수 있다.
추상 클래스는 공통적인 기능(일반 메서드 포함) 을 제공할 수 있으며, 구체적인 동작은 자식 클래스에서 구현하도록 강제할 수도 있다.
인터페이스는 순수하게 "해야 할 일(행동)"만 정의하고, 그 구현 방식은 클래스가 자유롭게 결정한다.
언제 사용해야 하는가?
"is-a 관계"가 필요하면 추상 클래스
→ 예: Animal(추상 클래스) → Dog, Cat(하위 클래스)
→ 공통된 필드와 메서드를 제공하고, 일부 기능을 하위 클래스에서 구현하도록 강제하고 싶을 때 사용한다.
"can-do 관계"가 필요하면 인터페이스
→ 예: Flyable(인터페이스) → Bird, Airplane (구현 클래스)
→ 특정 기능(예: 날기, 수영하기 등)을 여러 클래스에서 공통적으로 사용하고 싶을 때 사용한다.
추상 클래스 예제
using System;
abstract class Animal
{
public abstract void MakeSound(); // 추상 메서드 (구현 X)
}
class Dog : Animal
{
public override void MakeSound() => Console.WriteLine("멍멍");
}
class Program
{
static void Main()
{
Animal dog = new Dog();
dog.MakeSound(); // 출력: 멍멍
}
}
인터페이스 예제
using System;
interface IFlyable
{
void Fly();
}
class Bird : IFlyable
{
public void Fly() => Console.WriteLine("짹짹");
}
class Program
{
static void Main()
{
IFlyable bird = new Bird();
bird.Fly(); // 출력: 짹짹
}
}
추가)
장점
코드 재사용성 (재활용 가능) → 상속을 활용해 기존 코드를 재사용 가능
유지보수 용이 → 캡슐화를 통해 코드 수정 시 영향이 최소화됨
확장성 높음 → 새로운 기능 추가가 쉽고 유연한 구조 제공
가독성 향상 → 객체 단위로 논리적으로 코드 구성 가능
보안성 강화 → 데이터 은닉(캡슐화)으로 직접 접근을 막고 안전한 데이터 처리 가능
단점
복잡한 설계 → 구조 설계가 어렵고 개발 시간이 증가할 수 있음
메모리 사용 증가 → 객체 생성 및 관리로 인해 절차적 프로그래밍보다 메모리 소모가 많음
실행 속도 저하 → 동적 바인딩(다형성 등)으로 인해 절차적 언어보다 속도가 느릴 수 있음
초기 개발 비용 증가 → 설계 단계에서 시간과 비용이 많이 소요될 수 있음
추가)
맞다 하지만, 상속과 참조(Composition)는 목적이 다름
상속(Inheritance): "is-a" 관계 (예: Dog는 Animal이다)
참조(Composition): "has-a" 관계 (예: Car는 Engine을 가진다)
언제 상속을 쓰고, 언제 참조를 쓸까?
상속 → "is-a" 관계일 때 (Student는 Person이다)
참조(Composition) → "has-a" 관계일 때 (Car는 Engine을 가진다)
즉, 상속은 공통 기능을 재사용하고 다형성을 활용하기 위한 것이며, 단순한 참조로는 상속이 제공하는 유연성과 확장성을 얻기 어려움