[Revit API]01-7_C#Basic_객체지향-02

JakkeLab·2023년 11월 29일

Revit API

목록 보기
8/9

이전시간에 기본적으로 객체를 정의하는 방법에 대해 알아보았습니다. 다만 기존에 작성한 코드는
아래와 같은 문제들이 있었습니다.

  1. 상판 유형을 사용자가 임의로 아무값이나 넣을 수 있습니다.
  2. 가격정보를 미리 정해두는게 아니라 책상이 생성될 때 정해집니다.
  3. 책상의 크기에 따라 가격을 계산해주는 부분이 없어 (예를들어 폭 200당 얼마 등) 가격을 규칙성 있게 정의할 수
    없습니다.
  4. 이 코드를 이용해서 책상 판매 기능을 여럿이서 만들 때, 서로 임의로 가격정보를 가져올 수 있어서
    책상 가격이나 원가에 대한 정보를 다른 작업자에게 노출시킬 우려가 있습니다.

각 상황들이 어떤 상황인지 알아봅시다.

  1. 상판 유형을 사용자가 임의로 아무값이나 넣을 수 있습니다.

문자열을 통해서 임의의 값을 다 받을 수 있도록 되어있기 때문에, 손님이 임의로 값을 계속해서 넣는다면 대응해야할 가지수가 무한정으로 늘어날 수 있다는 문제가 있습니다.

  1. 가격정보를 미리 정해두는게 아니라 책상이 생성될 때 정해집니다.

우리가 일반적으로 물건을 살 때 물품의 종류에 따라 가격이 이미 정해져 있거나 주문 제작형 제품의 경우 제품의 사이즈 등에 의해 일정한 규칙을 통해서 가격이 책정이 되고, 우리는 판매자가 정한 그 가격을 확인하게 됩니다.

  1. 책상의 크기에 따라 가격을 계산해주는 부분이 없어 (예를들어 폭 200당 얼마 등) 가격을 규칙성 있게 정의할 수 없습니다.

2에서 지적한 문제와 비슷하기는 합니다. 이미 정의된 속성의 값에서 추가적인 행위를 통해 새로운 값으로 만들어서 구매자가 이를 확인할 수 있도록 해야 합니다.

  1. 이 코드를 이용해서 책상 판매 기능을 여럿이서 만들 때, 서로 임의로 가격정보를 가져올 수 있어서 책상 가격이나 원가에 대한 정보를 다른 작업자에게 노출시킬 우려가 있습니다.

이전 강의에서 알려드린 속성의 정의방법은 아래와 같습니다.

public string Property { get; set;}

위와 같이 설정한 속성은 외부에서 얼마든지 접근하여 수정할 수 있는 방식입니다. 우리는 외부에서 함부로 수정하거나, 값을 얻지 못하도록 해서 데이터를 보호해야 합니다. (책상의 가격을 임의로 바꾸거나 원가를 알아내는 등의 행위를 금지하기 위해)

이번 강의에서는 1번 항목의 해결방법에 대해 알아보겠습니다.


여러가지 방법이 있겠지만, 저는 열거형을 이용한 방법으로 하겠습니다.

열거형(Enum)이란?

출처 : Microsoft

즉, 일정한 정수 값의 집합이지만 각 값들에 이름을 붙여서 의미있는 상수들의 집합으로 만들 수 있습니다.
예를들어

public enum Furniture
{
	Desk,
    Chair,
    Bed,
    ...
}

와 같이 정의하였다면 이후 다른곳에서

Furniture fur1 = new Furniture();
Furniture fur2 = Furniture.Desk;

와 같이 변수를 정의하여 이름 자체로 변수간의 비교 등이 가능합니다.

열거형을 활용하기 위해 책상 상판의 대표유형을 3가지로 한정시켜서 TableTops라는 열거형을 아래와 같이 정의합니다.

public enum TableTops
{
	NotDefined
	Maple,
	Mahogany,
	Ash
}

이 때, 각 항목에 해당하는 정수는 맨 위부터 0, 1, 2, ... 로 매겨지며 아래와 같이 별도로 지정해주면, 해당 값으로 매겨집니다.

public enum TableTops
{
	NotDefined,
	Maple,
	Mahogany=7,
	Ash  `		//8
}

이 때 주의할 점은, 특정 값을 지정한 후 그다음 항목에서 지정을 생략하게 되면 바로 이전에 지정된 정수의 다음 값으로 정해집니다.
위 예시에서는 중간에 3이라는 값을 지정하였기 때문에 4번째 항목은 8로 지정이 됩니다.

이제, 책상 상판 속성의 타입을 string에서 TableTops로 바꿔줍니다.

그리고 기존의 코드를 수정하지 않고 이전처럼 문자열을 넣으려고 하면 오류가 발생합니다.

이렇게 속성으로 쓸 수 있는 값들을 한정시켜줄 수 있게 되었습니다. 한편 enum은 기본적으로 정수형 상수이기 때문에

이렇게 정수를 넣어서 지정하도록 하는것도 가능합니다. 대신 열거형에 없는 상수를 대입하려고 하면


이렇게 오류가 발생합니다. 따라서 우리는 enum을 사용하여 책상 상판의 종류를 한정시켜줬다고 볼 수 있습니다.

한편, 입력할 수 있는 값의 종류가 제한되었으므로 우리는 구매자에게 어떤종류의 상판들이 있는지 알려줘야 합니다.
따라서 상판의 이름을 가져오기 위한 메소드를 하나 만들어줍니다.

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;
}

다만 상판 종류의 항목 중, 정의되지 않았음을 의미하는 0번은 포함하지 않아야 하므로 if문을 적절히 사용해줍니다. 이후, 메소드를 실행시키면 아래와 같이 출력됩니다.

여기서 static이란, 정적 멤버로 정의하기 위해 붙이는 키워드입니다. 정적 멤버는 별도의 객체 생성 없이 프로그램이 실행될 때부터 바로 접근 가능하도록 하는 것으로, 아래와 같이 바로 실행할 수 있도록 합니다.

...
//Desk 객체 생성되지 않은 상태에서도 메소드 호출 가능
string tableTops = Desk.ShowTableTops();
Console.WriteLine(tableTops);
//1. Maple
//2. Mahogany
//3. Ash
//와 같이 출력됨.
...

지금까지 우리는 기존 코드에서 책상 상판의 종류를 한정시키기 위해 열거형, 그리고 어디에서든 접근 가능하도록 하는 정적멤버 키워드인 static을 사용하는 법을 확인하였습니다.

이번 강의를 통해 현재까지 완성된 코드는 아래와 같습니다.

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 TableTops TableTop { get; set; }
        public int Price { get; set; }
        #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}, L : {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
    }
}

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("==========");
            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;
                    default:
                        Console.WriteLine("잘못 입력하였습니다. 다시 입력해 주세요.");
                        break;
                }
                Console.WriteLine("주문내역을 더 확인하겠습니까?(Y/N)");
                string checkNewOrder = Console.ReadLine();
                if (checkNewOrder == "Y")
                {
                    continue;
                }
                else
                {
                    Console.WriteLine("주문확인을 끝냅니다.");
                }
            }
        }
    }
}

다음 강의에서는 2번 항목을 고치기위해 상속, 인터페이스를 사용하는 법을 살펴보겠습니다.

이번시간에 작성한 코드는 아래 리포지토리에서 내려받을 수 있습니다.
https://github.com/JakkeLab/Tutorial_RevitAPI/tree/main/src

profile
시간을 설계하는 건축가

0개의 댓글