【Swift】自動販売機の例外処理を実装してみた

この記事は、[【Swift】自動販売機の機能を実装してみた](https://qiita.com/Kai_82/items/5e9d7c011838a12cc511) の続きです。
そちらをご覧になっていない方は是非ご覧ください。

GitHubリポジトリは**[コチラ](https://github.com/onishi-app/vendingMachine)**になります。

では、続きの例外処理を実装していきたいと思います。


##仕様
実装するにあたり仕様を箇条書きで挙げていきたいと思います。
**・所持金を超える額を自動販売機に投入することはできない
・金額を超えている商品は購入できない
・在庫がない商品は買うことができない**

とりあえず上記の３つを追加してみようと思います。

##実装

では、一番最初の「所持金を超える額は投入できない」を実装していきます。

処理内容としては、
`所持金 > 投入金額`の場合は投入可能という処理にします。 

同期処理なのでOptional\<Wrapped>型のエラー処理か、
do-catch文を使ったエラー処理を実装します。

**今回は、所持金が足りているか足りていないかの二択なので前者のエラー処理で実装します。**

`sendMoneyAction()`メソッドを次の様に変更しました。
`-> String?`を追加しnilを許容する戻り値の型を定義しました。

メソッドが実行されたらまず初めに、
引数のvalue(投入金額)とmoneyプロパティ(所持金)の大小関係を確認しています。

**この時点でmoneyプロパティよりも大きい値が渡されていたら、
自動販売機にお金を入れることはできないはずです。**

なので、valueの方が大きかった場合は`return nil`をしています。


```swift:ViewController.swift

    func sendMoneyAction(value: Int) -> String? {
        if money < value {
            return nil
        }
        
        money -= value
        inputMoney += value
        moneyLabel.text = "所持金：\(money)円"
        inputMoneyLabel.text = "金額：\(inputMoney)円"
        
        return "\(value)円を入れました。"
    }
```

次に、呼び出し元の処理になります。
(`case 2:`以降は同じ処理なので省略しています。)

`guard let result = sendMoneyAction(value: 10) else { }`で
返ってきた値がnilかどうかをチェックしています。

nilの場合は{ }内を実行し、そうでない場合は`print(result)`を実行します。

```swift:ViewController.swift

    @IBAction func sendMoney(_ sender: Any) {
        guard let button = sender as? UIButton else {
            return
        }
        
        switch button.tag {
        case 1:
            guard let result = sendMoneyAction(value: 10) else {
                print("所持金が不足しています。")
                return
            }
            print(result)
        default: return
        }
    }
```

実際の挙動は画像の様になります。

![スクリーンショット 2020-12-30 18.15.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/450b7daa-d6b0-7fd6-7cf8-2d0a02570b45.png)

では、一度コミットし次の例外処理機能を実装したいと思います！

次に実装する機能は、**「金額を超えている商品は購入できない」**です。
現在入れられている金額よりも値段の高い商品の購入ボタンを押した際に例外処理を行います。

処理としては簡単で、inputMoneyプロパティ(現在入っている金額)から
購入額を引いた値が０以上であれば購入しても問題ありません。

なので、`if inputMoney - 100 < 0 { }`を追記して終了です。

```swift:ViewController.swift

    // 購入処理
    func buyDrink(product: Drink) {
        switch product {
        case .irohasu:
            if inputMoney - 100 < 0 {
                print("お金が足りません。")
                return
            }
            inputMoney -= 100
            inputMoneyLabel.text = "金額：\(inputMoney)円"
            print("いろはすを購入しました。")
        }
    }
```

実際の挙動としては画像の様になります。

２００円入れて午後ティーを購入し残金が５０円。
この状態でいろはすを買おうとすると**「お金が足りません」**と出力されます。

![スクリーンショット 2020-12-30 18.33.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/eb8035e7-193b-edb3-7737-9b31d3128062.png)

最後に、**「在庫がない商品は買うことができない」**を実装しようと思います。

新しく各商品のストックを保持するプロパティを定義しました。
(列挙型ではなく構造体で定義した方が良かったなーとか思ったりしたり・・・)

また、在庫の管理を行う`inventoryControl()`メソッドも定義しました。

このメソッドでは、在庫があるかチェックし存在しない場合はfalseを返し
存在した場合は在庫を一つ減らしtrueを返します。

そして購入処理を行う`buyDrink()`メソッドでは、
まず初めに`inventoryControl()`メソッドで在庫があるか確認します。

その後、料金が足りるかを確認し、
どちらの条件にも掛からなかったら処理を最後まで行います。

エラーが「在庫がありません。」と「お金が足りません。」の２種類があります。
**なので、詳細なエラーを表示できるdo-catch文を使った方が良かった気がします。**

取り合えず機能の実装はできましたので、ミッションクリアということで・・・。
ですが、気になってしまうので次の記事でちゃんとしたものを載せれたらなと。

```swift:ViewController.swift

    var irohasuStock = 3
    var ryokuchaStock = 2
    var gogothiStock = 1

    // 購入処理
    func buyDrink(product: Drink) {
        switch product {
        case .irohasu:
            if !inventoryControl(product: product) {
                print("在庫がありません。")
                return
            }
            if inputMoney - 100 < 0 {
                print("お金が足りません。")
                return
            }
            inputMoney -= 100
            inputMoneyLabel.text = "金額：\(inputMoney)円"
            print("いろはすを購入しました。")
        }
    }

    // 在庫管理
    func inventoryControl(product: Drink) -> Bool {
        switch product {
        case .irohasu:
            if irohasuStock == 0 {
                return false
            }
            irohasuStock -= 1
            return true
        }
    }
```

実際の挙動としては次の様になります。

５００円を入れ、緑茶を在庫分の2個購入します。

その後もう一度緑茶を購入しようとすると、
在庫がありませんと表示されて購入ができません。


![スクリーンショット 2020-12-30 19.30.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/37377d89-79db-076a-f404-1349ec8d5ad7.png)


以上で、自動販売機のサンプル実装を一応完了にしたいと思います。

**次の記事で先ほど行ったエラーの箇所を修正する予定です。**
是非そちらもご覧いただければと思います。

次の記事はこちら
->[【Swift】自動販売機の例外処理の修正(番外編)](https://qiita.com/Kai_82/items/5ea22d4915ba11d340a1)

最後までご覧いただきありがとうございました。