22. 12. 30 (금)
# 학습 내용 22.12.30 (금)
## Protocol
### 🔎 간단한 개념정리
- 프로토콜은 타입의 ```청사진```이라고 할 수 있다. 프로토콜을 이용해 특정 역할을 수행하기 위한 요구사항(메서드, 프로퍼티, 이니셜라이저)을 구현해줄 수 있다.
- 구현한 프로토콜은 타입이 ```채택```할 수 있고, 이를 해당 타입이 프로토콜을 ```준수한다```고 표현한다.
- 프로토콜에서 정의한 코드는 채택한 타입 내부에서 반드시 구현해야 한다.
- 프로토콜은 class, struct, enum 모두 채택할 수 있고 다중 채택이 가능하다.
- ```protocl```정의 후 ```extension```을 사용하여 ```protocol```내부의 **아직 구현되지 않은** 프로퍼티, 메서드의 내용들을 구현할 수 있다.
### ✏️ 예제 코드
```swift
// 프로토콜 정의
protocol Speakable {
var language: String { get }
func greeting()
}
// 익스텐션 - 함수의 기본 기능 구현해두기
extension Speakable {
func greeting() {
print("안녕하세요?")
}
}
// Speakable protocol을 준수하는 class 정의
class KoreanPerson: Speakable {
var language = "Korean"
let name: String
init(name: String) {
self.name = name
}
}
// 익스텐션에 구현해둔 내용과 다른 기능을 구현하기(익스텐션이 없을 경우 필수)
class JapanesPerson: Speakable {
var language = "Japanese"
let name: String
func greeting() {
print("콘방와 \(name)또 모우시마스 요로시꾸!")
}
init(name: String) {
self.name = name
}
}
let rowan = KoreanPerson(name: "Rowan")
let japaneseRowan = JapanesPerson(name: "로-와은")
rowan.greeting() // 안녕하세요?
japaneseRowan.greeting() // 콘방와 로-와은또 모우시마스 요로시꾸!
```
---
## STEP2 개선에 대한 고민
아마 다른 캠퍼분들도 비슷한 고민을 하셨겠죠?
'어떻게 하면 비슷하게 생긴 가위바위보와 묵찌빠를 예쁜 코드로 표현할 수 있을까?'
해결이 되지 않은 채 남겨두고 싶지 않은 마음에 TIL에 혼자 고민해봅니다.
### 개선 방안 1. class의 상속 사용하기
class는 ```상속```이 가능하다는 특징이 있습니다. 프로젝트 진행 중에 묵찌빠와 가위바위보는 겹치는 기능의 메서드들이 많으니 상위 클래스에 모아두면 좋지 않을까? 하는 생각이 여러번 들었습니다.
하지만 시도해본 결과 가위바위보를 먼저 구현하고 나면, 묵찌빠는 메서드에 parameter의 argument type이 변경되는 등의 문제점으로 인해서 ```override```키워드를 통해 함수를 다시 정의하는 일이 불가피합니다.
그럼 상속을 받는 의미가 없어지는 것 같아 이 방법은 그렇게 좋은 방법이 아닌 것 같다고 생각했습니다.
### 개선 방안 2. extension을 이용하여 하나의 클래스에 가위바위보, 묵찌빠 코드를 나눠 표현하기
저희 4조가 선택했던 방법입니다. 하나의 클래스에 가위바위보, 묵찌빠의 기능을 모두 구현하려다 보니 비슷한 코드가 나열되어있어 보기 불편하여 extension을 통해 묵찌빠와 관련된 기능을 따로 작성해주었습니다.
관련된 기능을 묶어놓는 점은 좋지만 자꾸만 가위바위보와 묵찌빠를 확실하게 분리하고 싶다는 생각이 들었습니다...🧐
### 개선 방안 3. protocol의 사용
여러분들의 TIL도 둘러보면서 직접 고민하다 문득 생각난 방법이 있습니다. 바로바로 protocol을 정의해 가위바위보와 묵찌빠의 공통적인 메서드, 프로퍼티를 미리 골라놓는 것이죠!
protocol을 사용하면 어느정도 보기 좋은 결과가 나올 것으로 예상됩니다. protocol은 타입의 ```청사진```이라고 해요. 메서드나 프로퍼티의 이름만 정해주고 실제 기능 구현은 타입을 정의할 때 내부에서 이루어지기 때문입니다.
또한 가위바위보와 묵찌빠에 공통적으로 사용되는 프로퍼티와 메서드는 extension을 통해 미리 기능을 구현해둘 수 있습니다.
**개선 방안 3.에서 상상해본 struct와 class의 장단점**
◾️ struct로 타입을 정의할 경우
- 장점: memberwise initializer가 제공됨
- 단점: 프로퍼티를 변경하는 메서드를 사용할 때 ```mutating``` 키워드가 필요, winner 전달이 쉽지않음....
◾️ class로 정의할 경우
- 장점: 프로퍼티를 변경하는 메서드를 ```mutating``` 없이 정의할 수 있고 참조 타입이라 winner 전달에 용이함
- 단점: ```init()```을 직접 작성해야 함
struct로 타입을 만들면 가위바위보, 묵찌빠 분리 이후에 두 게임을 연결할 때 매우매우 어려울 것 같으므로 ~~(어려운 이유: mutating과 외부 값 받아오기, 가위바위보와는 다르게 묵찌빠에서는 메서드에 프로퍼티가 필요한 경우 등)~~ 지금은 class로 해보는게 좋겠습니다.
그러면 class로 가위바위보, 묵찌빠를 만들려면 어떻게 해야할까요?
간단하게 프로젝트 일부 코드를 예를 들어 표현해보겠습니다. (좀 길지만요..)
```swift
// 프로토콜에 미리 겹치는 함수 선언해두기
protocol HandGame {
func startGame()
func displayMenu()
func readInput() -> Result<Menu, InputError>
func generateRockPaperScissors() -> Menu?
}
// 익스텐션에 가위바위보와 묵찌빠에서 완전히 똑같은 함수 구현해두기
extension HandGame {
func readInput() -> Result<Menu, InputError> {
guard let input = readLine(),
let number = Int(input),
let menu = Menu.get(number) else {
return .failure(.invalidInput)
}
}
func generateRockPaperScissors() -> Menu? {
let randomIndex = Int.random(in: 1...3)
let result = Menu.get(randomIndex)
return result
}
}
```
```swift
// 가위바위보
class RockPaperScissors: HandGame {
func startGame() {
displayMenu()
let userChoice = readInput()
let computerChoice = generateRockPaperScissors()
}
func displayMenu() {
let menuMessage = "가위(1), 바위(2), 보(3)! <종료: 0> :"
print(menuMessage, terminator: " ")
}
}
```
```swift
// 묵찌빠
class MookZziPpa: HandGame {
var isFirst = true
var turn: String?
func startGame() {
printTurn()
changeTrueToFalse()
displayMenu()
let userChoice = readInput()
let computerChoice = generateRockPaperScissors()
}
func printTurn() {
if self.isFirst == false {
print("\(self.turn)의 턴입니다.")
}
}
func changeTrueToFalse() {
if self.isFirst {
self.isFirst.toggle()
}
}
func displayMenu() {
let menuMessage = "[\(self.turn)턴] 묵(1), 찌(2), 빠(3)! <종료: 0> :"
print(menuMessage, terminator: " ")
}
init(turn: String?) {
self.turn = turn
}
}
}
```
어떤가요..? 가위바위보와 묵찌빠가 예쁘게 나눠질 것 같은 느낌이 들지 않나요??
(~~아님말구😂~~)
마지막으로 정의된 가위바위보와 묵찌빠는 ```GamePlayer```라는 class에서 인스턴스를 생성하여 연결해주면 좋을 것 같습니다. 언젠가는 끝까지 제대로 작성해보고 싶네요.
```swift
class GamePlayer {
var winner: String?
let gameSlotOne = RockPaperScissors()
let gameSlotTwo = MookZziPpa(turn: winner)
func playGameInSlot() {
...
gameSlotOne.startGame()
gameSlotTwo.startGame()
...
}
}
```