Item 을 인터페이스로 만들었을 때 문제점
// 아이템을 사용할 수 있는 인터페이스
public interface IUsable
{
void Use();
}
// 아이템 클래스
public class Item : IUsable
{
public string Name { get; set; }
public void Use()
{
Console.WriteLine("아이템 {0}을 사용했습니다.", Name);
}
}
강의에서 위와 같이 아이템을 인터페이스로 사용하는 예시를 보고 개인 프로젝트에도 적용해봤다.
그런데 점점 거대해져가는 클래스..!(눈갱 주의) ▼
// 아이템 인터페이스
public class IItem
{
int equipped { get; }
string name { get; }
int stat { get; }
string description { get; }
string DisplayItemState();
}
// 방어구
public class Armor : IItem
{
public int equipped { get; }
public string name { get; }
public int stat { get; }
public string description { get; }
public Armor(int _equipped, string _name, int _stat, string _description)
{
equipped = _equipped;
name = _name;
stat = _stat;
description = _description;
}
public string DisplayItemState()
{
if (equipped == 1)
{
return "[E]";
}
else
{
return " ";
}
}
}
// 무기
public class Weapon : IItem
{
public int equipped { get; }
public string name { get; }
public int stat { get; }
public string description { get; }
public Weapon(int _equipped, string _name, int _stat, string _description)
{
equipped = _equipped;
name = _name;
stat = _stat;
description = _description;
}
public string DisplayItemState()
{
if (equipped == 1)
{
return "[E]";
}
else
{
return " ";
}
}
}
내가 하고 싶었던건 아이템마다 '착용시 어떤 스탯이 변경되는지' 만 다르게 할뿐... 아이템의 기본적인 정보들은 다 같았다.
거대해진 클래스.. 해결법은?
코드가 좀 더 간결해지기 위해서 인터페이스 방식이 아닌 상속 관계를 만들기로 했다.
// 아이템 클래스
public class Item
{
public int equipped { get; private set; }
public string name { get; private set; }
public int stat { get; private set; }
public string description { get; private set; }
public Item(int _equipped, string _name, int _stat, string _description)
{
equipped = _equipped;
name = _name;
stat = _stat;
description = _description;
}
public virtual string DisplayItemState()
{
if (equipped == 1)
{
return "[E]";
}
else
{
return " ";
}
}
}
// 아이템 스탯 변경 인터페이스
public interface IChangeStat
{
void ChangeStat(Character character);
}
// 방어구 클래스
public class Armor : Item, IChangeStat
{
public Armor(int _equipped, string _name, int _stat, string _description)
: base(_equipped, _name, _stat, _description)
{
}
public void ChangeStat(Character character)
{
// 방어력 스탯 변경
character.ChangeDefensePower(stat);
}
}
// 무기 클래스
public class Weapon : Item, IChangeStat
{
public Weapon(int _equipped, string _name, int _stat, string _description)
: base(_equipped, _name, _stat, _description)
{
}
public void ChangeStat(Character character)
{
// 공격력 스탯 변경
character.ChangeAttackPower(stat);
}
}
`Item` 클래스를 부모로 두고, 자식 클래스들은 ' 착용시 어떤 스탯이 변경되는지 ' 만 구현하도록 했다.
다른 시각에서 인터페이스 사용해보기
아직까지 인터페이스 개념이 제대로 익혀지지 않은 것 같다.
그래도 배운건 써보고 싶어서 고민을 해봤는데
`Item` 클래스에서는 `ChangeStat()` 메소드가 필요없고, 자식 클래스들은 필요하다.
그럼 자식 클래스에게 `ChangeStat()` 메소드를 만들라는 강제성을 부여하도록 인터페이스를 사용하면 되지 않는가!
적용한 결과▼
// 아이템 스탯 변경 인터페이스
public interface IChangeStat
{
void ChangeStat(Character character);
}
// 방어구 클래스
public class Armor : Item, IChangeStat
{
public Armor(int _equipped, string _name, int _stat, string _description)
: base(_equipped, _name, _stat, _description)
{
}
public void ChangeStat(Character character)
{
// 방어력 스탯 변경
character.ChangeDefensePower(stat);
}
}
// 무기 클래스
public class Weapon : Item, IChangeStat
{
public Weapon(int _equipped, string _name, int _stat, string _description)
: base(_equipped, _name, _stat, _description)
{
}
public void ChangeStat(Character character)
{
// 공격력 스탯 변경
character.ChangeAttackPower(stat);
}
}
여기서 더 추가 구현해볼 것이 생각났다. 강의 자료를 보고 생각해냈다.
public interface IItemPickable
{
void PickUp();
}
// 인터페이스 2
public interface IDroppable
{
void Drop();
}
// 아이템 클래스
public class Item : IItemPickable, IDroppable
{
public string Name { get; set; }
public void PickUp()
{
Console.WriteLine("아이템 {0}을 주웠습니다.", Name);
}
public void Drop()
{
Console.WriteLine("아이템 {0}을 버렸습니다.", Name);
}
}
강의 자료에서 위와 같이 다중 상속으로 인터페이스를 활용한 예제가 있었다.
지금은 아이템 스탯 변경 인터페이스를 뒀는데 예제를 보니 깨달았다. 아이템 장착과 해제을 따로 두어야 한다는것을...
아이템 스탯 증가 → 아이템 장착과 해제로 수정하기
그래서 다시 코드를 수정해봤다.
수정된 코드▼
// 아이템 스탯 증가 인터페이스
public interface IEquipItem
{
void EquipItem(Character character);
}
// 아이템 스탯 감소 인터페이스
public interface IUnequipItem
{
void UnequipItem(Character character);
}
// 방어구 클래스
public class Armor : Item, IEquipItem, IUnequipItem
{
public Armor(int _equipped, string _name, int _stat, string _description)
: base(_equipped, _name, _stat, _description)
{
}
// 방어력 스탯 증가
public void EquipItem(Character character)
{
character.IncreaseDefense(stat);
}
// 방어력 스탯 감소
public void UnequipItem(Character character)
{
character.DecreaseDefense(stat);
}
}
// 무기 클래스
public class Weapon : Item, IEquipItem, IUnequipItem
{
public Weapon(int _equipped, string _name, int _stat, string _description)
: base(_equipped, _name, _stat, _description)
{
}
// 공격력 스탯 증가
public void EquipItem(Character character)
{
character.IncreaseAttack(stat);
}
// 공격력 스탯 감소
public void UnequipItem(Character character)
{
character.DecreaseAttack(stat);
}
}
다중 상속 인터페이스를 두면 아이템을 장착할것인지 해제할것인지가 쉽게 알 수 있어 가독성이 좋아지는 것 같다.
가독성 예시▼
public void InteractWithItem(IItemPickable item)
{
item.PickUp();
}
public void DropItem(IDroppable item)
{
item.Drop();
}
`IItemPickable item` `IDroppable item`을 봤을 때 뭘할 것인지 한 눈에 알 수 있다!..
댓글