【実践】アプリの開発

2022年7月6日

今回は、家庭向けアプリの制作を通して学習を進めてみましょう

実現したいこと

アプリ概要

ゴミの回収される曜日をチェックできるツール、チェッカー
ゴミの名前を入力すると、そのゴミの回収される曜日が表示される

必要な機能

特定の一部でもいいので、上記機能が働けばいいです
全てを網羅する必要はありません。ピックアップしていただいていいです

プログラム開発言語

C#でコンソールアプリとして開発してください
.Net6のトップレベルステートメントで作成
文法やC#の実装する機能の選択は、お任せします

サンプルコード

サンプルのために次をピックアップします

  • 可燃ゴミのおむつ -> 月・木曜日
  • 資源プラスチックのボトル類 -> 木曜日
  • ビンのスプレー缶 ->金曜日

If文を採用

string gabege = Console.ReadLine();

if (gabege == "おむつ")
{
    Console.WriteLine("月・木曜日");
}
else if (gabege == "ボトル類")
{
    Console.WriteLine("木曜日");
}
else if (gabege == "スプレー缶")
{
    Console.WriteLine("金曜日");
}

表示

おむつ // <-入力
月・木曜日

Dictionaryクラスを採用

Dictionary<string, string> gabages = new()
{
    ["おむつ"] = "月・木曜日",
    ["ボトル類"] = "木曜日",
    ["スプレー缶"] = "金曜日",
};

string gabege = Console.ReadLine();

Console.WriteLine(gabages[gabege]);

表示

おむつ // <-入力
月・木曜日

自作Classを採用

クラスを使ったコードを作る場合、次のようにまず、名詞・名詞句と動詞・動詞句を抽出するといいでしょう

名詞・名詞句

クラス名やフィールド、プロパティなどのデータ部分を作成する上で材料とします
課題や与えられた資料から、特別な先入観なく書き出すといいでしょう

  • ゴミのベースクラス (GarbageBase)
  • ゴミ (Garbage)
  • ゴミ回収曜日 (GarbageCollectionDay)
  • ゴミの分類 (GarbageClass)
    • 可燃ゴミ (BurnableGarbage)
      • おむつ (Diaper)
    • 資源プラスチック (RecyclablePlastic)
      • ボトル類 (Bottles)
    • ビン (Bin)
      • スプレー缶 (SpryCan)
  • ゴミの分類ごとのリスト (Garbages)

動詞・動詞句

メソッドを作成するために同じように、書き出してみましょう

  • 表示する (Show)
  • 結果を取得する (Get
  • 検索する (Search)

クラスを検討する

上記、書き出した単語からクラスになりそうなものを作っていきます
この時点では、ブロック内は空っぽで構いません
継承関係を考えながら進めましょう
大事なことは、is-aの関係が崩れないようにすることです
例えば、「可燃ゴミ」は、「ゴミの分類」である。つまり、「可燃ゴミ is a ゴミの分類」のように話し言葉として違和感がないか考えながら進めましょう。

基底クラス

GarbageBase

class GarbageBase
{
}

GarbageClass

class GarbageClass
{
}
派生クラス

Gargbage

class Garbage : GarbageBase
{
}

BurnableGargbage

class BurnableGarbage : GarbageClass
{
}

ResyclablePlastic

class RecyclablePlastic : GarbageClass
{
}

Bin

class Bin : GarbageClass
{
}
各クラスのメンバー

分類された「もの」をListにします

クラスにメンバーを追加する

基底クラス

GarbageBase

このクラスでは、ゴミ全般に言えることを記述します
名前とそのゴミに対するコメントをメンバーとしています

class GarbageBase
{
    protected GarbageBase(string name, string comment = "")
    {
        Name = name;
        Comment = comment;
    }

    public string Name { get; set; }
    public string Comment { get; set; }
}

GarbageClass

上記ゴミの特徴を持ちながら、ゴミの分類に関するメンバーを追加するための基底クラスを定義しています
抽象クラスとすることで、このクラスからはインスタンスを生成しないこととしています
派生クラスには、ゴミの収集日とその分類に属するゴミのリストをメンバーとして持つことを要求します
このクラスをインスタンス生成ようとしない設計の理由として、各ゴミの分類に特有の処理が必要となる可能性があり、その余地を各クラス側に残したいためです

ここに、Showメソッドを記述します
ゴミの分類にその分類ごとのゴミの一覧にヒットしたら表示させるためのメソッドを記述するのがしっくりくるんじゃないでしょうか
戻り値として、ヒットしたらtrueを、しなかったらfalseを返しています

abstract class GarbageClass : GarbageBase
{
    protected GarbageClass(string name, string comment = "") : base(name, comment)
    {
        Garbages = new();
    }

    public abstract string GarbageCollectionDay { get; set; }
    public abstract List<Garbage> Garbages { get; set; }

    public bool Show(string sel)
    {
        var check = Garbages.Any(x => x.Name == sel);

        if (check)
        {
            Console.WriteLine($"{sel}のゴミ収集日は、{GarbageCollectionDay}です");
            return true;
        }
        return false;
    }
}
派生クラス

Garbage

ゴミそのもののインスタンスを生成するためのクラスです
ベースクラスを基底クラスとしています

class Garbage : GarbageBase
{
    public Garbage(string name, string comment = "") : base(name, comment)
    {
    }
}

BurnableGarbage

可燃ゴミの分類パターンで、インスタンスを作成するためのクラスです
GarbageClassを基底クラスとしています
ゴミ収集日と該当するゴミのリストをメンバーとして持っています

class BurnableGarbage : GarbageClass
{
    public BurnableGarbage(string name, string garbageCollectionDay, string comment = "") : base(name, comment)
    {
        GarbageCollectionDay = garbageCollectionDay;
    }

    public override string GarbageCollectionDay { get; set; }
    public override List<Garbage> Garbages { get; set; } = new List<Garbage>();
}

ResyclablePlastic

資源プラスチックの分類パターンです

class RecyclablePlastic : GarbageClass
{
    public RecyclablePlastic(string name, string garbageCollectionDay, string comment = "") : base(name, comment)
    {
        GarbageCollectionDay = garbageCollectionDay;
    }

    public override string GarbageCollectionDay { get; set; }
    public override List<Garbage> Garbages { get; set; } = new List<Garbage>();
}

Bin

ビンの分類パターンです

class Bin : GarbageClass
{
    public Bin(string name, string garbageCollectionDay, string comment = "") : base(name, comment)
    {
        GarbageCollectionDay = garbageCollectionDay;
    }

    public override string GarbageCollectionDay { get; set; }
    public override List<Garbage> Garbages { get; set; } = new List<Garbage>();
}

Mainメソッド

実行コードを記述しています
順次実行するように記述していますが、必要に応じてメソッド化します
データの追記は、コンソール入力や、ファイルからの入力等、柔軟に対応できるようにしています
また、処理に関しては、抽出をListメソッドを活用しています
LINQを駆使することにより多くの処理を短い行で実現することも可能になっています


// 可燃ゴミのインスタンスを作成
BurnableGarbage burnableGarbage = new BurnableGarbage("可燃ゴミ", "月・木曜日");

// 資源プラスチックのインスタンスを作成
RecyclablePlastic recyclablePlastics = new RecyclablePlastic("資源プラスチック", "木曜日");

// ビンのインスタンスを作成
Bin bin = new Bin("ビン", "金曜日");


// おむつは、可燃ゴミ
Garbage diaper = new Garbage("おむつ");
// 可燃ゴミのゴミリストにおむつを追加
burnableGarbage.Garbages.Add(diaper);

// ボトル類は、資源プラスチック
Garbage buttles = new Garbage("ボトル");
// 資源プラスチックのゴミリストにボトル類を追加
recyclablePlastics.Garbages.Add(buttles);

// スプレー缶は、ビン
Garbage sprayCan = new Garbage("スプレー缶");
// 資源プラスチックのゴミリストにボトル類を追加
bin.Garbages.Add(sprayCan);

List<GarbageClass> garbageClasses = new();
garbageClasses.Add(burnableGarbage);
garbageClasses.Add(recyclablePlastics);
garbageClasses.Add(bin);

string sel = Console.ReadLine();

var containCheck = garbageClasses.Any(n => n.Show(sel));

if (!containCheck)
{
    Console.WriteLine($"{sel}のゴミ収集日は、未登録です");
}

/*
foreach (var garbageClass in garbageClasses)
{
    if (garbageClass.Show(sel))
    {
        return;
    }
}

Console.WriteLine($"{sel}のゴミ収集日は、未登録です");
*/
/*
foreach (var garbageData in garbageClasses)
{
    var check = garbageData.Garbages.Any(x => x.Name == sel);

    if (check)
    {
        Console.WriteLine($"{sel}のゴミ収集日は、{garbageData.GarbageCollectionDay}です");
        return;
    }
}

Console.WriteLine($"{sel}のゴミ収集日は、未登録です");
*/

表示

おむつ // <-入力
月・木曜日

クラス図

おまけ

最終的なコード

曜日の列挙型

曜日は、7曜日固定で、限定なので、列挙型を採用

複数の曜日を保存できるように2進数で管理しています
どの曜日が含まれているかを取得するメソッドも同時に作成

enum DayOfWeekJapan
{
    日曜日 = 0b0000001,
    月曜日 = 0b0000010,
    火曜日 = 0b0000100,
    水曜日 = 0b0001000,
    木曜日 = 0b0010000,
    金曜日 = 0b0100000,
    土曜日 = 0b1000000,
}

class Convert
{
    public static string? GetAllDayOfWeek(DayOfWeekJapan? dayOfWeeks)
    {
        if (dayOfWeeks == null) return null;

        // 全てのパターンと比較する処理
        string weeks = "";

        if ((DayOfWeekJapan.日曜日 & dayOfWeeks) != 0)
        {
            weeks += DayOfWeekJapan.日曜日 + ",";
        }
        if ((DayOfWeekJapan.月曜日 & dayOfWeeks) != 0)
        {
            weeks += DayOfWeekJapan.月曜日 + ",";
        }
        if ((DayOfWeekJapan.火曜日 & dayOfWeeks) != 0)
        {
            weeks += DayOfWeekJapan.火曜日 + ",";
        }
        if ((DayOfWeekJapan.水曜日 & dayOfWeeks) != 0)
        {
            weeks += DayOfWeekJapan.水曜日 + ",";
        }
        if ((DayOfWeekJapan.木曜日 & dayOfWeeks) != 0)
        {
            weeks += DayOfWeekJapan.木曜日 + ",";
        }
        if ((DayOfWeekJapan.金曜日 & dayOfWeeks) != 0)
        {
            weeks += DayOfWeekJapan.金曜日 + ",";
        }
        if ((DayOfWeekJapan.土曜日 & dayOfWeeks) != 0)
        {
            weeks += DayOfWeekJapan.土曜日;
        }
        return weeks;

        /* リストに追加していく処理
         List<DayOfWeekJapan> weeks = new();

        if ((DayOfWeekJapan.日曜日 & dayOfWeeks) != 0)
        {
            weeks.Add(DayOfWeekJapan.日曜日);
        }
        if ((DayOfWeekJapan.月曜日 & dayOfWeeks) != 0)
        {
            weeks.Add(DayOfWeekJapan.月曜日);
        }
        if ((DayOfWeekJapan.火曜日 & dayOfWeeks) != 0)
        {
            weeks.Add(DayOfWeekJapan.火曜日);
        }
        if ((DayOfWeekJapan.水曜日 & dayOfWeeks) != 0)
        {
            weeks.Add(DayOfWeekJapan.水曜日);
        }
        if ((DayOfWeekJapan.木曜日 & dayOfWeeks) != 0)
        {
            weeks.Add(DayOfWeekJapan.木曜日);
        }
        if ((DayOfWeekJapan.金曜日 & dayOfWeeks) != 0)
        {
            weeks.Add(DayOfWeekJapan.金曜日);
        }
        if ((DayOfWeekJapan.土曜日 & dayOfWeeks) != 0)
        {
            weeks.Add(DayOfWeekJapan.土曜日);
        }

        return string.Join(",", weeks);

        */

        /* ビットが立っているところを順に処理
         
        List<DayOfWeekJapan> weeks = new();

        for (int i = (int)DayOfWeekJapan.土曜日; (int)DayOfWeekJapan.日曜日 <= i; i /= 2)
        {
            if ((int)dayOfWeeks / i == 1)
            {
                weeks.Add((DayOfWeekJapan)Enum.ToObject(typeof(DayOfWeekJapan), i));
                dayOfWeeks -= i;
            }
        }

        weeks.Reverse();

        return string.Join(",", weeks);
        */

        /* ビットシフトを使って処理
        List<DayOfWeekJapan> weeks = new();

        for (int i = (int)DayOfWeekJapan.日曜日; i <= (int)DayOfWeekJapan.土曜日; i <<= 1)
        {
            if (((int)dayOfWeeks & i) != 0)
            {
                weeks.Add((DayOfWeekJapan)Enum.ToObject(typeof(DayOfWeekJapan), i));
            }
        }

        return string.Join(",", weeks);
        */
    }
}

ゴミの基底クラス

abstract class GarbageBase
{
    protected GarbageBase(string name, string comment = "")
    {
        Name = name;
        Comment = comment;
    }

    public string Name { get; set; }
    public string Comment { get; set; }
}

ゴミの種別クラス

各ゴミの種別(可燃ゴミ、資源プラスチック、ビン類)のクラス作成時の基底クラスになります

abstract class GarbageClass : GarbageBase
{
    protected GarbageClass(string name, string comment = "") : base(name, comment) { }

    public static List<GarbageClass> GarbageClasses { get; set; } = new List<GarbageClass>();

    public static GarbageClass GetGarbageCollectionDay(string? sel)
    {
        var hit = GarbageClasses.Where(n => n.Garbages.Any(x => x.Name == sel)).ToList();

        if (hit.Count == 1)
        {
            return hit[0];
        }

        return null;
    }

    public static bool Show(string sel)
    {
        var hit = GarbageClasses.Where(n => n.Garbages.Any(x => x.Name == sel)).ToList();

        if (hit.Count == 1)
        {
            Console.WriteLine($"{sel}のゴミ収集日は、{hit[0].GarbageCollectionDay}です");
            return true;
        }
        return false;
    }

    public void Add(Garbage garbage)
    {
        Garbages.Add(garbage);
    }

    public abstract DayOfWeekJapan GarbageCollectionDay { get; set; }
    public abstract List<Garbage> Garbages { get; set; }
}

ゴミ単体の登録用クラス

大元の基底クラスから継承しています

class Garbage : GarbageBase
{
    public Garbage(string name, string comment = "") : base(name, comment) { }
}

可燃ゴミクラス

ゴミの種別クラスから継承しています

class BurnableGarbage : GarbageClass
{
    public BurnableGarbage(string name, DayOfWeekJapan garbageCollectionDay, string comment = "") : base(name, comment)
    {
        GarbageCollectionDay = garbageCollectionDay;
    }

    public override DayOfWeekJapan GarbageCollectionDay { get; set; }
    public override List<Garbage> Garbages { get; set; } = new List<Garbage>();
}

資源プラスチッククラス

ゴミの種別クラスから継承しています

class RecyclablePlastic : GarbageClass
{
    public RecyclablePlastic(string name, DayOfWeekJapan garbageCollectionDay, string comment = "") : base(name, comment)
    {
        GarbageCollectionDay = garbageCollectionDay;
    }

    public override DayOfWeekJapan GarbageCollectionDay { get; set; }
    public override List<Garbage> Garbages { get; set; } = new List<Garbage>();
}

ビン類クラス

ゴミの種別クラスから継承しています

class Bin : GarbageClass
{
    public Bin(string name, DayOfWeekJapan garbageCollectionDay, string comment = "") : base(name, comment)
    {
        GarbageCollectionDay = garbageCollectionDay;
    }

    public override DayOfWeekJapan GarbageCollectionDay { get; set; }
    public override List<Garbage> Garbages { get; set; } = new List<Garbage>();
}

Mainメソッド

各クラスからインスタンスを生成、データのセット、処理を担当しています

// 可燃ゴミ分類型変数の宣言と作成したインスタンスの代入
GarbageClass burnableGarbage = new BurnableGarbage("可燃ゴミ", DayOfWeekJapan.月曜日 | DayOfWeekJapan.木曜日);
// 可燃ゴミ分類リストへ追加
burnableGarbage.Add(new Garbage("おむつ"));
burnableGarbage.Add(new Garbage("ぬいぐるみ"));

// 資源プラスチック分類型変数の宣言と作成したインスタンスの代入
GarbageClass recyclablePlastics = new RecyclablePlastic("資源プラスチック", DayOfWeekJapan.木曜日);
// 資源プラスチックゴミ分類リストへ追加
recyclablePlastics.Add(new Garbage("ボトル"));

// ビン分類型変数の宣言と作成したインスタンスの代入
GarbageClass bin = new Bin("ビン", DayOfWeekJapan.金曜日);
// ビンゴミ分類リストへ追加
bin.Add(new Garbage("スプレー缶"));

// 基底クラス(GarbageClass)のリストに上記ゴミの種別リストを追加
GarbageClass.GarbageClasses.Add(burnableGarbage);
GarbageClass.GarbageClasses.Add(recyclablePlastics);
GarbageClass.GarbageClasses.Add(bin);

// ここから、処理のサンプル
// ゴミを入力
string? sel = Console.ReadLine();

// ゴミを引数として、ゴミの種別のインスタンスを取得
GarbageClass garbageClass = GarbageClass.GetGarbageCollectionDay(sel);

if (garbageClass == null)
{
    Console.WriteLine("未登録です");
    Console.ReadLine();
    return;
}
// 収集曜日プロパティから収集曜日情報を取得
DayOfWeekJapan? dayOfWeeks = garbageClass.GarbageCollectionDay;
// 複数の曜日が含まれている可能性があるので、取り出す
string? weeks = Convert.GetAllDayOfWeek(dayOfWeeks);

// ゴミの種別名プロパティから種別名を取得
string garbageClassName = garbageClass.Name;

// 結果を表示
Console.WriteLine($"ゴミの種別は{garbageClassName}で、収集曜日は{weeks}");

//GarbageClass.Show(sel);

クラス図