###### tags: `project` # ✊✌✋ 묵찌빠 게임 ![](https://i.imgur.com/rxIHn7P.png) ## Ground Rules ### 규칙 - TIL, 일일 회고 작성 시간(매일 23시부터 1시간 작성 진행) - 네비게이터, 드라이버, 스크라이브로 나누어서 진행 ### 스크럼 - 오전 10시 디스코드에서 진행 - 금일 진행 사항 공유하기(오늘의 할일) ### 프로젝트 규칙 - 네이밍 준수하기(가이드 라인) - 커밋 메시지 규칙 진행 - 코드에 대한 기록 그때그때 하기 ## 일일 스크럼 ### 🙌 05/01 - 오늘의 컨디션 - MINT: 신나면서 슬픔👿 - DasanKim: 신났는데 슬퍼짐 - hoon: 꿈이야...... - 특이사항 - MINT: 5월 5일 서울 가서 훈 다산 만날 예정 - DasanKim: 없음 - hoon: 없음 - 오늘 할 일 - [X] 그라운드 룰 작성 - [X] 순서도 작성 - [X] STEP 1 완료 - [X] STEP 1 PR 보내기 ### 🙌 05/02 - 오늘의 컨디션 - MINT: 졸립니당~ - DasanKim: 졸려요... - hoon: 어제보다는 개운 - 특이사항 - MINT: 1시부터 30분 가량 사라질 예정, 5시 반부터 8시까지 저녁 먹고 올게요. - DasanKim: 없음 - hoon: 방에서 나가지 않을 예정.... - 오늘 할 일 - [X] 리뷰 오면 수정 - [X] 개인 공부 - [ ] STEP 2 순서도 작성 ### 🙌 05/03 - 오늘의 컨디션 - MINT: 어제의 나는 왜 늦게 잤을까 - DasanKim: 계속 졸려요 - hoon: 쉬고 싶은 날이에요 - 특이사항 - MINT: 서울 가는 일정 사라짐.... - DasanKim: 커피를 마셔도 졸려요 - hoon: 병원을 가야해요 - 오늘 할 일 - [X] STEP 2 순서도 작성 - [X] STEP 2 페어프로그래밍 진행 - [ ] STEP 2 PR 보내기 - [ ] Human Interface Guideline 공부 ### 🙌 05/04 - 오늘의 컨디션 - MINT: 💀🥶😰🤢👿☠️ 생각보다 상쾌~ - DasanKim: 졸린 것 빼곤 괜찮습니다! - hoon: 운동을 안하니 덜 피곤해요 - 특이사항 - MINT: 오늘은 병원을 못가겠죠? 금요일에 가야겠다요. - DasanKim: 눈이 뻑뻑해여 - hoon: 목이 조금 아파요😭 - 오늘 할 일 - [X] 민트 병원 다녀오기!!!!!!!!!! - [X] STEP 2 리펙토링 - [ ] STEP 2 PR 보내기 - [X] Human Interface Guideline 공부 ### 🙌 05/05 - 오늘의 컨디션 - MINT: 자~고~싶~어~요~~ 민다훈 조합 못넘겨... 슬퍼요오오오💧 - DasanKim: 다크써클이.... 무릎을 찍었어요 - hoon: 마지막 날🥳! 이제 쉴거에요. - 특이사항 - MINT: 민다훈이 너무 조하요😈 - DasanKim: 오후 3시 친구 결혼식인데 멀어서 점심시간에 준비 및 출발할 듯합니다! 아쉽다 아쉽다아아아아아 - hoon: 비가 오면 축 처져요🌧️ - 오늘 할 일 - [x] STEP 2 리펙토링 - [x] STEP 2 PR 보내기 - [x] README 작성 - [x] README 제출 ## flowchart #### step 1 ![](https://i.imgur.com/7PNajSA.png) #### step 2 ![](https://i.imgur.com/PF0LmvA.png) ## class diagram ![](https://i.imgur.com/EQiH8Yi.jpg) <br> # ✊✌✋ 묵찌빠 게임 ## 📖 목차 1. [소개](#-소개) 2. [팀원](#-팀원) 3. [타임라인](#-타임라인) 4. [시각화된 프로젝트 구조](#-시각화된-프로젝트-구조) 5. [실행 화면](#-실행-화면) 6. [트러블 슈팅](#-트러블-슈팅) 7. [참고 링크](#-참고-링크) 8. [팀 회고](#-팀-회고) </br> ## 🍀 소개 민다훈(`MINT`, `DasanKim`, `hoon`) 팀이 만든 묵찌빠 게임입니다. 사용자는 컴퓨터와의 가위바위보를 진행하여 묵찌빠의 선후를 정합니다. 이후 묵찌빠의 게임을 이어서 진행하며 승리자를 출력합니다. * 주요 개념: `Control Flow`, `Fuctions`, `Enumerations`, `Properties`, `Methods` </br> ## 👨‍💻 팀원 | MINT | DasanKim | hoon | | :--------: | :--------: | :--------: | | <Img src = "https://i.imgur.com/QZAM3wu.jpg" width="200"> |<Img src="https://i.imgur.com/EU67fox.jpg" width="200"> |<Img src="https://i.imgur.com/muRP0gP.png" width="200"> | |[Github Profile](https://github.com/mint3382) |[Github Profile](https://github.com/DasanKim) |[Github Profile](https://github.com/Hoon94) </br> ## ⏰ 타임라인 |날짜|내용| |:--:|--| |2023.05.01.| - 가위바위보 순서도 작성 <br> - `selectMenu`함수 구현 <br> - `generateComputerRandomNumber`함수 구현 <br> - `comparePick`함수 구현| |2023.05.02.| - `comparePick` 함수 리팩토링 <br> - PR comment 작성| |2023.05.03.| - 묵찌빠 순서도 작성 <br> - `Player, Result, Game` 열거형 구현 <br> - `compareMukJjiPpa, playMukJjiPpa, printReslut` 함수 구현 <br> - `GameManager, RockPaperScissorsGame, MukJjiPpaGame` 클래스 구현| |2023.05.04.| - `GameBase class` 생성 <br> - 묵찌빠 class diagram 작성 <br> - class design 수정| |2023.05.05.| - `GameBase class`를 `Gameable protocol`로 리팩토링 <br> - README 작성| </br> ## 👀 시각화된 프로젝트 구조 ### Flowchart <p align="center"> <img width="800" src="https://hackmd.io/_uploads/SJBAUpQ43.png"> </p> ### Diagram <p align="center"> <img width="800" src="https://i.imgur.com/EQiH8Yi.jpg"> </p> </br> ## 💻 실행 화면 ### 가위바위보 | 비겼을 경우 | 사용자 승리 | |:--------:|:--------:| |<img src="https://i.imgur.com/spPCIpO.gif" width="300">|<img src= "https://i.imgur.com/AVTz85C.gif" width="300">| | 컴퓨터 승리 | 잘못된 입력 | |:--------:|:--------:| |<img src= "https://i.imgur.com/VJKxRKi.gif" width="300">|<img src= "https://i.imgur.com/sN4PZUt.gif" width="300">| | 게임 종료 | |:-------:| |<img src= "https://i.imgur.com/XOyjjfH.gif" width="300">| ### 묵찌빠 | 사용자 승리 | 컴퓨터 승리 | |:--------:|:--------:| |<img src= "https://i.imgur.com/MvEFjGN.gif" width="300">|<img src= "https://i.imgur.com/hVkF8qQ.gif" width="300">| | 잘못된 입력 | 게임 종료 | |:--------:|:-------:| |<img src= "https://i.imgur.com/vPfMURh.gif" width="300">|<img src= "https://i.imgur.com/JZF5P1O.gif" width="300">| </br> ## 🧨 트러블 슈팅 1️⃣ **재귀와 반복문 사용** <br> - 🔒 **문제점** <br> - 처음 일정한 동작을 반복하는 과정에서 재귀를 사용하였습니다. 같은 동작을 반복하는 과정에서 사용할 수 있는 방법으로 반복문과 재귀, 두 가지 방법이 있었습니다. 코드의 간결하게 작성할 수 있는 재귀를 사용하였습니다. 반복문과 재귀의 비교를 확인하였을 때 재귀는 함수 호출 시 스택 메모리 영역에 호출 때마다 쌓여 메모리 영역을 초과할 시 스택오버플로우가 발생할 수 있는 문제점을 알게 되었습니다. ``` swift guard let pick = readLine(), let menu = Menu(rawValue: pick) else { print("잘못된 입력입니다. 다시 시도해주세요.") selectMenu() return } ``` 🔑 **해결방법** <br> - `while`문을 사용하여 반복문을 사용하였습니다. 재귀 함수를 사용하는 것처럼 함수를 시작하고서 내부 구현 부분을 전체를 `while`문으로 묶어서 처리하였습니다. ``` swift func startRockPaperScissors() -> (Player, Result) { while matchResult == .draw { print("가위(1), 바위(2), 보(3)! <종료 : 0>:", terminator: " ") guard let input = readLine(), let index = Int(input), RockPaperScissorsMenu.allCases.indices.contains(index) else { print("잘못된 입력입니다. 다시 시도해주세요.") continue } playGame(user: index) printResult() } return (turn, matchResult) } ``` <br> 2️⃣ **여러 값의 비교** <br> - 🔒 **문제점** <br> - 매개변수 2개를 받아서 값을 `if-else`문을 사용하여 각각의 값을 비교하는 방법으로 코드를 작성하였습니다. 전달인자로 들어온 값을 각각 사용하여 개별적으로 비교하여 `&&` 연산을 사용하여 조건을 완성하였습니다. 각각 비교하였기 때문에 첫번째 조건이 길어져 무엇을 의미하는지 바로 와닿지 않았습니다. ```swift func comparePick(with user: String, and computer: String) { if (user == "2" && computer == "1") || (user == "1" && computer == "3") || (user == "3" && computer == "2") { print("이겼습니다") } else if user == computer { print("비겼습니다") } else { print("졌습니다") } } ``` 🔑 **해결방법** <br> - 두 개의 매개변수를 `tuple`을 사용하여 하나의 매개변수로 전달하고 `tuple`끼리 비교 연산을 사용하여 문제를 해결하였습니다. ```swift private func comparePick(with pair: (user: String, computer: String)) { let winningPair = [("2", "1"), ("1", "3"), ("3", "2")] if winningPair.contains(where: {$0 == pair}) { print("이겼습니다!") } else if pair.user == pair.computer { print("비겼습니다!") } else { print("졌습니다!") } } ``` <br> 3️⃣ **`String` 비교 대신 `RockPaperScissorsMenu` 사용자 타입 비교** <br> - 🔒 **문제점** <br> - 가위바위보의 결과를 비교하는 과정에서 `RockPaperScissorsMenu`의 `rawValue`인 `String` 타입으로 비교하였습니다. 그러니 코드를 보았을 때 어떤 짝인지, 무엇을 비교하는지에 대해서 직관적으로 이해가 되지 않았습니다. ```swift private func comparePick(with pair: (user: String, computer: String)) { let winningPair = [("2", "1"), ("1", "3"), ("3", "2")] ``` 🔑 **해결방법** <br> - `rawValue`를 줄 필요 없이 `RockPaperScissorsMenu`를 받아서 `RockPaperScissorsMenu`를 비교하는 방법으로 변경하였습니다. 가위바위보 Pair를 바로 확인할 수 있어서 어떠한 것들을 비교하는지 더 명확해졌고, `if 문`에서 비교할 때 `rawValue`를 줄 필요도 없어서 타입에 있어서도 직관성이 올라갔습니다. ```swift private func compareRockPaperScissors(_ user: RockPaperScissorsMenu, _ computer: RockPaperScissorsMenu) { let winningPair: [(user: RockPaperScissorsMenu, computer: RockPaperScissorsMenu)] = [(.rock, .scissors), (.scissors, .paper), (.paper, .rock)] if winningPair.contains(where: { $0.user == user && $0.computer == computer }) { matchResult = .win turn = .user } else if user == computer { matchResult = .draw } else { matchResult = .lose turn = .computer } } ``` <br> 4️⃣ **Diagram** <br> - 🔒 **문제점** <br> - 처음 순서도만을 가지고 코드를 작성한 후 객체의 관점으로 바라보기 위해서 각각의 class를 분리하는 과정이 있었습니다. 그 후 코드들을 더 깔끔하고 가독성이 좋기 위해 리펙토링 하는 과정에서 길고 고통스러운 고민의 과정이 있었습니다. 각각의 객체들이 하나의 역할과 책임만을 지고 있는 것이 아니라서 한 곳을 수정할 때마다 다른 곳들도 전부 수정해야했습니다. 중간에 잠시 저희들이 토론하고 있는 방에 찾아와주신 리뷰어 분들께 '이는 SOLID 원칙에 위배되기 때문에 그렇다'라는 말을 들었습니다. 🔑 **해결방법** <br> - 결국 팀원이 다같이 SOLID 원칙에 대해 공부하고, 객체 간의 역할과 책임에 대해 고민하면서 class diagram을 작성하는 시간을 가졌습니다. `GameManager`, `RockPaperScissorsGame`, `MukJjiPpaGame` 3개의 객체가 **서로 주고 받는 메세지**와 **그 메세지에 따라 넘겨주고 넘겨 받아야 하는 값**을 그림을 그려서 적어보니, 함수 하나 당 하나의 일을 하도록 만들 수 있었고 코드의 직관성도 나아졌습니다. ![](https://i.imgur.com/TjLHXX0.jpg) 5️⃣ **`class` 초기화** <br> <br> - 🔒 **문제점** <br> - `class`에 선언한 프로퍼티의 초기화를 위해 **실제 사용하지 않는 값을 사용하여 초기화**를 하였습니다. 초기화 한 후에 실제로 필요한 값을 받아 사용하기 위해 메서드에서 입력으로 사용해야 할 값들을 매개변수로 받아오도록 코드를 작성하였습니다. ```swift init() { rockPaperScissorsGame = RockPaperScissorsGame(turn: turn, matchResult: matchResult, selectedGame: selectedGame) mukJjiPpaGame = MukJjiPpaGame(turn: turn, matchResult: matchResult, selectedGame: selectedGame) } ``` 🔑 **해결방법** <br> - 프로퍼티를 처음 초기화하는 것이 아닌 사용을 할 때 초기화를 진행하기 위해 옵셔널을 사용하여 프로퍼티를 선언하였고 실제 필요한 `owner`와 `result`를 할당받은 후에 초기화를 진행하도록 하였습니다. ```swift private var mukJjiPpaGame: MukJjiPpaGame? mukJjiPpaGame = MukJjiPpaGame(turn: owner, matchResult: result) ``` 6️⃣ **`printResult` 함수의 위치** <br> - 🔒 **문제점** <br> - 결과를 출력하는 객체가 `GameManager`일지 `RockPaperScissorsGame`과 `MukJjiPpaGame`일지에 대한 의논이 있었습니다. `GameManager`에서 출력하게 된다면 다른 `Game`들의 객체에서 반복하며 출력하는 부분의 처리가 `GameManager`에서 여러 조건을 사용하여 확인해 주어야 했습니다. ```swift func playGame() { (turn, matchResult) = rockPaperScissorsGame.playRockPaperScissors() guard let owner = turn, let result = matchResult, result != .giveUp else { print("게임 종료") return } print("\(result.koreanMessage)") mukJjiPpaGame = MukJjiPpaGame(turn: owner, matchResult: result) guard let player = mukJjiPpaGame?.playMukJjiPpa(), player.status != .giveUp else { print("게임 종료") return } print("\(player.winner.koreanMessage)의 승리!") } } ``` 🔑 **해결방법** <br> - `RockPaperScissorsGame`과 `MukJjiPpaGame`가 결과를 출력하게 전환하였습니다. `GameManager`는 단순하게 가위바위보 게임을 시작하게 하고, 가위바위보 결과에 따라 묵찌빠 게임을 시작하게 하며 묵찌빠 게임이 끝나면 그대로 프로그램을 종료시켜주는 역할을 맡게 했습니다. ```swift //playGame()에서는 Print를 삭제 func playGame() { (turn, matchResult) = rockPaperScissorsGame.startRockPaperScissors() guard let owner = turn, let result = matchResult, result != .giveUp else { return } mukJjiPpaGame = MukJjiPpaGame(turn: owner, matchResult: result) mukJjiPpaGame?.startMukJjiPpa() } //rockPaperScissorsGame에서의 PrintReslut() func printResult() { print("\(matchResult.koreanMessage)") } //MukJjiPpaGame에서의 PrintResult() func printResult() { switch matchResult { case .win, .lose: print("\(turn.koreanMessage)의 턴입니다.") case .draw: print("\(turn.koreanMessage)의 승리!") case .giveUp: print("게임 종료💀") } } ``` </br> ## 📚 참고 링크 - [🍎Apple Docs: Enumerations](https://www.swift.org/documentation/api-design-guidelines/https://docs.swift.org/swift-book/documentation/the-swift-programming-language/enumerations/#Initializing-from-a-Raw-Value) - [🍎Apple Docs: Choosing Between Structures and Classes](https://developer.apple.com/documentation/swift/choosing-between-structures-and-classes) - [🍎Apple Docs: Understanding Swift Performance](https://developer.apple.com/videos/play/wwdc2016/416/) - [🍎Apple Docs: CaseIterable](https://developer.apple.com/documentation/swift/caseiterable) - [📘stackOverflow: tuples](https://stackoverflow.com/questions/29736244/how-do-i-check-if-an-array-of-tuples-contains-a-particular-one-in-swift) - [📘stackOverflow: enum](https://stackoverflow.com/questions/40636469/can-i-restrict-an-enum-to-certain-cases-of-another-enum) - [📘blog: solid](https://soojin.ro/blog/solid-principles-in-swift) </br> ## 👥 팀 회고 - [팀 회고 링크](https://github.com/mint3382/ios-rock-paper-scissors/blob/main/%ED%8C%80%ED%9A%8C%EA%B3%A0.md) # 팀 회고 ## 우리팀이 잘한 점 - 코드가 실행되도록 만드는 것에 그치지 않고, 더 좋은 코드를 만들기 위한 고민하고 노력하였습니다.🔥 - 팀원들의 상황을 이해하고 배려하는 모습👍이 프로젝트를 더 즐겁게 하였습니다.🥰 - commit message를 영어로 작성해보는 도전 정신을 발휘해 볼 수 있었습니다. - 서로를 알아가고 하루의 목표를 그릴 수 있었던 스크럼 시간을 함께 잘 지켰습니다! ## 우리팀 개선할 점 - 페어프로그래밍의 최대 시간을 정해두었으면 좋았을 것 같습니다.🤣 ## 서로에게 좋았던 점 피드백 - MINT - **to DasanKim**: 모두의 의견🗣️을 끝까지 들어준 후에 깊게 생각🧠해보신 후 본인의 의견을 이야기하시는 부분이 인상깊었어요! 생각을 깊게 하시는 것 같아 본받고 싶어요. - **to hoon**: 정리되지 않은 채 떠오른 생각을 던지면 찰떡🍡같이 받아서 비슷한 것을 찾아와주는! 코드가 어떻게 하면 더 명확⭐️할 수 있는지 항상 고민하는 끈기를 제것으로...😈 - DasanKim - 첫 3인 프로젝트인데 좋은 실력자분들을 만나 (체력적으로 피곤하지만) 즐거운 시간이었습니다. 많은 것들 배우고 갑니다! 감사했어요☺️ - **to MINT**: 팀에 활력을 주는 귀요미 민트! 뿐만아니라 프로젝트 및 코드 진행에 대한 이해가 높아 멋졌습니다. 질문을 했을 때 자세한 설명으로 이해를 할 수 있도록 도와주셨어요! - **to hoon**: 팀 프로젝트 진행에 있어 방향 제시를 부드럽고 명확하게 해주시는 부분이 감명깊었습니다. 뿐만아니라 설명도 잘해주시고! 민트가 왜 훈을 제꺼라고 하는지 알 것 같은😉 - hoon - **to MINT**: 우리 민다훈 팀의 아이디어 뱅크🏧. 아이디어 한가득🎁 ㅇㅅㅁㅌ👍 - **to DasanKim**: 네비게이터👨‍💻를 진행하며 스스로 정리📝를 하여 진행하는 모습에서 많이 배웠습니다. ## 서로에게 하고싶은 말 - MINT - 저는 희망을 버리지 않을 거에요... 꼭 다시 팀이 되면 좋겠어요! - **to DasanKim**: 진짜 놀러가도 되요?🥹 - **to hoon**: 마지막 프로젝트...훈은 제꺼에요😈 - DasanKim - 함께한 시간이 짧았다는 것이 아쉬울 뿐... - **to MINT**: 아플 때는 바로 병원 가셔야해요. 그리고 잠을 충분히 주무시기!ㅋㅋㅋ 잔소리가 되었네요🥴 - **to hoon**: 훈도 일찍 주무셔야합니다...😉 - hoon - to All: 너무 짧은 시간을 함께해서 아쉽습니다.😭😭😭 다시 한번같이하면 좋겠습니다. - **to MINT**: 건강 챙기면서 하세요!!!😠 - **to DasanKim** : 다음에 꼭 오프라인 모각코로 모여서 함께 공부해요.🥳 # STEP 1 ## 고민되었던 점 ### 튜플끼리 비교 - 처음 가위바위보의 승자와 패자를 판별하는 `comparePick()` 함수에서 단순히 `if else` 구문으로 조건을 주어서 해결하였습니다. 그 후 논의 끝에 조금 더 보기에 가독성이 있는 코드로 바꾸기 위한 노력이 있었습니다. `winningPair` 라는 튜플을 선언하여 그 안에 이기는 경우의 수들을 담아 놓고 매개변수로 받은 `pair`를 `if`문으로 비교해 해결하는 방법입니다. `winningPair` 안에 있는 경우 승리, `pair`의 내부 요소들끼리 값이 동일하면 무승부, 두 경우 모두 아니면 패배로 나누었습니다. ## 해결하지 못한 점 ### 접근제어와 난독화(Obfuscation)의 관계 - 접근제어와 관련하여 오늘 학습 활동을 진행하며 외부에서의 사용을 위해 필수적인 부분만을 공개해주는 캡슐화에 대해 배웠습니다. 이때 캡슐화를 통해 접근이 가능한 함수의 코드 길이를 최소화하는 방법으로 진행하는 것이 맞을까요? 아니면 그 함수에서 내부의 변수들 값을 변경하는 일이 없다면 크게 문제가 되는 부분들이 없을까요? - 은닉화와 난독화의 관계 또한 궁금합니다. 난독화란 프로그래밍의 언어로 작성된 코드에 대해 읽기 어렵게 만드는 작업이라는 정의를 봤습니다. 은닉화와 캡슐화 과정에서 이런 정보 보호와 같은 부분에 대해서도 관련이 있을까요? ## 조언을 얻고 싶은 부분 ### `Int.random` vs `shuffle` vs `randomElement` - `Int.random` 함수를 사용하여 `generateComputerRandomNumber` 함수에서 컴퓨터의 임의의 값을 생성하였습니다. 이때 사용했던 `Int.random`과 유사한 기능을 할 수 있는 `shuffle`과 `randomElement`를 비교해 보았습니다. `shuffle`과 `randomElement` 함수는 시간 복잡도에 대한 내용이 `O(n)`으로 documentation에 정리가 되어있었으나 `Int.random`은 확인하지 못하여 어떤 것을 사용하는 것이 더 효율적이었을지 궁금합니다. - 또한 `randomElement`의 경우 `generator` 매개변수를 통해 임의성에 대한 규칙을 제공합니다. 이때 사용하는 `inout` 키워드는 스위프트에서 제공하지만 사용에 주의를 요한다고 알고 있습니다. 이런 함수들의 사용은 괜찮을까요? ### 함수의 분리 - startGame()매소드 내에서 두가지 기능을 하고 있는 것 같아 매소드가 한가지 일을 하도록 기능을 분리해보려고 고민해보았습니다. - `유저에게 값을 받는 부분`과 `입력 받은 값에 따라 행동이 결정되는 부분`으로 기능을 분리해보려고 하였으나 기능 구분이 명확하지 않은 것 같아 조언을 얻고 싶습니다. - 그리고 두 함수를 `selectMenu()`과 `startGame()`라는 네이밍을 지을 경우 두 부분 중 어디가 더`selectMenu()`라는 함수와 어울리는지 궁금합니다. 유저에게 값을 받기 때문에 `유저에게 값을 받는 부분`이 어울리는 것 같기도 하고 결국 메뉴를 타고 들어가 메뉴 선택에 따른 행동을 결정하니 `입력받은 값에 따라 행동이 결정되는 부분`이 옳은 것 같기도 합니다. ## PR 답변 질문 - `Int.random`의 시간 복잡도는 어디서 확인이 가능할까요? - [확인 링크](https://swiftrocks.com/how-random-numbers-work-in-swift) - `in-out`: 값이 복사되지 않기 때문에, 값의 크기가 큰 경우에는 메모리 사용량이 증가할 수 있습니다. 또 함수 호출 시 값이 복사되지 않기 때문에, 함수 호출 속도가 느려질 수 있기 때문에 inout 매개변수를 사용할 때는 성능에 대한 고려도 필요합니다. - private 함수 크기 - dispatch method / factory pattern # STEP 2 ## 고민되었던 점 ### `struct` 타입에서 `class` 타입으로 변경 - `RockPaperScissorsGame`를 처음에는 `struct` 타입으로 생성하였습니다. 이번 STEP 2를 진행하면서 내부에서 사용자와 컴퓨터 간에 턴을 주고 받아야했고 이때 매번 값의 변경을 반환 값으로 나타내기보다는 프로퍼티로 생성하여 사용하고자 하였습니다. 이렇게 값에 대한 변경을 사용하는 경우에는 값 타입인 `struct`를 사용하는것 보다는 참조 타입인 `class`를 사용하는것이 더 올바른 선택지라고 생각하여 `RockPaperScissorsGame`의 타입을 수정하였습니다. ### 역할, 책임 불명확한 부분을 해결하고자 class diagram 그림 - 처음 순서도만을 가지고 코드를 작성한 후 객체의 관점으로 바라보기 위해서 각각의 class를 분리하는 과정이 있었습니다. 그 후 코드들을 더 깔끔하고 가독성이 좋기 위해 리펙토링 하는 과정에서 길고 고통스러운 고민의 과정이 있었습니다. 각각의 객체들이 하나의 역할과 책임만을 지고 있는 것이 아니라서 한 곳을 수정할 때마다 다른 곳들도 전부 수정해야했습니다. 중간에 잠시 저희들이 토론하고 있는 방에 찾아와주신 리뷰어 분들께 이는 SOLID 원칙에 위배되기 때문에 그렇다, 라는 말을 들었습니다. 결국 팀원이 다같이 SOLID 원칙에 대해 공부하고, `class diagram`을 작성하는 시간을 가졌습니다. `GameManager`, `RockPaperScissorsGame`, `MukJjiPpaGame` 3개의 객체가 서로 주고 받는 메세지와 그 메세지에 따라 넘겨주고 넘겨 받아야 하는 값을 그림을 그려서 적어보니 훨씬 명확한 코드로 변경할 수 있었습니다. ### `protocol` 채택 - `RockPaperScissorsGame`과 `MukJiiPpaGame`은 게임이라는 공통된 부분을 가지고있습니다. 이를 일반화하여 처음에는 `GameBase class`를 생성한 후 `GameBase`를 다시 두 개의 게임에 상속하는 방식으로 코드를 작성하였습니다. 그런데 class와 protocol에 대해서 class를 통한 상속은 수직적인 관계이고 protocol을 통한 채택은 수평적인 관계라는 설명을 듣게 되었습니다. 저희의 생각으로는 `Gamebase`라는 큰 부서의 밑에 `RockPaperScissorsGame`, `MukJjiPpaGame`이라는 두 부서가 있는 것 보다는 `RockPaperScissorsGame`, `MukJjiPpaGame` 두 부서가 `Gameable`이라는 방법을 채택하는 형식이 더 어울린다는 생각을 했습니다. 때문에 상속하던 `GameBase`를 프로토콜 `Gameable`로 변경해 채택하였습니다. ## 해결하지 못한 점 ### 중복 처리 - 가위바위보와 묵찌빠는 각각의 모양에 대한 순서의 차이가 있습니다. 이를 하나의 `enum`으로 해결하기가 어려워 결국 개별적으로 두 개의 `RockPaperScissorsMenu` `MukJjiPpaMenu` 열거형을 생성하였습니다. 하나의 열거형으로 묶을 경우에는 실제로 가위바위보에서 사용하지 않는 묵찌빠의 case가 함께 있는 부분에 대해 선택적인 제한을 주고 싶어서 사용하는 `enum`의 의미와 맞지 않는다고 생각하였습니다. 이를 두 가지의 타입으로 분리를 하였더니 같은 행동을 하는 함수임에도 불구하고 타입이 달라서 중복에 대한 처리를 하지 못했다는 생각이 들었습니다. 이런 부분에서는 어떻게 해결할 수 있을까요? ## 조언을 얻고 싶은 부분 ### `private`, `protocol/class` - `protocol`과 상속을 하려 하는 `class`의 경우 `private`으로 은닉화를 할 수가 없었습니다. 객체 간의 서로의 요구 사항을 전달하는 역할을 하는 것이 인터페이스이고 스위프트에서는 인터페이스가 `protocol`이니 `protocol`의 존재 이유 자체가 서로의 요구 사항을 전달하기 위함이라 은닉화를 할 수 없다는 것으로 이해했습니다. 결과적으로 하나의 장점을 포기해야 한다면 어떤 기준을 가지고 선택을 해야 할지 궁금합니다. ### `protocol`과 `class` 선택 방법 - 이전까지 배운 `class`와 `struct`에서는 서로 사용해야 하는 때가 비교적 명확하게 구분되어 있다는 생각이 들었습니다.(공식 문서로도 존재를 하기 때문에) 이번에 `class`의 상속을 사용하다가 `protocol`의 채택으로 변환하는 과정에서 다양한 설명들을 들었습니다. `class`는 수직적인 관계와 같고 `protocol`은 수평적인 관계와 같다는 느낌의 설명을 들었습니다. 이 외에도 공식 문서와 같은 정리된 내용이 존재하는지에 대해 찾아봤지만 어떤 경우에 이 두 개를 사용해야 하는지 명확한 구분은 없다고 느꼈습니다. 어떤 기준점이 있을까요? SOLID 원칙 indices, allCases, Caseiternal 곰튀김 protocol 야곰 pop 히힛 0.<~ ![](https://i.imgur.com/dD0bbb4.png) ![](https://i.imgur.com/vgT5bXy.png) ![](https://i.imgur.com/2vcGkIJ.png) [건디 링크](https://github.com/yuvinrho/ios-diary/wiki/%EC%9D%BC%EC%9D%BC%EC%8A%A4%ED%81%AC%EB%9F%BC) indices: https://limjs-dev.tistory.com/105 컬렉션의 유효범위를 가지고 있다.