【C#】ロト購入でインターフェースを体感してみた

# １．はじめに
色々あったというか転職先で必要になったため、C#の勉強を開始した。
時々見かける、メソッドのみが羅列されている謎のクラス、インターフェース。
今回の目的は、インターフェースとは何かを体感することにしている。

# ２．インターフェースって何？

インターフェースについてC#入門の本で調べてみた。
+ 親クラスからのメソッドは宣言のみで、フィールドを生成することはできない。
+ 抽象クラスと同じように、インスタンスを生成できない
+ 抽象クラスの概念をさらに推し進めたもの
+ 呼び出すときは、親クラスをインターフェースでキャストして使うことができる

箇条書きの3つ目が気になる。
抽象クラスを誇張したもの？ザコシショウか？⇒結論、そういう意味ではなかった。

下記のような感じで作るらしい。
`interface インターフェース名`
`{`
`親クラスのメソッド1(引数)`
`親クラスのメソッド2(引数)`
`}`

わかんない。イマイチわからないのだ。
例を見てもよくわからない。
何で、わざわざ親クラスの一部のメソッドだけ宣言しているのか。

__親クラスの一部の機能だけしか、使わない場合ってあるのか？__
色々かんがえたところ、

__日頃からお世話になっている ~~(当たらないので課金を続けている)~~ Lotoの購入で使えそうかも__
と気が付いた。

# ３．ロトの購入について

## 3-1.ロトの購入処理手順

ここでロトを購入するときの処理内容を考える。
__１．購入するとき、ロト6やロト7という銘柄を決める。__
__２．購入の仕方も「自分で番号を決めて購入」か「クイックピック」がある。__
(厳密にいえば、半分ランダム、半分自己選択という混合型もあるが(クラス名：キューピーハーフ)、
今回は、全番号自己選択 or クイックピックで考える)
`※キューピーハーフクラスは、某会社の商品名ともかぶるし今回は作成しない。`
全番号クイックピックを指定した時、自分で番号を選択することができない。
番号選択の時は、全番号クイックピックを使うことができない。

(a)クイックピック
ロト6の場合は、1～43までの数字6つがランダムに選ばれる方式
ロト7の場合は1～37までの数字7つがランダムに選ばれる方式。

(b)自分で数字選択
自分で数字を決めて入力する方式。
数字の入力回数は、ロト6では購入本数*6、ロト7では購入本数*7とする。

__３．購入本数分の金額を払う。__
（ロト6は一本200円、ロト7は一本300円）

## 3-2.ロト関連クラスについて

__（１）ロトクラス__

`ロトクラス:自己番号選択インターフェース,クイックピックインターフェース`
`{`
`  銘柄(ロト6かロト7か)`
`  表示名`
`  購入方法(クリックピックか自己選択か)`
`  最大数値`
`  購入本数`
`  価格`
`  自己選択関連メソッド`
`  クイックピック関連メソッド`
`  購入金額計算メソッド`
`}`

__（２）自己番号選択インターフェース__

`自己番号選択インターフェース`
`{`
`  自己選択関連メソッド`
`  購入金額計算メソッド`
`}`

__（３）クイックピックインターフェース__

`クイックピックインターフェース`
`{`
`  クイックピック関連メソッド`
`  購入金額計算メソッド`
`}`

実行環境:Windows 10 home
visual studio 2022

# ４.実行画面

## 4-1.ロト6をクイックピックで購入

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689780/7a9fc1c3-0b62-3443-7fa7-40ae260411d0.png)



## 4-2.ロト7を数字を選択して購入
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689780/0d129e95-f7b0-5d0d-98fc-1bcf0a4d098b.png)


# ５．ソース

## ５－１．購入の選択

`(1)loto6かloto7か`
loto6を購入する場合は0を、loto7を購入したい場合は1を入力

`(2)購入方式`
クイックピック:0
自己選択:1
自己選択式の場合、以下の制限が必要になる。
1)ロト6では1～43までの数字
ロト7では1～37までの数字までしか購入できない。
2)1組の購入数字内に、同じ数字は選択できない。

`(3)購入本数`
自己選択なら、3本まで購入可能

```c#:Program0.cs
using ConsoleApp_loto;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApp_loto
{
 class Program0
 {

  //入力文字列のチェック
  public static Boolean Check_input(string s0, int nmax = 0)
  {
   int type0;
   //入力文字列が数字以外の時
   if (int.TryParse(s0, out type0) == false)
   {
    return true;
   }
   type0 = int.Parse(s0);
   //自己選択型での入力数字
   if (nmax > 2)
   {
    return (type0<1 || type0>nmax) ? true : false;
   }
   //自己選択型の購入本数
   if (nmax == 2)
   {
    return (type0>3 || type0<0) ? true : false;
   }
   //購入タイプ選択
   if (nmax==0)
   {
    return (type0>1 || type0<0) ? true : false;
   }
   //購入タイプQPの整数での本数入力
   return false;
  }

  public static void Main(string[] args)
  {
   int i, j;
   int rettype = 0;
   string retname = "";
   int nmax = 0;
   string s, s1, s2, s3;
   int type0 = 0;
   int type2 = 0;
   int n = 0;
   int price0 = 0;
   string tmpstr0 = "";

   Console.WriteLine("クイックピックの場合は0を、自分で選択する場合は１を入力してください。");
   s1 = Console.ReadLine();

   Console.WriteLine("ロト6は0,ロト7は1を入力してください");
   s2 = Console.ReadLine();

   if (Check_input(s1) == true || Check_input(s2) == true)
   {
    Console.WriteLine("不正な値です");
    return;
   }
   Console.WriteLine("購入本数(自分で選択の場合は３本まで)");
   s3 = Console.ReadLine();

   type0 = int.Parse(s1);
   type2 = int.Parse(s2);
   //ロトの銘柄
   rettype = (type2 == 0) ? 6 : 7;
   //一本当たりの金額
   price0 = (type2 == 0) ? 200 : 300;
   //くじ番号の最大値
   nmax = (type2 == 0) ? 43 : 37;

   //クイックピック
   if (type0 == 0)
   {
    if (Check_input(s3,1) == true)
    {
     Console.WriteLine("不正な値です");
     return;
    }
    n = int.Parse(s3); 

    retname=(type2==0)? "ロト6(QP)": "ロト7(QP)";
    Loto kuji = new Loto();
    kuji.Name = retname;
    kuji.Type1 = rettype;
    kuji.Num1 = n;
    kuji.Price = price0;
    kuji.Loto_max = nmax;

    Iquick_pick ltqp = (Iquick_pick)kuji;
    ltqp.Buyqp();
    ltqp.PrintTotalAmount();
   }
   //自分で選択した場合
   if (type0 == 1)
   {
    if (Check_input(s3, 2) == true)
    {
     Console.WriteLine("不正な値です");
     return;
    }
    n = int.Parse(s3);
    string[] tmpstr1 = new string[n];
    int n2;

    retname = (type2 == 0) ? "ロト6(自己選択)" : "ロト7(自己選択)";
    //n本分の数字を入力
    for (i = 0; i < n; i++)
    {
     List<int> retint_list = new List<int>();
     for (j = 0; j < rettype; j++)
     {
      Console.Write((i + 1) + "回目:" + (j + 1) + "番目(1～" + nmax + ")までを指定 ");
      s = Console.ReadLine();
      if (Check_input(s, nmax) == true)
      {
       Console.WriteLine("不正な値です");
       return;
      }
      n2 = int.Parse(s);
      if (retint_list.Contains(n2) == true)
      {
       Console.WriteLine("同じ値が入力されています");
       return;
      }
      retint_list.Add(n2);
     }
     Console.WriteLine();
     tmpstr1[i] = string.Join(",", retint_list);
     retint_list.Clear();
    }
    tmpstr0 = string.Join(" ", tmpstr1);
    Loto kuji = new Loto();
    kuji.Name = retname;
    kuji.Type1 = rettype;
    kuji.Num1 = n;
    kuji.Mynumbers = tmpstr0;
    kuji.Price = price0;
    kuji.Loto_max = nmax;
    Inumber_select mylt = (Inumber_select)kuji;
    mylt.Buymynum();
    mylt.PrintTotalAmount();
   }
  }
 }
}

```
ロトクラス
```c#:loto.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Numerics;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp_loto
{
 internal class Loto : Inumber_select, Iquick_pick
 {
  private string _name;
  private int _num1;
  private int _type1;
  private string _mynumbers;
  private int _loto_max;
  private int _price;

  public string Name
  {
   set { _name = value; }
   get { return _name; }
  }
  //ロト6やロト7など銘柄を表す数字
  public int Type1
  {
   set { _type1 = value; }
   get { return _type1; }
  }
  //購入本数
  public int Num1
  {
   set { _num1 = value; }
   get { return _num1; }
  }
  //自分で選択した数字を連結した文字列
  public string Mynumbers
  {
   set { _mynumbers = value; }
   get { return _mynumbers; }
  }

  //選択する最大値
  public int Loto_max
  {
   set { _loto_max = value; }
   get { return _loto_max; }
  }

  //一本当たりの価格
  public int Price
  {
   set { _price = value; }
   get { return _price; }
  }

  //クイックピックで購入
  public void Buyqp()
  {
   Random rand = new Random();
   int nn;
   //可変配列
   List<string> ret_list = new List<string>();

   for (int i = 0; i < _num1; i++)
   {
    List<int> lotoqp_list = new List<int>();
    for (int j = 1; j <= _loto_max; j++)
    {
     lotoqp_list.Add(j);
    }
    int[] tmparr = new int[_type1];

    for (int j = 0; j < _type1; j++)
    {
     nn = rand.Next(_loto_max - j);
     tmparr[j] = lotoqp_list[nn];
     lotoqp_list.RemoveAt(nn);
    }
    ret_list.Add(string.Join(",", tmparr));
    lotoqp_list.Clear();
   }

   for (int i = 0; i < _num1; i++)
    Console.WriteLine(_name + ": " + (i + 1) + "回目:" + ret_list[i]);
  }


  //合計金額表示
  public void PrintTotalAmount()
  {

   int total = 0;
   total = _price * _num1;
   Console.WriteLine("購入金額は" + total + "です");
  }

  public void Buymynum()
  {
   string[] tmpstr3 = new string[_num1];
   tmpstr3 = _mynumbers.Split(" ");
   int i, j;

   for (i = 0; i < _num1; i++)
    Console.WriteLine(_name + ":" + (i + 1) + "回目:" + tmpstr3[i]);
  }
 }
}


```
自己選択式
+ 購入数分の選択した数字を表示
+ 購入金額を表示

```c#:Inumber_select.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp_loto
{
 internal interface Inumber_select
 {
  void Buymynum();
  void PrintTotalAmount();
 }
}

```
クイックピッククラス
+ クイックピック方式の数字を表示
+ 購入金額を表示

```c#:Iquick_pick.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp_loto
{
 internal interface Iquick_pick
 {
  void Buyqp();
  void PrintTotalAmount();
 }
}

```
# 6.気になったこと
インターフェースについては体感できた。
それよりも地味に大変なことがあった。
入力文字列のチェックだ。
if,elseという判定文だ。
__簡単なif文で4行書くことになる__　⇒これ！！！
{カッコだけで1行使うのだ。
しかも、対になるカッコも必要なのだ。)
行数削減のため、3項演算子を駆使したのだ。

# 7.インターフェースのメリット
改めてインターフェースについて考えてみた。
__2つの共通点が多いが一部の処理が違うクラスを作りたいとする。__
そんな時いちいちコピペして、クラスを作るのは効率が悪い。
さらに何かあったときに、  __`[例えば2つのクラスに共通する処理を変更するときは親クラスだけ変更]`__ できた方が良い。
全体のコード量も削減できる。

メソッドのみが羅列されている謎のクラスのメリットだったのだ。