포트란과 런타임 다형성 (runtime polymorphism)

포트란에서의 객체지향프로그래밍 다형성과 관련하여 함수 오버로딩연산자 오버로딩에 대해 글을 올린 적이 있습니다. 함수 오버로딩은 다른 함수를 같은 이름으로 사용하는 것으로, 이를 통해 하나의 함수 이름으로 다른 인수(argument)를 사용하는 것과 같은 효과를 나타낼 수 있습니다. 연산자 오버로딩은 함수를 이용해 기존 연산자의 기능을 재정의하거나 새로운 연산자를 만드는 기능이었죠. 런타임 다형성은 하나의 함수에 서로 다른 인수를 사용하는 기능으로, 상속 및 오버라이딩과 관련이 있습니다.

함수 오버로딩에서도 하나의 함수 이름으로 다른 인수를 사용하는 것과 같은 효과를 낼 수 있다고 하였는데, 실제로는 인수에 따라 다른 함수들을 정의하고 같은 이름으로 사용한 것인 반면에, 런타임 다형성을 이용하면 하나의 함수만 정의하고 서로 다른 인수를 사용할 수 있습니다. 이 때 인수들은 아무 인수나 되는 것은 아니고 서로 상속 관계에 있어야 합니다. 코드를 살펴보겠습니다.

module m_hero
    type:: hero_t
    contains
        procedure::speak => speak_hero
    end type 

    type,extends(hero_t):: ironman_t
    contains
        procedure:: speak => speak_ironman
    end type

    type,extends(hero_t):: spiderman_t
    contains
        procedure:: speak => speak_spiderman
    end type

contains

    subroutine speak_hero(hero)
    class(hero_t):: hero
    print*,'I am a hero.'
    end subroutine

    subroutine speak_ironman(hero)
    class(ironman_t):: hero
    print*,'I am Iron Man.'
    end subroutine

    subroutine speak_spiderman(hero)
    class(spiderman_t):: hero
    print*,"I'm Peter."
    end subroutine

    subroutine who_are_you(hero)
    class(hero_t):: hero
    call hero%speak()
    end subroutine

end module

위 코드에서 ironman_tspiderman_thero_t를 상속합니다. 모두 각자의 speak 메소드를 가지고 있는데, 서로 다른 type이지만 다음과 같이 who_are_you 서브루틴의 인수로 사용할 수 있습니다.

program test_runtime_polymorphism1
    use m_hero
    type(hero_t):: hero
    type(ironman_t):: ironman
    type(spiderman_t):: spiderman

    call who_are_you(hero)
    call who_are_you(ironman)
    call who_are_you(spiderman)
end program

실행 결과는 예상대로 다음과 같습니다.

I am a hero.
I am Iron Man.
I’m Peter.

이번에는 ihero라는 변수 값에 따라 hero들 중 한 명이 달려가 사람들을 구해야 한다고 생각해봅시다(모두 run, save_people 메소드를 가지고 있다고 합시다). 아래와 같이 구현할 수 있습니다.

program test_runtime_polymorphism2_not_good
    use m_hero
    type(ironman_t):: ironman
    type(spiderman_t):: spiderman
    integer:: ihero = 1

    select case (ihero)
    case (1)
        call who_are_you(ironman)
        !call ironman%run()
        !call ironman%save_people()
    case (2)
        call who_are_you(spiderman)
        !call spiderman%run()
        !call spiderman%save_people()
    end select
end program

그러나 위 코드는 중복이 많습니다. Hulk, Black Widow, Thor 등 새로운 avenger가 올 때마다 코드가 여러 줄씩 늘어나게 됩니다. 아름답지 않습니다! 포인터를 이용하면 hero_t 클래스 변수가 자식 type을 가리키도록 할 수 있습니다.

program test_runtime_polymorphism2_better
    use m_hero
    class(hero_t),pointer:: hero
    type(ironman_t),target:: ironman
    type(spiderman_t),target:: spiderman
    integer:: ihero = 1

    select case (ihero)
    case (1)
        hero => ironman
    case (2)
        hero => spiderman
    end select

    call who_are_you(hero)
    !call hero%run()
    !call hero%save_people()
end program

아름답습니다! 유지 보수하기 훨씬 좋아졌습니다.

답글 남기기

아래 항목을 채우거나 오른쪽 아이콘 중 하나를 클릭하여 로그 인 하세요:

WordPress.com 로고

WordPress.com의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

Facebook 사진

Facebook의 계정을 사용하여 댓글을 남깁니다. 로그아웃 /  변경 )

%s에 연결하는 중