【Swift】Xcodeで始めるアプリ開発入門④

# この投稿は...

前回の「[Xcodeで始めるアプリ開発入門③](https://qiita.com/imchino/items/c10117ca9e0e2f47c047)」に続き、元素について学ぶことができる、コマンドラインインターフェース（CLI）のアプリケーションを開発します。

> Swiftを基礎から学ぶには
自著、工学社より発売中の「[まるごと分かるSwiftプログラミング](https://amzn.asia/d/7YPWJwl)」をお勧めします。変数、関数、フロー制御構文、データ構造はもちろん、構造体からクロージャ、エクステンション、プロトコル、クロージャまでを基礎からわかりやすく解説しています。
> また、Swiftプログラミングを基礎から動画で学びたい方には、Udemyコース「[今日からはじめるプログラミング](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwii1qTuj_2BAxV4hlYBHbj5BqEQFnoECBYQAQ&url=https%3A%2F%2Fwww.udemy.com%2Fcourse%2Fstart-programming-today%2F&usg=AOvVaw2vs4Sccq-bpfA4ZD2rCyNm&opi=89978449)」をお勧めします。

# ハンズオン

コードを適切に抽象化すると、読みやすさと表現力が改善されます。
そのためには「冗長なタスク」を分割して、メソッドを抽象化します。

### メソッドの抽象化

`start()`メソッドで行われるタスクは...
- 導入パート
- ユーザー名の設定
- 学習方法の選択
...の3つです。

それぞれを抽出して、3つのメソッドに分割します。

さらに、`start()`メソッドでは何度か`print()`関数と`readLine()`関数を使用して、「ユーザーに応答を促し、入力させるコード」があります。  

1. *LearningCenter.swift* ファイルを開きます。   
「ユーザーに応答を促し、入力された値を取得する」ための`responseToPrompt(_:)`メソッドを定義してください。  
そして、このメソッドが「オブジェクトの外部から呼び出されないようにする」ため、`private`をマークします。

    ```swift
    struct LearningCenter {
        ...
    
        private func responseToPrompt(_ prompt: String) -> String {
            print(prompt)
            return readLine()!
        }
    }
    ```

2. ユーザと対話するコードを、`responseToPrompt(_:)`メソッドの呼び出しに置き換えます。  
「ユーザー名を設定するコード」と「学習方法を選択するコード」です。

    ```swift
    struct LearningCenter {
        ...
        
        mutating func start() {
            print("Welcome to the Elements Illustrated Book!")
            print("There are 1 element")
            
            // ユーザー名の設定
            student = responseToPrompt("What is your name?")
            print("Hi, \(student!).")
            
            // ランループ
            var decision = ""
            while !(decision == "Y" || decision == "N") {
                decision = responseToPrompt("Select elements to learn, Randomly? (Y or N)")
                ...
            }
        }

        private func responseToPrompt(_ prompt: String) -> String {
            print(prompt)
            return readLine()!
        }
    }
    ```

3. 「導入パート」を抽出して、`introduction()`メソッドを定義します。  
このメソッドも「外部から呼び出されないようにする」ため、`private`をマークします。

    ```swift
    struct LearningCenter {
        ...
        
        mutating func start() {
            ...
        }

        private func introduction() {
            print("Welcome to the Elements Illustrated Book!")
            print("There are 1 element")
        }
    }
    ```

4. `start()`メソッドの「コードが抽出された部分」を、`introduction()`メソッドの呼び出しに置き換えてください。

    ```swift
    struct LearningCenter {
        ...
        func start() {
            introduction()
            ...
        }
    }
    ```

5. 次に、「ユーザー名を設定する」コードを抽出します。  
そして、プライベートな`assignStudent()`メソッドを定義してください。  
このメソッドにも`mutating`キーワードをマークします。

    ```swift
    struct LearningCenter {
        ...
        
        func start() {
            ...
        }

        private mutating func assignStudent() {
            student = responseToPrompt("What is your name?")
            print("Hi, \(student!).")
        }
    }
    ```

6. 最後に、`start()`メソッドに残っているコードを抽出します。
そして、プライベートな`determineElement()`メソッドを定義してください。

    ```swift
    struct LearningCenter {
        ...
        
        mutating func start() {...}

        private func introduction() {...}

        private mutating func assignStudent() {...}
        
        private func determineElement() {
            var decision = ""            
            while !(decision == "Y" || decision == "N") {
                decision = responseToPrompt("Select elements to learn, Randomly? (Y or N)")
                ...
            }
        }
    }
    ```

7. `start()`メソッドを更新して、`assignStudent()`メソッドと`determineElement()`メソッドを呼び出します。

    ```swift
    struct LearningCenter {
        ...

        mutating func start() {
            introduction()
            assignStudent()
            determineElement()
        }

        ...
    }
    ```

8. プログラムを実行して、機能が失われていないことを確認してください。

関連するコードや反復的なコードを「適切な名前のメソッド」に再編成したことによって、コードの表現力と可読性を改善できました。

## 全体のコード

**main.swift**

```swift
import Foundation

var myLearningCenter = LearningCenter()
myLearningCenter.start()
print("Bye!")
```

**LearningCenter.swift**

```swift
import Foundation

struct LearningCenter {
    var student: String?

    mutating func start() {
        introduction()
        assignStudent()
        determineElement()
    }

    // 導入パート
    private func introduction() {
        print("Welcome to the Elements Illustrated Book!")
        print("There are 1 element.")
    }

    // ユーザー名の設定
    private mutating func assignStudent() {
        student = responseToPrompt("What is your name?")
        print("Hi, \(student!).")
    }

    // 学習方法の選択とランループ
    private func determineElement() {
        var decision = ""            
        while !(decision == "Y" || decision == "N") {
            decision = responseToPrompt("Select elements to learn, Randomly? (Y or N)")
            switch decision {
            case "Y":
                print("OK! Learning element randomly.")
                // TODO: learn to random element.
            case "N":
                print("OK, Select atomic weight of element...")
                // TODO: let the user select atomic weight of element.
            default:
                print("Sorry, I didn't get that.")
            }
        }
    }

    private func responseToPrompt(_ prompt: String) -> String {
        print(prompt)
        return readLine()!
    }
}
```

# 次のステップ

続きは、「[Xcodeで始めるアプリ開発入門⑤]()」にて解説（予定）。
投稿を告知するので、[X(旧Twitter)アカウント](https://twitter.com/startyourcode)のフォローをお願いします。