[Revit API]01-7_C#Basic_객체지향-03_상속

JakkeLab·2023년 12월 13일

Revit API

목록 보기
9/9
post-thumbnail
  1. 가격정보를 미리 정해두는게 아니라 책상이 생성될 때 정해집니다.

우리는 이전 시간에 책상 상판의 유형을 미리 정의해두는 방법으로 enum을 사용했습니다.

다만, 여전히 책상의 가격은 책상 인스턴스가 생성될 때 값을 따로 정의하면서 정해집니다. 커스텀 주문이 아니라 정해놓은 몇가지 제품 유형에 속하는 제품들은 제품 자체, 가격 등을 미리 정의해서 외부의 조작등을 통해 변경되지 않도록 하거나, 이후에 가격을 변경하게 될 때 클래스가 구현된 곳이 아닌 책상 주문 코드 상에서 일일히 다 바꿔줘야하는 번거로움이 있습니다.

이를 위해 "상속"과 "읽기전용" 속성을 이용하겠습니다.


상속이란?

일반적으로 상속은 부모, 또는 조상으로부터 무언가를 물려받는 것을 의미합니다. 예를들어, 한국, 일본, 중국 이라는 국가는 모두 나라이름, 언어 등의 특성이 있습니다. "국가" 라는 범주를 미리 정의를 하고 그 속성으로 나라이름, 언어를 넣어서 정의를 해두면 각 국가는 "국가" 라는 항목을 상속받아서 그 속의 속성(나라이름, 언어)를 각각 '한국', '일본', '중국', 그리고 언어는 "한국어", "일본어", "중국어" 와 같이 써넣으면 됩니다.

즉, '국가' 라는 틀을 만들고, 해당 틀을 이용해서 정의한다 라고 생각하면 편합니다.
좀 더 쉬운 예시로는 도시락이 있습니다.

(출처 : 한솥도시락)

위 사진을 보면 모두 도시락 통의 크기, 모양은 같습니다. 그 통에 담기는 반찬들의 종류에 따라 도시락의 종류가 달라집니다. '도시락' 이라는 공통된 항목을 가지고 그 속의 내용물(속성)을 바꾸면서 서로 다른 메뉴의 도시락을 만드는 것이죠.


이를 프로그래밍에서도 상속(Inheritance) 라고 부릅니다. "부모(Parent)" 클래스에 정의된 항목을 물려받는 클래스를 "자식(Child)" 클래스 라고 부릅니다. 클래스를 상속할때는 부모 클래스의 요소들 (속성, 메소드, 생성자 등)이 넘어가게 됩니다. 따라서 자식클래스에서 속성이나 메소드를 다시 정의할 필요없이 그대로 사용할 수 있습니다.

위 사진을 보면 MapleDesk에서 Width, Length, Height 속성을 정의하지 않더라도 사용가능한 것을 볼 수 있습니다. Desk 라는 부모클래스에서 물려받았기 때문입니다.


코드

C#에서는 아래와 같이 하위클래스 오른쪽에 콜론을 쓰고 그 오른쪽에 물려받고자 하는 부모클래스를 적어서 상속을 시킵니다.

public class ParentClass
{
	public int Price {get; set;}
    
    ...
}

public class ChildClass : ParentClass
{
	...
}

저희가 만들던 코드에서는 아래와 같이 만들어줍니다.

한편 책상 인스턴스를 생성한 뒤 '책상 상판종류'와 '가격'을 변경할 수 없도록 해당 속성을 읽기 속성을 쓰기 위해 부모클래스인 Desk 클래스의 속성 중 일부를 아래와 같이 바꿉니다.

public class Desk
{
	public double Width { get; set; }
	public double Length { get; set; }
	public double Height { get; set; }
	public virtual TableTops TableTop { get; }
	public virtual int Price { get;  }
    ...
  
}

참고 : 기존코드

public class Desk
{
	public double Width { get; set; }
	public double Length { get; set; }
	public double Height { get; set; }
	public TableTops TableTop { get; set; }
	public int Price { get; set; }
    ...
}

위를 보면 TableTop과 Price를 바꾸었습니다. get, set 중 set 부분을 지우고 각각의 속성에 대해 public 뒤에 virtual을 붙여두었습니다.

그다음 각각의 책상을 상속을 이용하여 정의합니다.

    //Maple 책상 정의
    public class MapleDesk : Desk
    {
        public MapleDesk(double width, double length, double height)
        {
            this.Width = width;
            this.Length = length;
            this.Height = height;
        }

		//기본 책상 사이즈
        public MapleDesk()
        {

        }

        public override int Price { get; } = 120000;
        public override TableTops TableTop { get; } = TableTops.Maple; 
    }

Price, TableTop 속성을 override 키워드를 붙여서 재정의 한 뒤 값을 할당시켜줍니다. 여기서 override(오버라이드)란 부모클래스에서 정의한 메소드나 속성을 자식클래스에서 재정의하여 사용할 수 있도록 하는 것을 의미합니다.
(이것과 관련된 자세한 설명은 객체지향에 대한 강의 중 마지막 장에서 진행하겠습니다.)

각 책상의 가격과 상판종류가 클래스와 맞도록 설정해줍니다. 한편, 책상크기에 해당하는 속성들 (폭, 길이, 높이 등)을 입력하지 않으면 기본규격의 (1200x600x750)의 책상이 생성되도록 생성자를 추가했습니다.

최종적으로 지난시간에 Enum에 정의했던 3가지 책상(Maple, Ash, Mahogany)을 각각 자식클래스로 만들면 아래와 같습니다.

        //Maple 책상 정의
    public class MapleDesk : Desk
    {
        public MapleDesk(double width, double length, double height)
        {
            this.Width = width;
            this.Length = length;
            this.Height = height;
        }

        //기본사이즈 책상 생성자
        public MapleDesk()
        {

        }

        public override int Price { get; } = 120000;
        public override TableTops TableTop { get; } = TableTops.Maple; 
    }

    //Mahogany 책상 정의
    public class MahoganyDesk : Desk
    {
        public MahoganyDesk(double width, double length, double height)
        {
            this.Width = width;
            this.Length = length;
            this.Height = height;
        }

        //기본사이즈 책상 생성자
        public MahoganyDesk()
        {

        }

        public override int Price { get; } = 180000;
        public override TableTops TableTop { get; } = TableTops.Mahogany;
    }

    //Ash 책상 정의
    public class AshDesk : Desk
    {
        public AshDesk(double width, double length, double height)
        {
            this.Width = width;
            this.Length = length;
            this.Height = height;
        }

        //기본사이즈 책상 생성자
        public AshDesk()
        {

        }

        public override int Price { get; } = 150000;
        public override TableTops TableTop { get; } = TableTops.Ash;
    }

이제 Program에서 책상 구입과 관련된 기능중 "6. 책상 종류별 기본사이즈 가격 보기" 를 추가합니다.


가격을 불러오는 메소드는 아래와 같습니다.

		//Desk 객체의 가격 불러오기, 하위클래스를 넣어주면 하위클래스의 속성에서 가져온다.
		public int GetPrice(Desk desk)
        {
            return desk.GetPrice();
        }
		
        //모든 종류의 책상 가격 한번에 표시하기
        public static string GetAllDeskPrices()
        {
            string result = string.Empty;
            var ashDesk = new AshDesk();
            var mapleDesk = new MapleDesk();
            var mahoganyDesk = new MahoganyDesk();
            result += $"{ashDesk.TableTop} 책상({ashDesk.GetSize()}) : {ashDesk.GetPrice()}\n";
            result += $"{mapleDesk.TableTop} 책상({mapleDesk.GetSize()}) : {mapleDesk.GetPrice()}\n";
            result += $"{mahoganyDesk.TableTop} 책상({mahoganyDesk.GetSize()}) : {mahoganyDesk.GetPrice()}\n";

            return result;
        }

최종적으로 완성된 코드는 아래와 같습니다.

  1. Furnitures.cs
using System;

namespace OOP.Basic.Furnitures
{
    //책상 클래스
    public class Desk
    {
        #region 책상의 속성
        public double Width { get; set; }
        public double Length { get; set; }
        public double Height { get; set; }
        public virtual TableTops TableTop { get; }
        public virtual int Price { get;  }
        #endregion

        #region 책상 유형 기본값
        public enum TableTops
        {
            NotDefined,
            Maple,
            Mahogany,
            Ash
        }
        #endregion

        #region 책상관련 메소드

        //상판 종류 보여주기
        public static string ShowTableTops()
        {
            string result = string.Empty;
            int cnt = 1;
            foreach(TableTops tb in Enum.GetValues(typeof(TableTops)))
            {
                if(tb != TableTops.NotDefined)
                {
                    result += $"{cnt}. {tb}\n";
                    cnt++;
                }
            }
            return result;
        }

        //책상의 크기 정보 받기
        public string GetSize()
        {
            return $"W : {this.Width}, D : {this.Length}, H : {this.Height}";
        }

        //책상의 상판 정보 받기
        public TableTops GetTableTop()
        {
            return this.TableTop;
        }

        //책상의 가격정보 불러오기
        public int GetPrice()
        {
            return this.Price;
        }
        #endregion

        #region 책상 생성자

        //기본 책상 생성
        public Desk()
        {
            this.Width = 1200;
            this.Length = 600;
            this.Height = 750;
            this.TableTop = TableTops.NotDefined;
            this.Price = 80000;
        }

        //규격과 가격을 직접 입력해서 생성
        public Desk(double width, double length, double height, int price)
        {
            this.Width = width;
            this.Length = length;
            this.Height = height;
            this.TableTop = TableTops.NotDefined;
            this.Price = price;
        }

        //상판만 직접 지정해서 생성
        public Desk(string tableTop, int price)
        {
            this.Width = 1200;
            this.Length = 600;
            this.Height = 750;
            this.TableTop = TableTops.NotDefined;
            this.Price = price;
        }

        //전부 옵션을 지정해서 생성하기
        public Desk(double width, double length, double height, TableTops tableTop, int price)
        {
            this.Width = width;
            this.Length = length;
            this.Height = height;
            this.TableTop = tableTop;
            this.Price = price;
        }
        #endregion
    }

    //Maple 책상 정의
    public class MapleDesk : Desk
    {
        public MapleDesk(double width, double length, double height)
        {
            this.Width = width;
            this.Length = length;
            this.Height = height;
        }

        //기본사이즈 책상 생성자
        public MapleDesk()
        {

        }

        public override int Price { get; } = 120000;
        public override TableTops TableTop { get; } = TableTops.Maple; 
    }

    //Mahogany 책상 정의
    public class MahoganyDesk : Desk
    {
        public MahoganyDesk(double width, double length, double height)
        {
            this.Width = width;
            this.Length = length;
            this.Height = height;
        }

        //기본사이즈 책상 생성자
        public MahoganyDesk()
        {

        }

        public override int Price { get; } = 180000;
        public override TableTops TableTop { get; } = TableTops.Mahogany;
    }

    //Ash 책상 정의
    public class AshDesk : Desk
    {
        public AshDesk(double width, double length, double height)
        {
            this.Width = width;
            this.Length = length;
            this.Height = height;
        }

        //기본사이즈 책상 생성자
        public AshDesk()
        {

        }

        public override int Price { get; } = 150000;
        public override TableTops TableTop { get; } = TableTops.Ash;
    }
}
  1. Program.cs
using System;
using OOP.Basic.Furnitures;
using static OOP.Basic.Furnitures.Desk;

namespace _01_Basic
{
    public class GetOrder
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("책상 주문하기");
            Console.WriteLine("==========");
            Console.WriteLine("주문 방법 선택하기");
            Console.WriteLine("1. 기본형 책상 구매하기(1200*600*750, 메이플 옵션");
            Console.WriteLine("2. 크기만 정해서 구입하기(메이플 상판)");
            Console.WriteLine("3. 1200*600 책상의 상판만 바꿔서 구매하기");
            Console.WriteLine("4. 전부 커스텀 하기");
            Console.WriteLine("5. 상판 종류 보기");
            Console.WriteLine("6. 책상 종류별 기본사이즈 가격 보기");
            Console.WriteLine("==========");
            while (true)
            {
                Console.Write("구매 방법을 선택하세요 : ");
                int order = int.Parse(Console.ReadLine());
                Console.WriteLine("\n");
                Desk desk;
                switch (order)
                {
                    case 1:
                        //기본형 책상 생성
                        desk = new Desk();
                        Console.WriteLine("=========");
                        Console.WriteLine("구매내역");
                        Console.WriteLine($"규격 : {desk.GetSize()}");
                        Console.WriteLine($"상판 : {desk.GetTableTop()}");
                        Console.WriteLine($"가격 : {desk.GetPrice()}");
                        Console.WriteLine("=========");
                        break;

                    case 2:
                        //커스텀 크기 설정
                        desk = new Desk("Mahogany", 150000);
                        Console.WriteLine("=========");
                        Console.WriteLine("구매내역");
                        Console.WriteLine($"규격 : {desk.GetSize()}");
                        Console.WriteLine($"상판 : {desk.GetTableTop()}");
                        Console.WriteLine($"가격 : {desk.GetPrice()}");
                        Console.WriteLine("=========");
                        break;
                    case 3:
                        //상판만 변경
                        desk = new Desk(1200, 600, 750, 100000);
                        Console.WriteLine("=========");
                        Console.WriteLine("구매내역");
                        Console.WriteLine($"규격 : {desk.GetSize()}");
                        Console.WriteLine($"상판 : {desk.GetTableTop()}");
                        Console.WriteLine($"가격 : {desk.GetPrice()}");
                        Console.WriteLine("=========");
                        break;
                    case 4:
                        //전부 커스텀
                        desk = new Desk(1800, 700, 750, TableTops.NotDefined, 220000);
                        Console.WriteLine("=========");
                        Console.WriteLine("구매내역");
                        Console.WriteLine($"규격 : {desk.GetSize()}");
                        Console.WriteLine($"상판 : {desk.GetTableTop()}");
                        Console.WriteLine($"가격 : {desk.GetPrice()}");
                        Console.WriteLine("=========");
                        break;
                    case 5:
                        Console.WriteLine(Desk.ShowTableTops());
                        break;
                    case 6:
                        Console.WriteLine(GetAllDeskPrices());
                        break;
                    default:
                        Console.WriteLine("잘못 입력하였습니다. 다시 입력해 주세요.");
                        break;
                }
                Console.WriteLine("주문내역을 더 확인하겠습니까?(Y/N)");
                string checkNewOrder = Console.ReadLine();
                if (checkNewOrder == "Y")
                {
                    continue;
                }
                else
                {
                    Console.WriteLine("주문확인을 끝냅니다.");
                }
            }
        }

        public int GetPrice(Desk desk)
        {
            return desk.GetPrice();
        }

        public static string GetAllDeskPrices()
        {
            string result = string.Empty;
            var ashDesk = new AshDesk();
            var mapleDesk = new MapleDesk();
            var mahoganyDesk = new MahoganyDesk();
            result += $"{ashDesk.TableTop} 책상({ashDesk.GetSize()}) : {ashDesk.GetPrice()}\n";
            result += $"{mapleDesk.TableTop} 책상({mapleDesk.GetSize()}) : {mapleDesk.GetPrice()}\n";
            result += $"{mahoganyDesk.TableTop} 책상({mahoganyDesk.GetSize()}) : {mahoganyDesk.GetPrice()}\n";

            return result;
        }
    }
}

실행화면

이제 클래스를 정의한 Furnitures.cs 파일 내에서 가격을 변경하는 것 만으로 가격을 제어할 수 있게 되었습니다.


소스코드
https://github.com/JakkeLab/Tutorial_RevitAPI/tree/main/src

Part1-7_3_OOP_Inheritance 디렉토리에 있습니다.

profile
시간을 설계하는 건축가

0개의 댓글