////
Search
Duplicate
📨

18. 테이블 활용 기법

테이블 활용 기법은 어떤 정보를 찾기 위해서 조건문을 사용하지 않고 테이블에 있는 정보를 검색하는 방식이다.
조건이 간단할 때는 논리적인 명령문이 더 쉽고 직접적이나 조건문이 복잡해질수록 테이블 방식이 더 나을 것이다.

1. 테이블 활용 기법에서 일반적으로 고려해야할 사항

테이블을 활용한 기법의 두 가지 문제점

테이블에 있는 인테르리를 어떻게 참조할 것인가
어떤 데이터는 테이블에 직접 접근하는데 사용할 수 있다.
가령 데이터를 월별로 분류하는 경우, 월별 테이블에 입력하는 것이 간단하며 이들의 인덱스는 1부터 12가 될 것이다.
어떤 데이터는 테이블 엔트리를 사용하는 것이 적합하지 않을 수 있는데, 주민등록번호를 이용해 데이터를 분류하는 경우, 999999-9999999 포맷의 엔트리를 테이블에 저장할 수 없다면 이는 다른 접근법을 선택해야 한다.
테이블에 무엇을 저장할지 설명해야 한다.
테이블 참조 결과 자체가 데이터인 경우도 있다 그런 경우라면 테이블에 데이터를 저장해도 상관없다.
테이블 참조 결과 자체가 어떠한 행위인 경우, 행위를 나타내는 코드를 저장하거나 행위를 구현하는 메소드에 대한 참조를 저장하라. 이 경우 테이블이 복잡해진다.

2. 직접 접근 방식

모든 테이블 검색 방식과 마찬가지로 지겁 접근 방식도 복잡한 논리 제어 구조를 대신해 사용할 수 있다.
이 방식은 테이블에서 원하는 정보를 찾기 위해 다른 곳으로 이동할 필요가 없기 때문에 직접 저근 방식이라고 한다.

월별 일수 예제

월별 일수를 구해야한다고 가정해보자. 물론 다음처럼 거대한 if 문으로 작성할 수 있다.
If ( month = 1 ) Then days = 31 ElseIf ( month = 2 ) Then days = 28 ElseIf ( month = 3 ) Then days = 31 ElseIf ( month = 4 ) Then days = 30 ElseIf ( month = 5 ) Then days = 31 ElseIf ( month = 6 ) Then days = 30 ElseIf ( month = 7 ) Then days = 31 ElseIf ( month = 8 ) Then days = 31 ElseIf ( month = 9 ) Then days = 30 ElseIf ( month = 10 ) Then days = 31 ElseIf ( month = 11 ) Then days = 30 ElseIf ( month = 12 ) Then days = 31 End If
Visual Basic
복사
데이터를 테이블로 사용하면 같은 기능을 더 이해하고 수정하기 쉽게 고칠 수 있다. enum을 사용해도 좋다.
Dim daysPerMonth() As Integer = _ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
Visual Basic
복사
비주얼 베이직에서 사용하는 방식이다.
이처럼 데이터를 사용해 다수의 if 문을 직접 접근 방식으로 전환할 수 있다.

보험료 예제

의료보험료를 계산하는 프로그램에서 보험료가 나이, 성별, 결혼 여부, 흡연 여부에 따라 달라진다고 가정해보자.
보험료에 대한 논리적인 제어 구조를 작성해야 한다면 다음과 같이 작성할 것이다.
if ( gender == Gender.Female ) { if ( maritalStatus == MaritalStatus.Single ) { if ( smokingStatus == SmokingStatus.NonSmoking ) { if ( age < 18 ) { rate = 200.00; } else if ( age == 18 ) { rate = 250.00; } else if ( age == 19 ) { rate = 300.00; } ... else if ( 65 < age ) { rate = 450.00; } else { if ( age < 18 ) { rate = 250.00; } else if ( age == 18 ) { rate = 300.00; } else if ( age == 19 ) { rate = 350.00; } ... else if ( 65 < age ) { rate = 575.00; } } else if ( maritalStatus == MaritalStatus.Married ) ... }
Visual Basic
복사
이 코드는 논리 구조를 임의로 어느정도 생략했는데도 불구하고 복잡하다. 이 코드는 결혼한 여성이나 남성, 18세부터 65세 사이의 연령층 대부분의 조건을 생략했음에도 어렵다.
나이만이 아닌 배열에 모든 요소를 넣고 보험료를 입력하는 것이 낫다. 그러한 배열을 다음과 같이 선언할 수 있다.
Public Enum SmokingStatus SmokingStatus_First = 0 SmokingStatus_Smoking = 0 SmokingStatus_NonSmoking = 1 SmokingStatus_Last = 1 End Enum Public Enum Gender Gender_First = 0 Gender_Male = 0 Gender_Female = 1 Gender_Last = 1 End Enum Public Enum MaritalStatus MaritalStatus_First = 0 MaritalStatus_Single = 0 MaritalStatus_Married = 1 MaritalStatus_Last = 1 End Enum Const MAX_AGE As Integer = 125 Dim rateTable ( SmokingStatus_Last, Gender_Last, MaritalStatus_Last, _ MAX_AGE ) As Double
Visual Basic
복사

유연한 메시지 형식 예제

코드로 구현하기에 동적인 논리 구조에도 테이블을 사용할 수 있다.
파일에 저장된 메시지를 출력하는 메소드를 작성하고 있다고 가정해보자.
파일은 보통 500개의 메시지를 포함하고 있으며 각 파일에는 20가지 종류의 메시지가 들어 있다. 메시지는 부표로부터 발생하고 수온과 부표의 위치 등을 제공한다.
각 메시지는 필드를 여럿 가지고 있으며 메시지의 종류를 나타내는 ID를 포함하고 있는 헤더로 시작한다.
논리적 접근 방식
논리적 접근 방식을 사용했다면 메시지를 읽어서 ID를 검사한 후 메시지를 읽고 해석하여 출력하는 메소드를 호출할 것이다.
메시지의 종류가 20개라면 20개의 메소드가 존재할 것이며 메시지를 지원하는 하위 수준의 메소드도 여럿 작성해야할 것이다.
메시지 형식이 바뀔 때마다 해당 메시지를 담당하는 메소드나 클래스의 코드를 변경해야 할 것이다.
다음과 같이 각 메시지 타입을 조건문으로 검사하고 그에 해당하는 메소드를 호출하는 형태로 의사코드가 작성된다.
While more messages to read Read a message header Decode the message ID from the message header If the message header is type 1 then Print a type 1 message Else if the message header is type 2 then Print a type 2 message ... Else if the message header is type 19 then Print a type 19 message Else if the message header is type 20 then Print a type 20 message
Visual Basic
복사
객체지향적인 접근법
논리적인 조건문 코드들을 객체의 상속 구조에 숨길 수 있겠지만 그 구현 자체는 여전히 복잡하다.
다음과 같은 의사코드가 작성된다.
While more messages to read Read a message header Decode the message ID from the message header If the message header is type 1 then Instantiate a type 1 message object Else if the message header is type 2 then Instantiate a type 2 message object ... Else if the message header is type 19 then Instantiate a type 19 message object Else if the message header is type 20 then Instantiate a type 20 message object End if End While
Visual Basic
복사
테이블을 활용한 접근법
테이블을 활용한 접근법은 이런 경우에 대해서 다른 접근법들에 비해 훨씬 경제적이다.
메시지를 읽는 메소드는 각 메시지의 헤더를 읽어 ID를 해석하고 Message 배열에서 메시지 설명을 찾은 다음 메시지를 포매팅하기 위해 항상 같은 메소드를 호출해주면 끝이다.
테이블을 활용한 접근법을 사용하면 각 메시지 형식을 코드로 작성하는 대신 테이블에 기술해둘 수 있다.
다음과 같은 의사코드를 작성할 수 있다.
While more messages to read Read a message header Decode the message ID from the message header Look up the message description in the message-description table Read the message fields and print them based on the message description End While
Visual Basic
복사
상속과 다형성을 사용한다고 해서 항상 좋은 설계가 되는 것은 아니므로 주의해서 사용하자. 그리고 해당 해결책은 객체지향도 기능 위주도 아닌 참조 테이블을 잘 사용한 것이다.
참조 키 변경하기
앞에서 살펴본 세 예제에선 테이블에 접근하기 위해 데이터를 바로 사용할 수 있었다. 즉 messageID를 변경없이 키값으로 사용할 수 있었다.
이 방법이 간단하고 빠르기 때문에 항상 테이블에 직접 접근하고 싶어한다. 하지만 조회 시 messageID가 아니라 coordinate를 사용해야 한다면? 직접적인 접근이 불가능해진다.
따라서 참조 키 변경이라는 주제가 나오게 되었다. 키 값을 변경하는 방법은 다양하다.
키 값을 변경하는 방법은 다양하며 다음과 같은 방법들이 존재한다.
키 변환 기능을 메소드로 작성한다.
데이터를 키로 변환하는 연산을 메소드로 작성하는 방법으로 자바는 키와 값 쌍을 관리하는 데 사용할 수 있는 HashMap을 제공한다.
직접 접근할 수 있도록 키 값을 변환한다.
요지는 키 값이 될 수 있도록 데이터에 연산을 수행하는 방법인 것 같은데,, 적절한지는 잘 모르겠다.

3. 인덱스 접근 방식

때때로 Age를 테이블의 키 값을 변환하는 것처럼 간단한 수학적 변환만으로는 충분하지 않은 경우가 있다. 그러한 경우 인덱스 접근 방식이 적합하다.
인덱스를 사용하면 인덱스 테이블에 있는 키를 참조하기 위해서 원본 데이터를 인덱스로 사용하고 데이터를 참조하기 위해서 인덱스 테이블의 값을 사용한다.
인덱스 접근 방식은 다음 두 가지 장점을 가진다.
주 참조 테이블에 있는 각 엔트리가 크다면 많은 공간이 낭비되는 주 참조 테이블을 만드는 대신 인덱스 배열을 만드는 것이 공간을 더 적게 사용한다.
인덱스를 사용함으로써 공간을 절약하지 못한다 하더라도 주 테이블에 있는 엔트리보다 인덱스에 있는 엔트리를 다루는 것이 더 간단한 경우가 존재한다.

4. 단계적 접근 방식

단계적 접근 방식은 계단에 따라 수준을 결정하여 엔트리를 분류한다.
이 접근 방식은 다른 테이블을 활용하는 방식보다 불규칙한 데이터를 더 잘 다룰 수 있다는 장점을 제공한다.
다음은 단계적 접근 방식을 사용할 때 고려할 사항이다.
종결점을 확인하라.
각 단계의 최댓값에 해당하는 경우를 처리했는지 확인해야 한다.
<<=를 혼동하지 않도록 한다.
순차 검색법 대신 이진 검색법 사용을 고려하라.
단계적 접근 방식 대신 인덱스 접근 방식을 고려해보라.
실행 속도가 중요한 경우라면 인덱스 접근 방식이 더 나을 수 있다.

요점 정리

테이블을 복잡한 논리 코드와 상속 구조에 대한 대안을 제공한다. 프로그램의 논리나 상속 트리가 혼란스럽다고 생각한다면 참조 테이블을 사용하여 단순화할 수 있는지 확인해본다.
테이블을 사용할 때 한가지 고려할 핵심은 테이블 접근법을 결정하는 것이다. 직접 접근 방식이나 인덱스 접근 방식, 단계적 접근 방식을 사용하여 테이블에 접근할 수 있다.
테이블을 사용할 때는 테이블에 무엇을 입력할지 결정하는 게 매우 중요하다.