[Java] 10. 다형성(Polymorphism)


로버트 마틴이 좋은 객체 지향 설계를 위한 SOLID원칙 5가지 원칙 중에 OCP, DIP는 다형성 개념을 기본으로 활용된다. 즉, 다형성은 객체지향에서 중요한 개념이다.


1. 다형성(Ploymorphism)이란?

-조상 타입 참조 변수자손 타입 객체를 다루는 것
- 여러 가지 형태를 가질 수 있는 것


다형성(Polymorphism)이란 {부모-자식} 상속 관계에 있는 클래스에서
상위 클래스가 동일한 메시지로 하위 클래스들을 서로 다르게 동작시키는 객체 지향 원리이다.
다형성을 활용하면 부모 클래스가 자식 클래스의 동작 방식을 알수 없어도 오버라이딩을 통해 자식 클래스를 접근할 수 있다.

상속에서는 자식 클래스에서 부모 클래스의 멤버 메서드를 접근 하고 실행시킬 수 있는데
반대로 다형성에서는 부모 클래스에서 자식 클래스의 멤버 메서드를 호출할 수 있다는 것이다.

그게 가능한 이유는 실행 시(런타임)에 상태가 결정되는 동적 바인딩 개념이 있기 때문이다.

프로그램 컴파일 시점에는 부모 클래스에서 자식 클래스의 멤버 접근할 수 없으나,
런타임 시에는 동적 바인딩이 일어나 부모 클래스가 자식 클래스의 멤버 메서드를 접근하여 실행할 수 있다.


로버트 마틴이 좋은 객체 지향 설계를 위한 SOLID원칙 5가지 원칙 중에 OCP, DIP는 다형성 개념을 기본으로 활용된다. 즉, 다형성은 객체지향에서 중요한 개념이다.



정적 바인딩(Static binding)

  • 실행 이전에 값이 확정되면 정적 바인딩
  • 컴파일 타임에 호출될 함수가 결정되는 것으로, 함수는 기본적으로 정적 바인딩

동적 바인딩(Dynamic binding)

  • 실행 이후에 값이 확정되면 동적 바인딩
  • 런타임에 호출될 함수가 결정되는 것으로, virtual 키워드를 통해 동적 바인딩하는 함수를 가상 함수라고 한다.



2. 다형성 예제 코드

다형성은 하나의 타입에 여러 객체를 가질 수가 있다. 다형성을 구현하기 위해서는 여러 자식 객체들 중 공통 기능 멤버를 추상화하고 상속 또는 구현 해야되는 개념이다.

해당 예제에서는 프로그래밍 구현하는 다형성 예제로 접근해서 보면 좋을 듯 하다.

다형성으로 {부모 - 자식} 간의 관계로 여러 형태를 가지며, 오버라이딩을 통해서 부모 클래스에서 자식 클래스 멤버에 접근해서 실행하는 것을 볼 수 있다.


//다형성으로 {부모 - 자식} 간의 관계로 여러 형태 가능(전사, 마법사, 궁수)
Unit warrior = new Warrior(31, 75, 5, 120, 30);
Unit wizard = new Wizard(31, 30, 20, 80, 110);
Unit archer = new Archer(31, 55, 10, 90, 90);

//다형성으로 오버라이딩을 통해서 부모 클래스에서 자식 클래스 멤버에 접근해서 실행
for (Unit u : units) {
    System.out.println("----------------------------------------");
    u.basicAttack();
    u.lowSkill();
    u.highSkill();
    u.showStats();
}



//기본 유닛(전직 전)
class Unit {

    //캐릭터 스탯
    private String unitType = "전직 전";
    private int level;
    private int damage;
    private int skillDamage;
    private int hp = 300;
    private int mp = 150;

    //레벨 업에 따른 보상(기본 보상 스탯)
    private int basicDamage;
    private int basicSkillDamage;
    private int baseHp;
    private int baseMp;

    Unit () {
    }

    Unit (int level, int basicDamage, int basicSkillDamage, int baseHp, int baseMp) {
        this.levelUp(level, basicDamage, basicSkillDamage, baseHp, baseMp);
    }

    public void levelUp(int level, int basicDamage, int basicSkillDamage, int baseHp, int baseMp) {
        this.level = level;
        this.damage = basicDamage * level;
        this.skillDamage = basicSkillDamage * level;
        this.hp = this.hp + baseHp * level;
        this.mp = this.mp + baseMp * level;
    }

    public void setUnitType(String unitType) {
        this.unitType = unitType;
    }

    public void basicAttack() {
        System.out.println("기본 공격");
    }

    public void lowSkill() {
        System.out.println("저급 스킬 시전");
    }

    public void highSkill() {
        System.out.println("고급 스킬 시전");
    }

    public void showStats() {
        System.out.println(this.toString());
    }

    @Override
    public String toString() {
        return "Unit{" +
                "전직캐릭터=" + unitType +
                ", level=" + level +
                ", damage=" + damage +
                ", skillDamage=" + skillDamage +
                ", hp=" + hp +
                ", mp=" + mp +
                '}';
    }
}

//전사 캐릭터
class Warrior extends Unit {

    Warrior() {
    }

    Warrior (int level, int basicDamage, int basicSkillDamage, int baseHp, int baseMp) {
        super.setUnitType("광전사");
        super.levelUp(level, basicDamage, basicSkillDamage, baseHp, baseMp);
    }

    @Override
    public void basicAttack() {
        System.out.println("전사 근접 공격");
    }

    @Override
    public void lowSkill() {
        System.out.println("전사 저급++ 스킬 시전");
    }

    @Override
    public void highSkill() {
        System.out.println("전사 고급 스킬 시전");
    }

}

//마법사 캐릭터
class Wizard extends Unit {

    Wizard() {
    }

    Wizard(int level, int basicDamage, int basicSkillDamage, int baseHp, int baseMp) {
        super.setUnitType("소서리스");
        super.levelUp(level, basicDamage, basicSkillDamage, baseHp, baseMp);
    }
    @Override
    public void basicAttack() {
        System.out.println("마법사 마법 공격");
    }

    @Override
    public void lowSkill() {
        System.out.println("마법사 저급++ 스킬 시전");
    }

    public void highSkill() {
        System.out.println("마법사 고급 스킬 시전");
    }
}

//궁수 캐릭터
class Archer extends Unit {

    Archer() {
    }
    Archer(int level, int basicDamage, int basicSkillDamage, int baseHp, int baseMp) {
        super.setUnitType("아쳐");
        super.levelUp(level, basicDamage, basicSkillDamage, baseHp, baseMp);
    }
    @Override
    public void basicAttack() {
        System.out.println("궁수 활 공격");
    }

    @Override
    public void lowSkill() {
        System.out.println("궁수 저급++ 스킬 시전");
    }

    public void highSkill() {
        System.out.println("궁수 고급 스킬 시전");
    }
}

public class ExGameUnit {

    public static void main(String... args) {
        //캐릭터 유닛의 전직 전과 전직 후에 객체의 행위(메서드)를 제한 할 수 있다.

        //기본 유닛(전직 전)
        //new Unit(int level, int basicDamage, int basicSkillDamage, int baseHp, int baseMp)
        Unit unit = new Unit(1, 20, 15, 50, 20);
        //전사, 마법사, 궁수
        Unit warrior = new Warrior(31, 75, 5, 120, 30);
        Unit wizard = new Wizard(31, 30, 20, 80, 110);
        Unit archer = new Archer(31, 55, 10, 90, 90);

        //다형성으로 {부모 - 자식} 간의 관계로 여러 형태 가능(전사, 마법사, 궁수)
        List<Unit> units = new ArrayList<>();
        units.add(unit);
        units.add(warrior);
        units.add(wizard);
        units.add(archer);

        //다형성으로 오버라이딩을 통해서 부모 클래스에서 자식 클래스 멤버에 접근해서 실행
        for (Unit u : units) {
            System.out.println("----------------------------------------");
            u.basicAttack();
            u.lowSkill();
            u.highSkill();
            u.showStats();
        }
    }
}

console

Unit{전직캐릭터=전직 전, level=1, damage=20, skillDamage=15, hp=350, mp=170}
기본 공격
저급 스킬 시전
고급 스킬 시전
----------------------------------------
Unit{전직캐릭터=광전사, level=31, damage=2325, skillDamage=155, hp=4020, mp=1080}
전사 근접 공격
전사 저급++ 스킬 시전
전사 고급 스킬 시전
----------------------------------------
Unit{전직캐릭터=소서리스, level=31, damage=930, skillDamage=620, hp=2780, mp=3560}
마법사 마법 공격
마법사 저급++ 스킬 시전
마법사 고급 스킬 시전
----------------------------------------
Unit{전직캐릭터=아쳐, level=31, damage=1705, skillDamage=310, hp=3090, mp=2940}
궁수 활 공격
궁수 저급++ 스킬 시전
궁수 고급 스킬 시전