IT/학습

상속(Inheritance)

wh011202 2024. 12. 11. 21:21

 

C#에서 상속(Inheritance)은 객체 지향 프로그래밍의 핵심 개념 중 하나로, 기존 클래스의 속성과 메서드를 새로운 클래스에 재사용하는 방법을 제공한것 이다.

 

상속을 사용하면 코드의 재사용성과 확장성을 높일 수 있다.

 

예를들어 자동차를 분류해 보았다.

 

class Sedan
{
    float _maxSpeed;
    string _brand;
    public void SedanAction()
    {
        Console.WriteLine("동승자 태우기");
    }
    public void Drive()
    {
        Console.WriteLine("주행");
    }
}

class Truck
{
    float _maxSpeed;
    string _brand;
    public void TruckAction()
    {
        Console.WriteLine("짐싣기");
    }
    public void Drive()
    {
        Console.WriteLine("주행");
    }
}

class Bulldoser
{
    float _maxSpeed;
    string _brand;
    public void BulldoserAction()
    {
        Console.WriteLine("평탄화작업");
    }
    public void Drive()
    {
        Console.WriteLine("주행");
    }
}

 

이렇게 세가지 클래스를 작해봤다.

 

각각의 독창적인 기능들도 있긴 하지만 maxSpeed나 brand, 그리고 Drive의 기능의 경우 중복이 된걸 확인할수 있다.

 

왜이리 중복이 많은가 알아보니, 트럭이나 불도저, 세단의 경우, 자동차라는 공통점이 떡하니 자리잡고 있어서 그러했다.

 

상속이라는 기능을 이용해서 코드를 간결하고 직관적이게 바꾸어 보았다.

 

클래스들을 만드는 데, 중복된 내용이 많을 경우, 공통된 기능이나 필드를 뽑아서 상속을 고려할 수 있다.

 

타 클래스의 근간이 된다는 의미에서 Base클래스라고 부른다.

혹은 모태가 된다는 뜻에서 부모클래스라 부르기도 한단다.

 

저 부모에서 파생된 클래스를 보고 Derived Class, 즉 파생클래스 혹은 자식클래스라고 부른다.

 

공통된 분야를 묶어서 부모, 자식 클래스 즉 상속관계로 정의해두게 되면 코드의 유지보수에도, 어느 특성들을 물려받은 객체인지 확인이 가능하니 가독성도 좋아지게 된다.

 

아무튼 중복되는부분인 

float maxSpeed;
string brand;

public void Drive()
{
     Console.WriteLine("주행");
}

이 부분을 이용해서 부모 클래스 즉 car 클래스를 만들어 보겠다.

 

잠시 사용하기 편하게 변수명들 앞에는 public를 써줬다.

class Car
{

	public float maxSpeed;
	public string brand;

    public void Drive()
    {
         Console.WriteLine("주행");
     }
}

 

이렇게 중복된 부분을 겉으로 car 클래스가 감싸면 완성

 

다른 클래스가 상속받을 어떤식으로 쓰면 되는지 보여주겠다..

 

    class Sedan: Car
    {
        
        public void SedanAction()
        {
            Console.WriteLine("동승자 태우기");
        }
    }

 

아까의 코드에서 클래스명 옆에 : 콜론 + 부모클래스 이름인 Car를 적어주면 끝  그 후에

겹친부분만 제거하면 완성.

 

 

이렇게 바꾸어준 후, main으로 가서 세단을 하나 만들어보겠다.

 

Sedan myFirstCar  = new Sedan();
myFirstCar .brand = "Hyundai";
myFirstCar .maxSpeed = 240.2f;

 

세단클래스 내부에는 브랜드와 맥스스피드가 존재하지 않음에도 부모에게 물려받았기에 사용이 가능한것을 볼 수 있었다.

 

자식 클래스 내부에서 사용 하는 방법도 있다.

 

    class Sedan: Car
    {
        
        public void SedanAction()
        {
            Console.WriteLine("최고속도는 " + maxSpeed + "입니다");
        }
    }

 

정상적으로 상속을 받았으면 이런식으로 사용하면 된다.

 

이제 메인에서 써보자

 

myFirstCar.SedanAction();

 

물려받은걸 잘 확인했기에 이제 다시 부모에 public으로 전환시켜놓은 요소들을 private으로 전환시켜 외부의 불필요한 노출 및 접근을 막아 보았다.

 

필드가 private 으로 변했으니 메인에서 직접 필드를 수정하는 부분이 에러가 나는건 당연했다.

 

바로 주석을 쳐주고, 보니 SedanAction 부분또한 에러가 난 것이 보여, 마우스를 대보니 보호수준때문에 엑세스를 할 수 없다고 나온다.

 

분명 부모로부터 모든걸 내려받는척 하더니 base클래스의 필드가 private 으로 변한 순간 파생클래스에서 부모의 필드를 가져가 쓸 수 없어 졌다.

 

이를 해결하기 위한 접근지정자( Protected )에 대한 설명을 이어 가겠다.

 

접근지정자 Protected

 

public으로 두자니, 외부에서 필드를 마구 접근하여 실수를 낼 수 있다고 생각이 들고, private 로 두자니, 자식객체에서 부모에게 물려받은 필드들을 사용할 수가 없다.

 

외부에는 접근을 막고, 자식에게만 내부적으로 전부 공개가 가능한 접근 지정자가 있었다. 바로 protected 였다.

 

    class Car
    {
        protected float maxSpeed;
        protected string brand;

        public void Drive()
        {
            Console.WriteLine("주행");
        }
    }

 

아까의 maxSpeed 와 같은 맴버변수 앞에 protected를 붙여 보았다.

 

이제, 메인에서 다시 살펴보면 외부에서는 maxSpeed를 볼 수 없지만, SedanAction과 같은 파생클래스 내부에선 베이스클래스, 즉 부모로부터 자식에게 맴버들이 잘 상속이 이루어진것을 볼 수 있다.

 

메소드도, public이 아닌 protected를 쓰게 되면 Car클래스와 그 파생클래스들 내부적으로만 사용 가능한 함수가 된다.

 

그렇다면 어느 상황에서 부모클래스에 private 필드를 두게 되는걸까? 상황에 따라 여러 이유가 있을 수 있지만 

강사님이 예시로 들은 상화을 보여주겠다.

 

 자식객체에서 수정을 원치 않는 변수가 있을때다.

 

private string _carPlate= "12가1234";

 

를 추가해준 후 메소드로 아래걸 추가해주면 

public void printName()
{
     Console.WriteLine(carPlate);
}

 

이렇게 된다.

    class Car
    {
        protected float maxSpeed;
        protected string brand;
        private string _carPlate = "12가2134";


        public Car(string inputPlate)
        {
            carPlate = inputPlate;
        }
        public void Drive()
        {
            Console.WriteLine("주행");
        }
        public void PrintName()
        {
            Console.WriteLine(carPlate);
        }
    }

 

이렇게 만들어주면, 자식객체는 부모의 printName함수는 사용 가능하니 이름이 출력은 되겠지만 carPlate을 자식 객체 내부에서 직접적으로 수정하는것은 불가능할 것이다.

 

상속관계에서의 생성자

 

 class Sedan : Car
 {
     int _passengers;
                                     
     public Sedan(int passengers,string carPlate) : base(carPlate)
     {
         Console.WriteLine("자식 생성자 호출");
         _passengers = passengers;

     }

     public void SedanAction()
     {
         Console.WriteLine($"최고속도는  : {_maxSpeed} 입니다.");
     }
 }

 class Truck : Car
 {
     protected bool _isTruckModeOn;
     public Truck(string carPlate) : base(carPlate) 
     {
         Console.WriteLine("트럭 만들어짐");
     }
     public void TruckAction()
     {
         Console.WriteLine("짐 싣기");
     }
 }

위 코드는 예시 코드이다.

 

상속관계에서 부모클래스의 생성자를 불러오는 방법이다.

 

그리고 이 방법을 쓰면 부모클래스의 private 변수도 변경해줄수 있다.

 

바로 부모 클래스 내부에선 private도 변경이 가능하니 const 로 선언만 해주지 않는한.

 

생성자 내부에서 바꿔주면 해결이 된다.

 

뭐 많은걸 바꾸진 않겠지만 종종 필요할곳도 있을거같아 써두었다.