【Swift】自動販売機の機能を実装してみた

私ごとなのですが、メンターのヤマタクさんと契約しておりまして
その中で応用学習としてビジネスロジックを組んでみようという課題がありました。

その中の一つの題材として、
自動販売機のロジックがあったのでそちらを作成してみようと思います！

やる気のある方は、プログラミングスクールよりメンターと契約した方が
安いですし効率もいい気がするので是非お勧めします！

**「Swift メンター」**で検索！

#ロジック作成の前に

以前までの私でしたら何も考えずにコードを組んでいましたが、
順にやることを教えていただいたのでその通りに定義していきたいと思います！

また、先日GitHubについても学習したので
そちらの練習もかねて進めていきたいと思います。

GitHubには次の順序で連携？しました。備忘録として残しておきます。

1.GitHubでリポジトリを作成
2.XCodeでプロジェクトを作成
※作成する時に`Create local git repository for this project`にチェック
3.ターミナルで`git remote add origin リポジトリのURL(https://~)`実行
4.XCodeの`Source Controll`から`Commit`や`Push`を行う

この順序でできた気がします。

もしできなかったら 3. の後に一度ターミナルから、
`git push -u origin main`を実行すれば登録かなんかされてできる様になるかも？

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


##仕様

まずはこの自動販売機のロジックの仕様を考えます。
・初期の所持金を１０００円する
・投入可能金額は、１０円・５０円・１００円・５００円・１０００円とする
・対象の商品は、「いろはす」・「緑茶」・「午後の紅茶」の３種類とする
・商品の詳細は下記のとおり

|  | いろはす | 緑茶 | 午後の紅茶 |
|:-:|:-:|:-:|:-:|
|  金額 | １００円  | １２０円  | １５０円  |
|  在庫 |  ３個 |  ２個 |  １個 |

・購入は現金のみとする
・商品購入後は、金額表記を減らす。
・おつりボタンを押すと返金される

とりあえずこんな感じの仕様でいきたいと思います！

ちなみに、PlaygroundとStoryboardのどちらでも可なので、
今回はStoryboardで実装したいと思います。

##実装していく

まずはUIを適当でいいので実装してみます。
今回は本物の自販機のかなりの劣化版となります・・・(笑)

![スクリーンショット 2020-12-30 13.36.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/b20e8083-bccd-7775-fd31-222d1036f09c.png)

とりあえずこんな感じでどうでしょう！

「購入」・「１０円〜１０００円」・「おつり」は、UIButton( )で実装しています。
それ以外は、UILabel( )での実装になります。

次に、Storyboardとコードを連携していきます。

**各ボタンを別にしてコードにすると量が多くなるなるので、
関連性のあるボタンはまとめてみました。**

![スクリーンショット 2020-12-30 13.46.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/1505ba15-e4f7-9b8a-d3ee-a8bfd5c43bfe.png)

では、一度GitHubにコミットして
バックアップを作成してから実際のコードを記述していきたいと思います。

`git reset`コマンドがあった気がするので、もし戻したくなったらこれで戻せるはずです。
(こういう使い方が推奨されているのかは分かりませんが・・・。)

では、ここからは処理を書いていきたいと思います。

実装の順としては、実際に購入する際の順で実装していきたいと思います。

最初にお金を入れると思うので、
各金額が記載されているボタンを押したら所持金が減り、
投入金額が増える様なコードを書きたいと思います。

今回は、各金額ボタンが一つの`sendMoney()`メソッドにまとめられています。
なので、**どのボタンが押されたかチェックしてから適した処理を行います。**

まず、Storyboardの対象のボタンを選択し、tagに値を設定します。
今回は１０円から順にtag1、tag2、tag3、tag4、tag5と振っていきました。

![スクリーンショット 2020-12-30 14.08.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/5ed346b4-bc72-01b8-8440-dcab3f72123d.png)

その後、switch文でどのタグが選択されたかで条件分岐しています。

引数senderに値が入ってくる訳ですが、
**Any型なのでguard-letの箇所でUIButton型にキャストしています。**

```swift:ViewController.swift
    // 投入額選択ボタン
    @IBAction func sendMoney(_ sender: Any) {
        guard let button = sender as? UIButton else {
            return
        }
        
        switch button.tag {
        case 1:
            print("1")
        case 2:
            print("2")
        case 3:
            print("3")
        case 4:
            print("4")
        case 5:
            print("5")
        default: return
        }
    }
```

この時点でどのボタンが押されたかまでは識別できる様になったので、
金額の反映を行うメソッドを定義していきたいと思います。

これから定義するメソッドが実装する機能は、
**所持金から投入金額分を引く機能と、金額の値を増やす機能です。**

新たにプロパティを二つ定義し、そこの値を更新するメソッドを定義しました。

`sendMoneyAction（）`メソッドでは、押されたボタンの値を引数として受け取り、
所持金からその値を引くのと金額に投入額を追加するのと、各ラベルの更新を行っています。

```swift:ViewController.swift

    var money = 1000
    var inputMoney = 0

    // 投入額選択ボタン
    @IBAction func sendMoney(_ sender: Any) {
        guard let button = sender as? UIButton else {
            return
        }
        
        switch button.tag {
        case 1:
            sendMoneyAction(value: 10)
        case 2:
            sendMoneyAction(value: 50)
        case 3:
            sendMoneyAction(value: 100)
        case 4:
            sendMoneyAction(value: 500)
        case 5:
            sendMoneyAction(value: 1000)
        default: return
        }
    }

    func sendMoneyAction(value: Int) {
        money -= value
        inputMoney += value
        
        moneyLabel.text = "所持金：\(money)円"
        inputMoneyLabel.text = "金額：\(inputMoney)円"
    }
```

実際に５０円のボタンと１００円のボタンを押すと
所持金が８５０円になり、金額が１５０円になっているのが分かります。

![スクリーンショット 2020-12-30 14.31.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/ad94e904-023d-7685-dbbd-d2aadf0dce71.png)


機能を一つ実装したので一度GitHubに**コミット**し、
次は、お金を入れたら飲み物を買うと思うので、購入ボタンの実装を行います。

こちらも、先ほどと同じくタグで識別します。
似たコードになるので説明は省略します！

**左の購入ボタンを押した時は金額から１００円を引く。**
というコードにしようと思ったのですが、実際は商品の並び替えを行うと思うので
左の購入ボタンがずっと１００円とは限りません。

なので、各購入ボタンを押した時に、
商品名を指定してその商品名に適した処理を行うことにしました。

**そうすることにより金額の変動や商品位置の変更に対してのメンテナンスが
ほんの少しですが楽になる？と思います。**

そうするために、まず列挙型を定義し各商品のケースを定義しました。

購入ボタンを押すと購入処理を行う`buyDrink()`メソッドが実行されます。

引数には列挙型Drinkのケースを指定しています。
switch文でパターンマッチを行いそのケースに適した処理を行っています。

```swift:ViewController.swift

enum Drink {
    case irohasu
    case ryokucha
    case gogothi
}

    // 購入ボタン
    @IBAction func buyButton(_ sender: Any) {
        guard let button = sender as? UIButton else {
            return
        }
        
        switch button.tag {
        case 1:
            buyDrink(product: .irohasu)
        case 2:
            buyDrink(product: .ryokucha)
        case 3:
            buyDrink(product: .gogothi)
        default:
            return
        }
    }

    // 購入処理
    func buyDrink(product: Drink) {
        switch product {
        case .irohasu:
            inputMoney -= 100
            inputMoneyLabel.text = "金額：\(inputMoney)円"
            print("いろはすを購入しました。")
        case .ryokucha:
            inputMoney -= 120
            inputMoneyLabel.text = "金額：\(inputMoney)円"
            print("緑茶を購入しました。")
        case .gogothi:
            inputMoney -= 150
            inputMoneyLabel.text = "金額：\(inputMoney)円"
            print("午後ティーを購入しました。")
        }
    }
```

実際に５００円投入後に、午後ティーと緑茶を購入した場合の結果が下記になります。
金額が減り、ログに購入結果が出力されているのが分かります。

![スクリーンショット 2020-12-30 15.04.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/b54ac61e-d498-a84a-a19c-65b0827ac65a.png)


結構それっぽくなってきましたね！
最後に、**投入金額が残った場合におつりボタンを押すと返金される機能**を実装します。

と思いましたが、その前にGitHubにコミットします。
開発がどんどん進むとコミットを忘れてしまいそうになります(笑)

おつりボタンの機能はものすごく簡単で、金額の残りを所持金にプラスするだけです。
それだけだと少し寂しいので、ついでにログに出力する処理も追加しておきます。

```swift:ViewController.swift

    // おつりボタン
    @IBAction func changeButton(_ sender: Any) {
        money += inputMoney
        print("\(inputMoney)円のおつりをもらいました")
        inputMoney = 0
        moneyLabel.text = "所持金：\(money)円"
        inputMoneyLabel.text = "金額：\(inputMoney)円"
    }
```

たったこれだけになります。

５００円投入後、１５０円の午後ティーを購入し、
３５０円のおつりを貰った際の挙動は画像のようになります。

![スクリーンショット 2020-12-30 15.17.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/797483/cf1a1ce7-7d33-ce01-06d8-48a92089f603.png)

所持金が８５０円で、ログにもしっかり出力されているので問題ないと思います！

これで最低限の自動販売機ロジックの完成になります。

最低限といったのには意味があり、
**このコードには例外の処理を一切記載していません。**

例えば、１０００円を何回もタップし所持金をマイナスにすることもできますし、
金額が０円のまま商品を購入することもできます。
他にも仕様では在庫が決まっているのに何本も買えてしまったりと穴だらけのコードです。

なので、次回の記事でここらへんもしっかりと実装していきたいと思います。

次回の記事はこちらになります。
-> [【Swift】自動販売機の例外処理を実装してみた](https://qiita.com/Kai_82/items/043f575af904bb8070a4)

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