Struct를 활용해서 채팅 메세지를 만든다고 가정해보겠습니다.
채팅메세지에는 아래와 같은 형식을 가지고 있습니다.
위와 같은 메세지 형태를 Struct
로 표현했을 경우와 Enum
으로 표현했을 경우에는 어떤 차이점이 있는지 확인해보겠습니다.
struct Message {
let userId: Int
let contents: String?
let data: Date
let hasJoined: Bool
let hasLeft: Bool
}
let joinMessage = Message(userId: 1, contents: nil, date: Date(), hasJoined: true, hasLeft: false)
let textMessage = Message(userId: 1, contents: "Hello World", date: Date(), hasJoined: false, hasLeft: false))
let leftMessage = Message(userId: 1, contents: nil, date: Date(), hasJoined: true, hasLeft: true))
Struct를 활용하면 잘못된 상태의 객체가 만들어질 수 있습니다.
let wrongMessage = Message(userId: 1, contents: "Hi", hasJoined: true, hasLeft: true)
이처럼 의도하지 않은 잘못된 상태의 객체가 만들어질 수 없도록 효율적인 방법이 없을까요?
- Enum은 '상호 배타적'인 관계를 표현할 수 있습니다.
- 연관 값(Tuple)을 이용하면, 데이터도 포함할 수 있습니다.
enum Message {
case text(userId: Int, contents: String, date: Date)
case join(userId: Int, date: Date)
case leave(userId: Int, date: Date)
}
let joinMessage: Message = Message.join(userId: 1, contents: "hello", date: Date())
let textMessage = Message.text(userId: 1, contents: "hello", date: Date())
let leaveMessage: Message = .leave(userId: 1, date: Date())
enum
을 활용한 switch-case
문을 활용할 경우에는 default
와 같이 모든 case에 대해서 처리하는 것 보다는, 모든 경우를 처리하는 형태로 만들어 놓는 것이 좋습니다.
func logMessage(message: Message) {
switch message {
case .text(userId: let userId, contents: let contents, date: let date):
break
case .join(userId: let userId, date: let date):
break
case .leave(userId: let userId, date: let date):
break
}
}
default
로 모든 경우를 처리할 경우에 enum
에 새로운 값이 추가되었을 경우에 컴파일러가 오류 없이 처리하기 때문에, 개발자가 의도치 않은 작동을 막아주는 역할을 기대할 수 없기 때문입니다.
Enum
을 사용하다 보면 if-else
보다는 switch-case
를 자주 사용하게 됩니다. 때문에 다양한 표현 방법을 알고 있는 것이 큰 도움이 될 때가 있습니다.
switch message {
case let .text(userId, contents, date):
break
case let .join(userId, date):
break
case let .leave(userId, date):
break
}
if case .text(userId: let userId, contents: let contents, date: let date) = textMessage {
...
}
if case .join(let userId, let contents) = joinMessage {
...
}
if case let .leave(userId, date) = leaveMessage {
...
}
enum DataType {
case date(Date)
case string(String)
case int(Int)
case double(Double)
// 새로운 항목이 추가되면, 컴파일러가 오류를 통해 처리해야 하는 부분을 알려줍니다.
case intRange(Range<Int>)
// 1...10: CloseRange
// 0..<10: Range
case dateRange(Range<Date>)
}
let startDate = Date()
let endDate = Date().addingTimeInterval(10)
let arr: [DataType] = [
.date(Date()),
.string("Hello"),
.int(42),
.double(3.14),
.intRange(1 ..< 10),
.dateRange(startDate ..< endDate)
]
for element in arr {
switch element {
case let .date(date):
print(date)
case let .string(string):
print(string)
case let .int(int):
print(int)
case let .double(double):
print(double)
case let .intRnage(range):
for e in range {
print(e)
}
case let .dateRange(range):
print(range)
}
}