SOLID 원칙

GamzaTori·2024년 1월 29일
0

객체지향설계의 5대 원칙

1. SRP(Single Responsibility Principle)

  • 단일 책임 원칙: 한 클래스는 하나의 책임만 가져야 한다는 원칙이다

객체지향의 세계에서의 책임이란 어떤 객체가 어떤 요청에 대해 대답해줄 수 있거나 적절한 행동을 할 의무가 있는 경우 해당 객체가 책임을 가진다고 말한다. 즉, 어떤 기능을 담당한다고 생각하면 된다.

  • 만일 클래스에 책임(기능)이 여러 개 존재한다면 기능이 변경되거나 수정되었을 때 영향을 받는 코드가 많아진다.
  • 예를 들어, A라는 기능을 수정했더니 B라는 기능도 수정해야하고 이것이 다시 A를 수정하게 만든다면 책임이 순환하게 되는 일이 발생한다.
  • 이를 SRP 원칙을 통해 하나의 책임으로부터 다른 책임의 변경으로의 연쇄 작용을 방지할 수 있다.
  • 즉, 단일 책임 원칙의 목적은 프로그램 유지보수를 용이하게 하기 위함이다.
class Restaurant
{
	void Order();    // 주문
    void Cook();     // 요리
    void Serving();  // 서빙
}

위와 같이 식당은 주문, 요리, 서빙의 3가지 책임을 가지고 있다. 만약 주문 기능에 수정사항이 생기면 이는 곧 요리와 서빙에도 영향을 미칠 수 있게 된다.

class Shef()
{
	void Cook();	
}

class Customer
{
	void Order();
}

class Server
{
	void Serving();
}

하지만 각각의 기능을 분리하게 되면 하나의 클래스는 하나의 기능만을 담당하게 되므로 쉐프의 기능이 변경되더라도 고객이나 서버에게 영향을 미치지 않는다.

2. OCP(Open Closed Principle)

  • 개방 폐쇄 원칙: 소프트웨어 요소는 확장에는 열려 있으나 변경에는 닫혀 있어야 한다
  • 객체 지향의 특징인 추상화다형성을 통해 OCP를 지향할 수 있다

    확장에 열려있다 -> 새로운 변경 사항이 생겼을 때 유연하게 코드를 추가함으로써 쉽게 기능을 확장할 수 있음
    변경에 닫혀있다 -> 새로운 변경 사항이 생겼을 때 객체의 직접적인 수정을 제한함

class Creature
{
	protected int _hp;
}

class Knight : Creature
{
	_hp = 100;
    int _mp = 100;
}

class Monster : Creature
{
	_hp = 50;
}
  • 몬스터에 수정사항이 생겨도 기사는 영향을 받지 않는다. -> 변경에 닫혀있음
  • 반대로 새로운 생명체를 만들고 싶다면 Creature를 상속받아 확장시킬 수 있다. -> 확장에 열려있음

3. LSP(Liskov Substitution Principle)

  • 리스코프 치환 원칙: 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야한다
  • 즉, 하위 클래스는 상위 클래스로 교체할 수 있어야 한다는 원칙이다.
  • 다시 말해, 하위 클래스는 상위 클래스로 업캐스팅된 상태에서도 상위 클래스의 기능을 동일하게 수행해야 한다는 뜻이다.
class Creature
{
	virtual void Hit()
    {
    	_hp -= 10;
    }
}

class Knight : Creature
{
	void Hit() override
    {
    	_hp -= 10;				// 부모 클래스 기능 수행
        BleedingRedBlood();		// 자식 클래스 기능 수행
    }
}

class Elf : Creature
{
	void Hit() override
    {
    	_hp -= 10;				// 부모 클래스 기능 수행
        BleedingGreenBlood();	// 자식 클래스 기능 수행
    }
}

class NPC : Creature
{
	void Hit() override;		// NPC는 피격이 되지 않기 때문에 Creature의 기능을 수행할 수 없다
}
  • 기사와 엘프의 경우 부모 클래스의 기능을 그대로 수행할 수 있지만, NPC의 경우 상위 클래스인 몬스터의 피격 기능을 수행할 수 없기 때문에 LSP 원칙에 어긋난다

4. ISP(Interface Segregation Principle)

  • 인터페이스 분리 원칙: 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다
  • SRP는 클래스의 단일 책임을 강조한다면, ISP는 인터페이스의 단일 책임을 강조하는 것이다.
  • SRP는 클래스의 분리를, ISP는 인터페이스의 분리를 통해 설계하는 것이다
  • 즉, ISP 원칙은 인터페이스를 기능과 용도에 맞게 잘게 분리해야한다는 것이다.

C++에서의 인터페이스는 추상 클래스이다.

class Creature abstract
{
	virtual void Walk() abstract;
    virtual void Fly() abstract;
}

class Knight : Creature
{
	virtual void Walk() abstract;
    virtual void Fly() abstract;
}

class Bird : Creature
{
	virtual void Walk() abstract;
    virtual void Fly() abstract;
}
  • 기사는 날 수 없지만 상속받은 인터페이스에 날 수 있는 기능이 있기 때문에 구현해야하는 문제가 발생하고 이는 인터페이스 분리 원칙에 어긋난다
class Creature abstract
{
	virtual void Walk() abstract;
}

class FlyCreature abstract
{
	virtual void Walk() abstract;
}

class Knight : Creature
{
	virtual void Walk() abstract;
}

class Bird : FlyCreature
{
    virtual void Fly() abstract;
}
  • 따라서 날 수 있는 생명체와 날 수 없는 생명체를 인터페이스로 분리하면 해당 문제를 해결할 수 있다

5. DIP(Dependency Inversion Principle)

  • 의존 역전 원칙: 추상화에 의존해야지 구체화에 의존하면 안된다
  • 구현 클래스에 의존하지 말고 인터페이스에 의존하라는 뜻이다.
class Creature
{
	virtual void Attack();
    virtual void Defence();
}

class Knight : Creature
{
	void Attack() override;
    void Defence() override;
}

class Monster : Creature
{
	void Attack() override;
    void Defence() override;
}
  • 구현되어있는 기사에 의존해 코드를 수정하게 되면 추후에 몬스터 클래스 또한 수정해주어야 한다.
  • 따라서, 구현 클래스가 아닌 인터페이스에 의존한다면 다양한 하위 클래스를 손쉽게 관리할 수 있다.

ref: 객체 지향 설계의 5가지 원칙 - S.O.L.I.D

profile
게임 개발 공부중입니다.

0개의 댓글