2022. 2. 17. 11:34ㆍPHP
객체지향 5원칙
올바른 객체지향 설계를 위해 수립한 원칙이 있으며, 이 다섯 가지의 원칙을 통틀어 객체지향 5원칙(SOLID)이라 명명한다. 필수로 적용하지 않지만, 적어도 이 규칙을 준수하면 준수할 수록 올바르게 설계된 객체지향이라 할 수 있습니다.
이 다섯 가지 원칙은 아래와 같습니다.
1. 단일 책임 원칙 (Single Responsibility Principle)
2. 개방 폐쇄 원칙 (Open-Closed Principle)
3. 리스코프 치환 원칙 (Liskov Substitution Principle)
4. 인터페이스 분리 원칙 (Interface Segregation Principle)
5. 의존성 역전 원칙 (Dependency Inversion Principle)
각 원칙의 영어 앞글자를 따서 SOLID원칙이라고 합니다.
의존성 역전 원칙 (Dependency Inversion Principle)
의존성 역전 원칙이란 객체는 저수준 모듈보다 고수준 모듈에 의존해야 한다는 원칙이다.
즉, 높은 레벨의 모듈은 낮은 레벨의 모듈을 의존하면 안되며 서로 추상에 의존해야 한다는 것이다.
추상은 구체적인것에 의존하면 안되고, 구체적인 것은 추상에 의존해야 한다는 것이다.
객체는 객체보다 인터페이스에 의존해야한다는 말로도 설명할 수 있습니다.
간단한 코드로 예시를 구현해보겠습니다.(PHP)
상황)
캐릭터를 생성하는 클래스, 무기와 스킬을 관리하는 클래스가 있다고 가정해보겠습니다.
의존성 역전 원칙을 지키지 않은 코드
<?
/**
* 한손검 객체
*
* @author RWB
* @since 2021.08.17 Tue 01:36:44
*/
class OneHandSword
{
private $NAME;
private $DAMAGE;
public function OneHandSword($name, $damage)
{
$this -> NAME = $name;
$this -> DAMAGE = $damage;
}
public function attack()
{
return $this -> DAMAGE + rand(0,10);
}
public function toString()
{
return $this -> NAME;
}
public function toDamage()
{
return $this -> DAMAGE;
}
}
/**
* 캐릭터 객체
*
* @author RWB
* @since 2021.08.17 Tue 00:46:15
*/
class Character
{
private $NAME;
private $HEALTH;
private $WEAPON;
public function Character($name, $health,$weaponName,$damage)
{
$this -> NAME = $name;
$this -> HEALTH = $health;
$this -> WEAPON = new OneHandSword($weaponName,$damage);
}
public function attack()
{
return $this -> WEAPON -> attack();
}
public function damaged($amount)
{
$this -> HEALTH -= $amount;
}
public function chageWeapon($weaponName,$damage){
$this -> WEAPON = new OneHandSword($weaponName,$damage);
}
public function getInfo(){
print("이름: ".$this->NAME."<br>");
print("체력: ".$this->HEALTH."<br>");
print("무기: ".$this->WEAPON->toString()."<br>");
print("공격력: ".$this->WEAPON->toDamage());
}
}
$class = new Character("케릭터이름",5000,'두손무기',300);
$class -> getInfo();
//출력
이름: 케릭터이름
체력: 5000
무기: 두손무기
공격력: 300
?>
위의 코드를 보게되면 게임 캐릭터를 구현한 Character 객체는 한손검만 사용하도록 의존하고 있습니다.
또한 OneHandSword 객체는 공격을 담당하는 attack() 메소드 역시 OneHandSword 객체에 의존성을 가지고 있고
이전에 다뤘던 개방-폐쇄 원칙을 위배하고 있습니다. 그래서 이번엔 다른 무기를 추가하면서 의존성 역전 원척인 추상적인 고수준 모듈을 의존하도록 리팩토링해보겠습니다.
<?php
interface AttackAble
{
public function attack();
public function toString();
}
class OneHandSword implements AttackAble
{
private $NAME;
private $DAMAGE;
public function __construct($name, $damage)
{
$this->NAME = $name;
$this->DAMAGE = $damage;
}
public function attack()
{
// TODO: Implement attack() method.
return $this->DAMAGE + rand(0, 10);
}
public function toString()
{
// TODO: Implement toString() method.
return $this->NAME;
}
}
class TwoHandSword implements AttackAble
{
private $NAME;
private $DAMAGE;
public function __construct($name, $damage)
{
$this->NAME = $name;
$this->DAMAGE = $damage;
}
public function attack()
{
// TODO: Implement attack() method.
return $this->DAMAGE + rand(0, 20);
}
public function toString()
{
// TODO: Implement toString() method.
return $this->NAME;
}
}
class Character
{
private $NAME;
private $HEALTH;
private $WEAPON;
public function __construct($name, $health, AttackAble $weapon)
{
$this->NAME = $name;
$this->HEALTH = $health;
$this->WEAPON = $weapon;
}
public function attack()
{
return $this->WEAPON->attack();
}
public function changeWeapon(AttackAble $weapon)
{
$this->WEAPON = $weapon;
}
public function getInfo()
{
print ("이름 : " . $this->NAME . "<br>");
print ("체력 : " . $this->HEALTH . "<br>");
print ("무기 : " . $this->WEAPON->toString() . "<br>");
}
}
$className = new Character("케릭터", 5000, new OneHandSword("손도끼", 30));
$className->getInfo();
//출력
이름 : 케릭터
체력 : 5000
무기 : 손도끼
$className->changeWeapon(new TwoHandSword("하이엔드", 500));
$className->getInfo();
//출력
이름 : 케릭터
체력 : 5000
무기 : 하이엔드
?>
요약
고수준 모듈인 Attackable 인터페이스를 생성한뒤 공격 데미지를 반환하는 추상 함수 attack() 메소드와 무기함수를 반환하는 추상함수 toString() 메소드를 선언했습니다. 앞으로 모든 무기객체는 이 인터페이스를 상속받게 될 것입니다.
Character 객체를 보게되면 기존의 OneHandSword를 파라미터로 받았지만 리팩토링 된 코드를 보면 좀 더 고수준 모듈인 Attackable을 파라미터로 받는걸 확인할 수 있습니다. TwoHandSword 객체를 하가 하므로써 다른 무기객체들은 Attackable 인터페이스를 상속받아, Character객체의 수정이 아닌 무기 객체를 추가하므로써 개방-폐쇄 원칙 또한 준수한 것을 알 수 있습니다.
정리
의존성 역전 원칙은 코드의 확장성 및 재사용성을 추구하기 위한 원칙입니다. 경직된 객체보다 구현되지 않아 우연한 인터페이스가 더욱 확장 가능성이 높을 것입니다. 의존성 역전 원칙은 다른 원칙인 단일 책인 원칙, 인터페이스 분리 원칙, 개방-폐쇄의 원칙을 준수하게 되면 자동적으로 적용이 된다고 보입니다.
참고자료
자바를 예시로 든 자료 https://blog.itcode.dev/posts/2021/08/17/dependency-inversion-principle
'PHP' 카테고리의 다른 글
객체지향 5원칙(SOLID) - 리스코프 치환 원칙 (0) | 2022.02.17 |
---|---|
Interface와 abstract의 차이 (0) | 2022.02.17 |
객체지향 5원칙(SOLID) - 인터페이스 분리 원칙 (0) | 2022.02.15 |
객체지향 5원칙(SOLID) - 개방 폐쇄 원칙 (0) | 2022.02.15 |
객체지향 5원칙(SOLID) - 단일 책임 원칙 (0) | 2022.02.15 |