인터페이스 활용

Amberjack·2024년 2월 14일
0

C# 문법

목록 보기
44/44

인터페이스 활용

어떤 앱에서 게스트 유저와 회원가입 유저 2가지 형태의 유저 형태가 있다고 하자.

유저 형태에 따라 서로 다른 작업이 필요할 수 있다. 게스트 유저는 서버에 저장되지 않고 앱 자체에 저장시키듯이 말이다.

이 때는 if문을 사용할 수 있을 것이다. 그러나, SNS로 가입하는 유저의 경우가 추가된다고 생각해보자. 그럴 경우 if문을 수정해야 할 필요가 있을 것이다. 그러나 SNS에서도 카카오, 네이버, 페이스북, 기타 등등 여러 SNS 서비스에 따라서도 모두 분기를 나누어서 처리를 해야한다. 결국 if문을 사용하던, switch문을 사용하던 코드가 길어지고 유지 보수, 가독성이 모두 떨어지게 될 것이다.

이를 해결하기 위해 추상화가 필요하다.

추상화

Interface는 기본적인 추상화 방법 중 하나이다.

카페에서 다양한 음료를 주문할 수 있다. 여러 음료, 디저트 등 여러 종류가 있을 수 있지만 이를 얻기 위해서는 주문을 해야 한다.

카페에서 진행하는 행동 과정

카페에 간다. → 주문 → 카페를 나간다.

이 때, 모든 메뉴는 주문이라는 과정을 거쳐야 하기 때문에, 각각의 메뉴에 주문 기능을 추가해두면 메뉴를 선택할 때 주문 기능을 활용할 수 있을 것이다.

추상화란, 공통된 것을 통해 무엇인가 들어올 수 있도록 준비해두는 것이다.
카페에서 어떤 메뉴을 "주문"할지는 손님이 직접 주문할 때까지 모르지만, 미리 주문 기능을 만들어 놓으면 손님이 어떤 메뉴를 고르던지 주문을 할 수 있도록 만들 수 있다.

추상화의 장점 : 확장성.

추상화를 사용할 때, 공통된 것은 크게 행동, 속성 2가지가 있다. 이 때, 행동에 대한 것을 추상화하는 것이 "인터페이스"이다.

인터페이스

추상화를 사용하기 위해서는 실제로 사용할 Object를 생성해줘야 한다. Object에서 추상화를 위해 공통된 부분(기능)을 사용하기 위해서는 그 기능(함수)를 가지고 있어야 하는데, 이 때 Object에 공통된 함수를 만들어 주는 것을 인터페이스를 통해 만들어 줄 수 있다.

ex) Player와 Monster 모두 공격, 피격 기능이 필요할 때 : Interface로 만들어서 사용해보자!

public class Player : MonoBehaviour
{
	private int HP;
    private int atk;
    
    public void Attack(Monster monster)
    {
    	monster.TakeDamage(atk);
    }
    
    public void TakeDamage(int damage)
    {
    	hp -= damage;
    }
}
public class Monster : MonoBehaviour
{
	private int HP;
    private int atk;
    
    public void Attack(Player player)
    {
    	monster.TakeDamage(atk);
    }
    
    public void TakeDamage(int damage)
    {
    	hp -= damage;
    }
}

위와 같이 코딩을 한다고 해보자. 딱 봐도 Player와 Monster에서 겹치는 코드가 문제가 된다는 것을 알 수 있다.

따라서 Character 라는 클래스를 만들어서 Player와 Monster가 Character를 상속받도록 만들어 보자.

public class Character : MonoBehaviour
{
	private int HP;
    private int atk;
    
    public void Attack(Player player)
    {
    	monster.TakeDamage(atk);
    }
    
    public void TakeDamage(int damage)
    {
    	hp -= damage;
    }
}
public class Player : Character
{
	
}

...

public class Monster : Character
{
	
}

그러나, Player와 Monster 말고도 물체가 공격을 받도록 만들고 싶다면 어떻게 해야할까? 물체는 공격을 하지 않기 때문에 TakeDamage만 상속받아야 할 것이다. 하지만 그렇다면 Character를 수정하게 될 것이다. 또한, 물체는 공격 한번 받으면 바로 없어지도록 만들고 싶다면? TakeDamage까지 수정해야 할 것이다.

이 때 데미지를 받는 Object들을 모두 묶어 Damageable 이라는 속성으로 묶을 수 있을 것이다. 이를 인터페이스로 만들어 볼 수 있다.

public class IDamageable
{
	public void TakeDamage(int damage);
}
public class Character : MonoBehaviour, IDamageable
{
	private int HP;
    private int atk;
    
    public void Attack(Player player)
    {
    	monster.TakeDamage(atk);
    }
    
    public void TakeDamage(int damage)
    {
    	hp -= damage;
    }
}
public class Player : Character
{
	
}
public class DestroyProbs : MonoBehaviour, IDamageable
{
	public void TakeDamage(int damage)
    {
    	Destroy(gameObject);
    }
}

의존성?

인터페이스를 사용하면 의존성을 줄일 수 있다. 쉽게 말해, Class A에 연결되어 있는 Class B가 있다고 가정해보자. 코딩을 하다가 A를 수정해야 할 일이 있어서 A를 수정할 때, A를 수정한 것에 의해 B 또한 영향을 받을 때, 이를 B는 A와 의존 관계라고 한다. 즉, A가 변경되었을 때 그것이 B에 영향을 미치는 것을 의미한다.

그러나 이는 객체 지향 5원칙 SOLID에 어긋나는 일이다. A가 변경되었기 때문에 B까지 수정을 해야한다는 것은 바람직하지 않기 때문이다.

따라서 인터페이스를 활용하면 이러한 의존성을 줄일 수가 있다고 한다. 인터페이스와 실제로 해당 함수를 구현하는 구현체는 분리되어 있기 때문이다.

0개의 댓글