회원가입

1. [의존관계역전원칙] 의존 관계 역전 원칙

NULL 2021-10-21

SOLID 원칙 중, 마지막 D 에 해당하는 의존 관계 역전 원칙을 배워보겠다.

 

영어로는 Dependency Inversion Principle 이다.

 

의존 관계 역전 원칙의 정의는 "상위 모듈은 하위 모듈의 구현 내용에 의존하면 안 된다. 상위 모듈과 하위 모듈 모두 추상화된 내용에 의존해야 한다."

이런 말이다.

 

말이 좀 어렵다...

이게 무슨 말인지 코드를 보면서 하나씩 이해해 보자.

class Sword:
    """검 클래스"""
    def __init__(self, damage):
        self.damage = damage

    def slash(self, other_character):
        """검 사용 메소드"""
        other_character.get_damage(self.damage)

class GameCharacter:
    """게임 캐릭터 클래스"""
    def __init__(self, name, hp, sword: Sword):
        self.name = name
        self.hp = hp
        self.sword = sword

    def attack(self, other_character):
        """다른 유저를 공격하는 메소드"""
        if self.hp > 0:
            self.sword.slash(other_character)
        else:
            print(self.name + "님은 사망해서 공격할 수 없습니다.")

    def change_sword(self, new_sword):
        """검을 바꾸는 메소드"""
        self.sword = new_sword

    def get_damage(self, damage):
        """캐릭터가 공격받았을 때 자신의 체력을 깎는 메소드"""
        if self.hp <= damage:
            self.hp = 0
            print(self.name + "님은 사망했습니다.")
        else:
            self.hp -= damage

    def __str__(self):
        """남은 체력을 문자열로 리턴하는 메소드"""
        return self.name + "님은 hp: {}이(가) 남았습니다.".format(self.hp)

코드에는 GameCharacter 클래스와 Sword 클래스가 있다.

 

GameCharacter


먼저 GameCharacter 클래스를 살펴보자.

인스턴스 변수에는

  • 게임 캐릭터의 이름 : self.name
  • 게임 캐릭터의 체력 : self.hp
  • 게임 캐릭터가 공격할 때 쓰는 검 : self.sword

메서드로는

  • 다른 게임 캐릭터를 공격하는 attack
  • 검을 바꾸는 change_sword
  • 게임 캐릭터가 공격 받았을 때 자신의 체력을 깎는 get_damage

__str__ 던더 메서드에는 이 GameCharacter 클래스 인스턴스의 체력이 출력된다.

 

Sword


게임 캐릭터가 사용할 Sword 클래스를 살펴보자

인스턴스 변수에는

  • 검의 공격력을 나타내는 self.damage

메서드로는

  • 검으로 공격하는 걸 나타내는 slash
    • slash 메서드는 다른 게임 캐릭터를 파라미터로 받아서 이 다른 게임 캐릭터의 체력을 깎는다.

 

그럼 이 두 클래스를 한번 사용해보자.

 

먼저 공격력이 1 짜리인 별로 안 좋은 검과 공격력이 100 인 좋은 검을 만들자.

bad_sword = Sword(1)
good_sword = Sword(100)

 

그리고 별로 안 좋은 검을 가진 체력이 100 인 "홍길동" 이라는 게임 캐릭터와 좋은 검을 가지고 체력이 1000 인 "아무개"라는 게임 캐릭터를 만들자.

game_character_1 = GameCharacter("홍길동", 100, bad_sword)
game_character_2 = GameCharacter("아무개", 1000, good_sword)

 

이제 "홍길동"이 "아무개"를 3번 공격하도록 하겠다.

game_character_1.attack(game_character_2)
game_character_1.attack(game_character_2)
game_character_1.attack(game_character_2)

 

이제 "아무개"도 "홍길동"을 공격하도록 하자

game_character_2.attack(game_character_1)

 

각 캐릭터의 남은 체력을 한번 출력해보자

print(game_character_1)
print(game_character_2)
홍길동님은 사망했습니다.
홍길동님은 hp: 0이(가) 남았습니다.
아무개님은 hp: 997이(가) 남았습니다.

"홍길동" 캐릭터는 죽었고, "아무개" 캐릭터는 체력이 좀 깎였다.

 

의존 관계 역전 원칙


의존 관계 역전 원칙으로 돌아와보자.

 

위의 코드는 사실 의존 관계 역전 원칙을 위반했다.

의존 관계 역전 원칙"상위 모듈은 하위 모듈의 구현 내용에 의존하면 안 된다. 상위 모듈과 하위 모듈 모두 추상화된 내용에 의존해야 한다."

 

두 클래스가 있을 때 어떤 클래스가 다른 클래스를 사용하는 관계에 있으면 이때 사용하는 클래스를 상위 모듈, 사용 당하는 클래스를 하위 모듈이라고 생각하면 된다.

 

지금 GameCharacter 클래스를 보면 attack 메서드에서 Sword 클래스의 인스턴스를 사용하고 있다.

def attack(self, other_character):
    """다른 유저를 공격하는 메소드"""
    if self.hp > 0:
        self.sword.slash(other_character)
    else:
        print(self.name + "님은 사망해서 공격할 수 없습니다.")

그러니까 GameCharacter 클래스가 상위 모듈이고, Sword 클래스가 하위 모듈이다.

 

지금 상위 모듈GameCharacter 클래스가 하위 모듈Sword 클래스 구현 내용에 의존하고 있다.

이 말은 무슨 말일까?

 

다시 게임 캐릭터 클래스의 attack 메서드를 보면 attack 메서드는 Sword 클래스로 만든 인스턴스slash 메서드를 사용한다.

attack 메서드가 문제 없이 실행되려면, slash 메서드가 잘 실행된다는 보장이 있어야 한다.

만약 Sword 클래스인 slash 메서드가 사라지거나 이름이 바뀌면 attack 메서드가 호출될 때 문제가 생길 것이다.

지금 GameCharacter 클래스가 Sword 클래스의 구현 내용 중, slash 메서드에 의존하고 있는 것이다.

 

예를 들어 Sword 클래스의 slash 메서드 이름이 do_slash 로 바뀐다면 attack 에 있는 slash 또한 do_slash 로 바뀌어야한다.

이렇게 하위 모듈의 구현 내용이 바뀌면 이것에 의존하던 상위 모듈도 수정되어야 한다.

이러면 코드를 유지 보수하기 힘들어지기 때문에 의존 관계 역전 원칙은 이러한 의존 관계를 만들지 말라는 것이다.

0 0