본문 바로가기
프로그래밍/C#

C# Virtual vs Abstract

by bantomak 2024. 2. 3.

Virtual vs Abstract

사용자가 선택 가능한 교통수단에 대한 클래스를 작성해 보자. 교통수단으로는 Car, Train, Plane 등이 있을 것이다. 이를 바탕으로 여행시간과 운임을 선택 가능하도록 작성해 보자.

 

예를 들어, 유저의 선택을 기반으로 적절한 교통수단으로 인스턴스화되는 TransportAgency 클래스를 작성해 보자.

 

public enum TransportModeType
{
    Car,
    Plane,
    Train
}
                                                                  
internal class TransportAgency
{
    public TransportMode CreateTransportMode(TransportModeType modeType)
    {
        return modeType switch
        {
            TransportModeType.Car => new Car(60),
            TransportModeType.Train => new Train(4),
            TransportModeType.Plane => new Plane(800),
            _ => throw new ArgumentException("Invalid transport mode type."),
        };
    }
}

 

이제 추상 메서드와 가상 메서드를 가지는 TrannsportMode라는 추상 클래스를 만들어보자

 

internal abstract class TransportMode
{
    public abstract double GetTravelTime(double distance);

    public virtual double CalculateBaseFare(double distance)
    {
        return distance * 0.5; 
    }
}

 

교통수단에 따른 총 여행 시간을 결정하기 위해서는 GetTravelTime 메서드를 사용하면 된다. 이 메서드는 추상 메서드로 디자인되어 있어서 각 교통수단에 알맞게 구현될 거라고 가정된 것이다. 그러므로 현재 시점에서는 기본 여행 시간은 의미가 없다.

 

이제 운임을 계산하는 CalculateBaseFare() 메서드를 살펴보면, 각 교통수단 모드에 따른 기본 운임을 결정한다. 우리는 해당 메서드를 Abstract가 아닌 Virtual로 설정하였다. 왜냐면 일반적으로 적용가능한 기본 운임이 있기 때문이다. 하위 클래스는 선택지를 가지게 된다. 기본 운임을 그대로 적용할 것인가? 아니면 함수를 재정의해서 특정한 요구에 맞춰진 새로운 운임을 적용할 것인지 말이다.

 

자식 클래스

TransportMode를 상속받은 Car, Train, Plane 클래스를 살펴보자.

 

internal class Car(double averageSpeed) : TransportMode
{
    private readonly double _averageSpeed = averageSpeed;

    public override double GetTravelTime(double distance)
    {
        return distance / _averageSpeed;
    }
   
    public override double CalculateBaseFare(double distance)
    {
        return base.CalculateBaseFare(distance) + 2.5; 
    }
}

 

Car 클래스는 가상 메서드 CalculateBaseFare()를 재정의해서 자동차에 대한 요금 계산을 조정한다. 해당 방법은 기본 클래스의 기존 기본요금을 활용한 다음 연료 비용을 추가해서 최종 계산에 통합한다. 또한 이동 시간을 결정하기 위한 자동차별 구현을 제공하기 위해서 추상 메서드 GetTrabelTime()을 재정의 한다.

 

internal class Train(double fixedJourneyTime) : TransportMode
{
    private double _fixedJourneyTime = fixedJourneyTime;

    public override double GetTravelTime(double distance)
    {
        return _fixedJourneyTime; 
    }

    public override double CalculateBaseFare(double distance)
    {
        var baseFare = base.CalculateBaseFare(distance);

        if (distance > 500)
        {
            baseFare *= 0.9;              
        }

        return baseFare;
    }
}

 

Train 클래스의 경우, 가상 메서드 CalculateBaseFare()를 재정의한다. 부모 클래스에서 정의된 기본요금을 사용하지만 거기에 장거리 여행에 따른 10% 할인을 제공한다. 그리고 거리에 상관없이 고정된 여행 시간을 갖는다.

 

internal class Plane(double cruisingSpeed) : TransportMode
{
    private readonly double _cruisingSpeed = cruisingSpeed;

    public override double GetTravelTime(double distance)
    {
        return distance / _cruisingSpeed + 0.5; 
    }

    public override double CalculateBaseFare(double distance)
    {
        if (distance < 500)
        {
            return 100; 
        }
        else if (distance < 1000)
        {
            return 150; 
        }
        else
        {
            return distance * 0.2; 
        }
    }
}

 

Plane 클래스의 경우, 기본 클래스에서 정의된 기본 운임에서 완전히 벗어난 새로운 운임 계산을 적용한다. 물론 그렇다고 함수가 바뀌는 건 아니다. CalculateBaseFare() 메서드가 재정의될 뿐이다. 전체 여행 시간을 정하기 위해서 추상 메서드인 GetTravelTime()을 구현합니다.

 

이제 모든 여정의 주요 내용을 요약해 보자.

TransportMode 클래스를 상속받아서 모든 하위 클래스 Car, Train, Plane을 구현하였다. 추상 메서드인 GetTravelTime()은 무조건 재정의되어야 한다. 모든 교통수단의 이동거리는 상이하기 때문에 추상메서드로 정의하였다.

 

가상 메서드의 경우에는 CalculateBaseFare() 메서드가 운임에 대한 가이드라인을 제공한다. 이를 따를 수도 있고 운송체계에 맞는 운임요금을 새로 작성할 수도 있다. Train과 같이 장거리 여행에 따른 할인과 같이 말이다.

 

추상 메서드(Abstract Method) 가상 메서드(Virtual Method)
선언만 가능 타입 기반 다형성에 주로 사용
자식 클래스에서 반드시 재정의 되어야함 반드시 재정의하지 않아도 됨(기본 형태 그대로 사용 가능)
abstract 키워드는 메서드와 클래스에 사용 가능 virtual 키워드는 메서드에 사용 가능 (클래스 불가)
abstract 메서드는 오직 abstract 클래스 안에서만 선언 가능 추상 클래스와 비 추상 클래스에 상관없이 선언 가능
static, virtual, private 호환 불가 static, abstract, private, override 호환 불가
일관성을 유지하기에 좋음 기본 형태를 가지며 상황에 맞게 커스터마이징 하기 좋음

 

함께 읽으면 좋은 글

 

C# virtual 키워드

virtual 키워드란? virtual 키워드는 메서드, 속성, 인덱서 또는 이벤트 선언을 수정하고 파생 클래스에서 재정의하도록 허용하는 데 사용된다. 예를 들어 이 메서드는 이를 상속하는 모든 클래스에

jettstream.tistory.com

 

참고 사이트

 

Differences Between a Virtual and an Abstract Method in C#

In this article, we are going to learn about the differences between a virtual and an abstract method and when to use which one.

code-maze.com

댓글