객체 지향 프로그래밍의 5가지 원칙

PTK·2025년 2월 19일
0

객체 지향 프로그래밍의 5가지 원칙은 SOLID 이다.
SOLID는 SRP(단일 책임 원칙), OCP(개방-폐쇄 원칙), LSP(리스코프 치환 원칙), ISP(인터페이스 분리 원칙), DIP(의존 역전 원칙)의 앞글자를 따서 만들어졌다. SOLID 원칙을 철저히 지키면 시간이 지나도 변경이 용이하고, 유지보수와 확장이 쉬운 소프트웨어를 개발하는데 도움이 된다.

1. SRP (Single Responsibility Principle) 단일 책임 원칙

SRP(단일 책임 원칙)는 클래스는 단 하나의 변경 이유만 가져야 한다는 원칙이다. 즉, 하나의 클래스는 하나의 역할만 수행해야 하며, 하나의 책임만 가져야 한다.

C#예제

using System;
using System.IO;

// 직원 클래스 (직원 정보만 관리)
class Employee
{
    public string Name { get; }
    public double Salary { get; }

    public Employee(string name, double salary)
    {
        Name = name;
        Salary = salary;
    }
}

// 파일 저장 클래스 (파일 저장만 담당)
class FileManager
{
    public void Save(Employee emp)
    {
        File.WriteAllText("employee.txt", $"{emp.Name}, {emp.Salary}");
    }
}

class Program
{
    static void Main()
    {
        Employee emp = new Employee("PTK", 4000);
        new FileManager().Save(emp);  // 직원 정보 저장
    }
}

2. OCP (Open-Closed Principle) 개방-폐쇄 원칙

"소프트웨어 엔티티(클래스, 모듈, 함수 등)는 확장에는 열려(Open) 있어야 하고, 변경에는 닫혀(Closed) 있어야 한다."
즉, 기존 코드를 수정하지 않고도 기능을 확장할 수 있어야 한다는 원칙이다.

using System;
using System.Collections.Generic;

// 할인 정책 인터페이스
interface IDiscountStrategy
{
    double ApplyDiscount(double price);
}

// 일반 고객 할인 (10%)
class RegularDiscount : IDiscountStrategy
{
    public double ApplyDiscount(double price) => price * 0.9;
}

// VIP 고객 할인 (20%)
class VIPDiscount : IDiscountStrategy
{
    public double ApplyDiscount(double price) => price * 0.8;
}

// 할인 계산기 (OCP 준수: 새로운 할인 정책이 추가될 때 기존 코드 수정 필요 없음)
class DiscountCalculator
{
    private readonly IDiscountStrategy _discountStrategy;

    public DiscountCalculator(IDiscountStrategy discountStrategy)
    {
        _discountStrategy = discountStrategy;
    }

    public double CalculateDiscount(double price) => _discountStrategy.ApplyDiscount(price);
}

class Program
{
    static void Main()
    {
        // 일반 고객 할인 적용
        DiscountCalculator regularCalc = new DiscountCalculator(new RegularDiscount());
        Console.WriteLine("Regular 고객 할인: " + regularCalc.CalculateDiscount(100));  // 90

        // VIP 고객 할인 적용
        DiscountCalculator vipCalc = new DiscountCalculator(new VIPDiscount());
        Console.WriteLine("VIP 고객 할인: " + vipCalc.CalculateDiscount(100));  // 80
    }
}

3. LSP (Liskov Substitution Principle) 리스코프 치환 원칙

LSP (리스코프 치환 원칙)은 자식 클래스는 언제나 부모 클래스를 대체할 수 있어야 한다"는 원칙이다.
즉, 상속받은 자식 클래스가 부모 클래스의 기능을 변경하지 않고 확장해야 하며, 부모 클래스 대신 사용해도 문제가 없어야 한다는 개념이다.

using System;

// 공통 인터페이스 정의
interface IShape
{
    int GetArea();
}

// 사각형 클래스 (직사각형)
class Rectangle : IShape
{
    public int Width { get; set; }
    public int Height { get; set; }

    public int GetArea() => Width * Height;
}

// 정사각형 클래스
class Square : IShape
{
    public int Side { get; set; }

    public int GetArea() => Side * Side;
}

class Program
{
    static void PrintArea(IShape shape)
    {
        Console.WriteLine("면적: " + shape.GetArea());
    }

    static void Main()
    {
        Rectangle rect = new Rectangle { Width = 5, Height = 10 };
        PrintArea(rect); // 50 출력

        Square square = new Square { Side = 5 };
        PrintArea(square); // 25 출력
    }
}

4. ISP (Interface Segregation Principle) 인터페이스 분리 원칙

"클라이언트가 자신이 사용하지 않는 메서드에 의존하지 않도록 인터페이스를 분리해야 한다."
즉, 하나의 큰 인터페이스보다는 여러 개의 작은 인터페이스로 나누어, 필요한 기능만 구현하도록 강제해야 한다.

using System;

// ISP 적용: 필요한 기능별로 인터페이스 분리
interface IWorker
{
    void Work();
}

interface IEater
{
    void Eat();
}

// 사람은 일도 하고 밥도 먹음
class HumanWorker : IWorker, IEater
{
    public void Work() => Console.WriteLine("사람이 일을 합니다.");
    public void Eat() => Console.WriteLine("사람이 밥을 먹습니다.");
}

// 로봇은 일만 함 → Eat()을 강제로 구현할 필요 없음
class RobotWorker : IWorker
{
    public void Work() => Console.WriteLine("로봇이 일을 합니다.");
}

class Program
{
    static void Main()
    {
        IWorker humanWorker = new HumanWorker();
        humanWorker.Work();

        IEater humanEater = new HumanWorker();
        humanEater.Eat();

        IWorker robotWorker = new RobotWorker();
        robotWorker.Work();
    }
}

5. DIP (Dependency Inversion Principle) 의존 역전 원칙

"고수준 모듈은 저수준 모듈에 의존해서는 안 되며, 둘 다 추상화에 의존해야 한다."
즉, 구체적인 클래스가 아니라 인터페이스나 추상 클래스에 의존하도록 설계하여 의존성을 역전시켜야 한다.

using System;

// DIP 준수: 인터페이스 도입하여 고수준 모듈이 저수준 모듈에 직접 의존하지 않음
interface ILight
{
    void TurnOn();
    void TurnOff();
}

// 기존 전구
class LightBulb : ILight
{
    public void TurnOn() => Console.WriteLine("전구가 켜졌습니다.");
    public void TurnOff() => Console.WriteLine("전구가 꺼졌습니다.");
}

// 새로운 LED 전구 추가
class LEDLightBulb : ILight
{
    public void TurnOn() => Console.WriteLine("LED 전구가 켜졌습니다.");
    public void TurnOff() => Console.WriteLine("LED 전구가 꺼졌습니다.");
}

// 스위치 클래스가 인터페이스에 의존 (DIP 준수)
class Switch
{
    private ILight _light; // 구체적인 구현이 아니라 추상화(인터페이스)에 의존

    public Switch(ILight light)
    {
        _light = light;
    }

    public void TurnOn() => _light.TurnOn();
    public void TurnOff() => _light.TurnOff();
}

class Program
{
    static void Main()
    {
        ILight bulb = new LightBulb();
        ILight ledBulb = new LEDLightBulb();

        Switch mySwitch = new Switch(bulb);
        mySwitch.TurnOn();  // 전구가 켜졌습니다.
        mySwitch.TurnOff(); // 전구가 꺼졌습니다.

        Switch ledSwitch = new Switch(ledBulb);
        ledSwitch.TurnOn();  // LED 전구가 켜졌습니다.
        ledSwitch.TurnOff(); // LED 전구가 꺼졌습니다.
    }
}

0개의 댓글