프로그래밍 공부
작성일
2024. 2. 15. 14:20
작성자
WDmil
728x90

9.2 코드 재사용을 위한 상속

지금껏 상속을 이용하기 위한 기본적인 문법을 익혔다. 이제는 상속이 중요하게 활용되는 케이스를 알아보자.

 

상속은 이미 존재하는 코드를 다시 활용하기 위한 핵심 창구이다. 실제 애플리케이션 코드 재사용을 위해 코드가 어떻게 상속되고 이용되는지 살펴보자.


9.2.1 날씨 예보를 위한 WeatherPrediction클래스

간단한 날씨 예보 프로그램을 만들어야 한다고 하자. 이 프로그램은 섭씨로도 작동하고 화씨로도 작동해야 한다. 그리고 날씨 예보는 별도의 전문적인 분야이기 떄문에, 직접 만들지 못하고 전문 업체가 만든 클래스 라이브러리를 이용한다. 이 라이브러리는 현재 온도와 목성과 화성 사이의 거리를 기반으로 날씨를 예측한다.

( 물론 이러한 예측 방법은 가상적인 설정이다.)

 

이 라이브러리는 지적재산권과 예측 알고리즘을 보호하기 위해 컴파일된 바이너리로 배포된다. 하지만 클래스 정의가 있는 헤더 파일은 오픈되어있다. 다음은 WeatherPrediction클래스의 정의부 이다.

#pragma once
#include<string>

class WeatherPrediction
{
public:
	// 버추얼 소멸자
	virtual ~WeatherPrediction();
	// 현재 온도를 화씨 단위로 세팅한다.
	virtual void setCurrentTempFahreheit(int inTemp);
	// 목성과 화성 사이의 거리를 세팅한다.
	virtual void setPositionOfJupiter(int inDistanceFromMars);
	// 내일의 온도 예측 결과를 리턴한다.
	virtual int getTommorowTempFahrenheit()const;
	// 내일 비가 올 확률을 얻는다. 리턴값 1은 반드시 비가 온다는 의미고,
	// 리턴값 0은 비가 오지 않는다는 의미다.
	virtual double getChanceOfRain() const;
	// 아래 포멧으로 날씨 에측 결과를 출력한다.
	// Result: x.xx Chance. Temp. xx;
	virtual void showResult() const;
	// 온도 예측 결과를 문자열로 리턴한다.
	virtual std::string getTemperature() const;
	
private:
	int mCurrentTempFahreheit;
	int mDistanceFromMars;
};

이 클래스는 모든 메서드를 virtual로 선언하고 있기 때문에 파생 클래스에서 마음대로 오버라이딩 할 수 있다.

 

이 클래스는 작성할 프로그램에서 필요로 하는 기능 대부분을 제공해준다. 하지만 일부 부족한 부분도 있다. 프로그램에서는 섭씨와 화씨 모두 다루어야 하는데 이 클래스는 화씨 단위만 입출력으로 제공하고 있다.

shwoResult() 메서드도 섭씨/화씨 선택권이 없다.


9.2.2 파생 클래스를 통해 기능 추가하기

요구사항대로 프로그램을 만들기 위해서는 WeatherPrediction클래스 그대로는 부족하고 몇 가지 기능이 더 추가되어야 한다. 이러한 상황은 상속을 이용해서 코드를 재활용하기에 적합하다. 먼저 WeatherPrediction클래스를 상속받는 새로운 클래스 MyWeatherPrediction을 다음과 같이 정의한다.

#pragma once
#include "WeatherPrediction.h"

class MyWeatherPrediction : public WeatherPrediction
{
};

이 새로운 클래스는 문제업싱 컴파일된다. 그리고 WeatherPrediction에서 제공하는 기능을 똑같이 이용할 수 있다. 단, 새로운 기능은 아직 만들지 않았다. 가장 먼저 추가할 기능은 섭씨(Cesius) 단위를 처리할 수 있게 하는 것이다. 그런데 약간 막막한 부분이 있다.

 

WeatherPrediction클래스 내부적으로 온도를 어떻게 처리하고 있는지 알 수 없다. 만약 모든 내부 계싼에서 화씨(Faherheit)단위를 이용하고 있다면 섭씨 단위를 어떻게 추가할 수 있을까?

 

한 가지 방법은 파생 클래스가 사용자와 WeatherPrediction클래스 사이의 작업을 중계하며 온도 단위를 변환하는 것이다.

 

마치 외국어를 통역하듯이 WeatherPrediction클래스의 화씨 사용과 클래스 사용자의 섭씨 사용을 변화하여 서로 알아들을 수 있게 하는 것이다.

 

첫 번째 단계는 사용자가 현재 온도를 입력하고 내일의 예측 온도를 넘겨받을 떄 화씨가 아닌 섭씨 단위를 이용할 수 있도록 새로운 메서드를 추가하는 것이다. 또한 단위 변환을 위한 편의 메서드를 선언한다.

 

이 메서드는 클래스 내부에서만 사용되기 떄문에 private로 접근을 제한하고, 개별 객체에 종속된 부분 없이 항상 동일하게 동작하기 때문에 static으로 선언한다.

#pragma once
#include "WeatherPrediction.h"

class MyWeatherPrediction : public WeatherPrediction
{
public:
	virtual void setCurrentTempCelsius(int inTemp);
	virtual int getTommorwTempCelsius() const;
private:
	static int convertCelsiusToFahreheit(int inCelsius);
	static int convertFahreheitToCelsius(int inFahreheit);
};

새로운 메서드의 이름은 베이스 클래스의 명명 규칙을 따르도록 한다. 사용자로서는 사용하는 멤버나 메서드가 베이스 클래스의 것인지 파생 클래스의 것인지 구분되지 않기 때문에 일관성 없이 이름을 지으면 혼란스러워진다.

 

섭씨/화씨 변환 메서드의 구현은 작성하지 않았다. 현재 온도를 섭씨 단위로 세팅하려면, 먼저 베이스 클래스 WeatherPrediction이 알아들을 수 있는 화씨 단위로 변환해야 한다.

#include "MyWeatherPrediction.h"

void MyWeatherPrediction::setCurrentTempCelsius(int inTemp)
{
	int fahreheitTemp = convertCelsiusToFahreheit(inTemp);
	setCurrentTempFahreheit(fahreheitTemp);
}

위 구현부에서 확인할 수 있는 것처럼, 화씨로 변환한 다음에는 베이스 클래스의 기능을 이용한다. 이와 비슷한 방법으로 getTommoreTempCelsius()도 베이스 클래스에 이미 존재하는 기능을 이용해서 화씨 결괏값을 얻은 후 리턴하기 전에 섭씨로 변환한다.

int MyWeatherPrediction::getTommorwTempCelsius() const
{
	int fahreheitTemp = getTommorowTempFahrenheit();
	return convertFahreheitToCelsius(fahreheitTemp);
}

이 두 메서드는 이미 존재하는 베이스 클래스의 기능을 재활용한 것으로 기존 기능을 감싸서 새로운 섭씨 단위 인터페이스를 만들었다.

 

베이스 클래스가 가진 기존 기능과 관계없는 아주 새로운 기능을 추가할 수도 있다. 에를 들어 날씨 예측 방법으로 온도와 행성 간 거리를 입력하게 하는 대신 인터넷에서 정보를 검색하여 보여줄 수 있다.


9.2.3 파생 클래스를 통해 변경된 기능 제공

파생 클래스를 활용하는 다른 방법으로 기능 변경이 있다. 베이스 클래스 WeatherPredicition에 정의된 showResult() 메서드를 바꿀 필요가 있다. 파생 클래스 MyWeatherPrediction에서 이 메서드를 오버라이딩하여 구현부를 새롭게 정의한다.

 

다음은 showresult()를 오버라이딩하기 위한 MySeatherPrediction 클래스의 정의다.

#pragma once
#include "WeatherPrediction.h"

class MyWeatherPrediction : public WeatherPrediction
{
public:
	virtual void setCurrentTempCelsius(int inTemp);
	virtual int getTommorwTempCelsius() const;
	virtual void showResult() const override;
private:
	static int convertCelsiusToFahreheit(int inCelsius);
	static int convertFahreheitToCelsius(int inFahreheit);
};

오버라이딩된 shjowResult()는 다음과 같이 구현부가 정의될 수 있다.

void MyWeatherPrediction::showResult() const
{
	cout << "Tomorrow's temperature will be " <<
		getTommorwTempCelsius() << " degrees Celsius (" <<
		getTommorowTempFahrenheit() << " degrees Fahrenheit)" << endl;
	cout << "chance of rain is " << (getChanceOfRain() * 100) << " percent"
		<< endl;
	if (getChanceOfRain() > 0.5) {
		cout << "Bring an umbrella!" << endl;
	}
}

사용자가 MyWeatherPrediction객체를 이용하는 한 기존의 ShowResult()메서드는 더는 호출될 수 없다.

 

이렇게 MyWeatherPrediciton클래스는 사용하려는 목적에 맞도록 커스터마이즈된 새로운 클래스가 되었다.

 

기존 클래스 기능을 재활용함으로써 많은 코드를 작성하지 않고도 효율적으로 만들 수 있었다.

728x90