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
로 바뀌어야한다.
이렇게 하위 모듈의 구현 내용이 바뀌면 이것에 의존하던 상위 모듈도 수정되어야 한다.
이러면 코드를 유지 보수하기 힘들어지기 때문에 의존 관계 역전 원칙
은 이러한 의존 관계를 만들지 말라는 것이다.