처음 만들 때 버튼 생성 반복문에 들어갈 배열을 하드코딩해두었다.
내가 느끼기엔 계산기에 버튼이 어떻게 들어갈지 직관적인 모습이긴 했다.
그러나 나누기를 /
에서 ÷
로 변경한다거나 *
에서 ×
로 바꾸는 등의 수정사항이 생기거나 추가적인 버튼 배열이 필요할 때 버튼을 정의해둔 enum에서 수정이 끝나지 않고 다시 하드코딩된 이 부분을 찾아와야 하기 때문에 수정을 하는 게 더 맞다고 판단했다.
저번 숫자야구 게임 프로젝트에서의 연관값을 가진 열거형을 복습할 겸 이번에도 숫자와 연산자를 나누어 연관값으로 처리했보았다.
때문에 케이스별 rawValue를 가지지 못하니 rawValue를 대신해줄 변수를 만들었다.
var buttonTitle: String {
switch self {
case .number(let number):
switch number {
case .one:
return "1"
case .two:
return "2"
case .three:
return "3"
case .four:
(...)
}
해당 변수는 점표기법을 통해 접근할 수 있으며 해당 변수를 호출한 케이스에 따른 String 값을 얻을 수 있다.
이제 별개의 구조체를 선언하여 구조화할지 또는 열거형에서 타입메서드로 값을 반환할지 고민해보았다.
별개의 구조체로 분리한다면 필요한 데이터에 대한 구조를 좀 더 명확히 처리해둘 수 있을 것이다.
열거형에서 반대로 코드의 응집도를 높이니 버튼의 추가가 필요한 상황에서도 바로 해당 열거형 안에서 수정을 마칠 수 있다.
둘 다 static으로 선언한다면 초기화 비용이 거의 없거나 초기화가 한 번만 실행되고 메모리에 한 번만 올라간 뒤 공유하여 사용할 수 있다.
대신 열거형에 추가로 해당 메서드를 선언한다면 코드가 너무 비대해질 수 있고 구조체를 추가로 선언하는 것도 불필요한 확장이 될 수 있다.
이번 계산기 버튼 타이틀에 필요한 데이터는 값이 작고 추가적인 데이터 구조가 확장될 가능성이 적다. (공학용 계산기 모드 등을 지원하지 않을 것이라..)
따라서 더 처리가 간단한 열거형 타입 메서드로 처리해도 괜찮다고 생각했다.
상기한 이유를 바탕으로 열거형 내부에 rows 배열을 만들었다.
이제 rows와 buttonTitle을 이용해 최종적인 타이틀 배열을 만들어내면 된다.
rows 안에 있는 row들의 buttonTitle을 요소로 하는 배열을 반환받고 그 배열을 요소로 하는 배열이 필요하다.
그냥 이중배열이 필요하다는 말이고 따라서 .map을 두 번 쓰면 된다.
static func getButtonTitles() -> [[String]]{
let rows: [[Buttons]] = [
[.symbol(.clear), .symbol(.negate), .symbol(.percent), .symbol(.divide)],
[.number(.one), .number(.two), .number(.three), .symbol(.plus)],
[.number(.four), .number(.five), .number(.six), .symbol(.minus)],
[.number(.seven), .number(.eight), .number(.nine), .symbol(.multiply)],
[.number(.zero), .number(.dot), .symbol(.equal)]
]
return rows.map { row in
row.map { $0.buttonTitle }
}
}
(getButtonArray에서 조금 더 명확한 전달을 위해 getButtonTitles로 이름을 변경했다. 프로퍼티 이름도 buttonsArray에서 buttonTitle로 변경.)
하드코딩 한 줄이 사라졌다!