技術とか戦略とか

IT技術者が技術や戦略について書くブログです。

C#:オブジェクトの中身をコピーする方法(MemberwiseCloneメソッド実装)

参照型変数(主に、自分で作成したクラスのオブジェクト)をコピーする場合、単純に「=」で代入するだけでは不十分な場合があります。
参照型変数の中身は参照先(オブジェクトのメモリ領域を示すポインタ)です。
「=」で代入するだけでは、参照先だけがコピーされて、参照しているものは同じという状態になるので、コピー先の変更がコピー元に影響してしまいますし、その逆にコピー元の変更がコピー先に影響してしまいます。
 
これを避けたい場合は、MemberwiseCloneメソッドを用いて中身を丸ごとコピー(新たにメモリ領域を確保し書き込み、ディープコピー)する必要があります。
MemberwiseCloneメソッドはobject型で定義されており、C#ではすべての型はobject型から派生しているので、特別な記述を行わなくともMemberwiseCloneメソッドを使用できます。ただし、戻り値はobject型なので、キャスト等の考慮は必要です。
javaの場合はこちらの記事(https://cyzennt.co.jp/blog/2020/01/24/java%ef%bc%9a%e3%82%aa%e3%83%96%e3%82%b8%e3%82%a7%e3%82%af%e3%83%88%e3%81%ae%e4%b8%ad%e8%ba%ab%e3%82%92%e3%82%b3%e3%83%94%e3%83%bc%e3%81%99%e3%82%8b%e6%96%b9%e6%b3%95%ef%bc%88clone%e3%83%a1%e3%82%bd/)のようにインターフェースの実装が必要だったり例外処理が必要だったりと色々面倒です。後発言語であるC#では言語仕様上ディープコピーを始めから考慮している印象を受けます。)
 
以下、サンプルコードです。
参照先のみコピーした場合とMemberwiseCloneメソッドで中身をコピーした場合を比較しています。
参照先のみコピーした場合は、コピー後にコピー先を変更した際にコピー元が影響を受けていますが、MemberwiseCloneメソッドで中身をコピーした場合は影響を受けていません。
 
【サンプルコード】
・CloneableItem.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CloneTest
{
    class CloneableItem
    {
        // ミュータブルの参照型変数を含む場合はそれも一緒にcloneが必要
        // javaの時は書き忘れたがシリアライズ・デシリアライズでも良い
        // (今回は割愛)
        public int ItemId { get; set; }
        public string ItemName { get; set; }

        // MemberwiseCloneメソッドを使用
        public CloneableItem Clone()
        {
            // Object型で返ってくるのでキャストが必要
            return (CloneableItem)MemberwiseClone(); ;
        }
    }
}
 
・ItemCloneMain.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace CloneTest
{
    class ItemCloneMain
    {
        static void Main(string[] args)
        {
            Console.WriteLine("[参照先のみコピーした場合]");
            CloneableItem cloneableItem1 = new CloneableItem();
            cloneableItem1.ItemId = 1;
            cloneableItem1.ItemName = "sword";
            Console.WriteLine
                ("コピー前のコピー元オブジェクト:ID=" +
                 cloneableItem1.ItemId +
                 " NAME=" +
                 cloneableItem1.ItemName);
            CloneableItem cloneableItem2 = cloneableItem1;
            Console.WriteLine
                ("コピー後のコピー元オブジェクト:ID=" +
                 cloneableItem1.ItemId +
                 " NAME=" +
                 cloneableItem1.ItemName);
            Console.WriteLine
                ("コピー後のコピー先オブジェクト:ID=" +
                 cloneableItem2.ItemId +
                 " NAME=" +
                 cloneableItem2.ItemName);
            cloneableItem2.ItemId = 2;
            cloneableItem2.ItemName = "shield";
            Console.WriteLine
                ("編集後のコピー元オブジェクト :ID=" +
                 cloneableItem1.ItemId +
                 " NAME=" +
                 cloneableItem1.ItemName);
            Console.WriteLine
                ("編集後のコピー先オブジェクト :ID=" +
                 cloneableItem2.ItemId +
                 " NAME=" +
                 cloneableItem2.ItemName);

            Console.WriteLine("[参照先のみコピーした場合]");
            CloneableItem cloneableItem3 = new CloneableItem();
            cloneableItem3.ItemId = 3;
            cloneableItem3.ItemName = "armor";
            Console.WriteLine
                ("コピー前のコピー元オブジェクト:ID=" +
                 cloneableItem3.ItemId +
                 " NAME=" +
                 cloneableItem3.ItemName);
            CloneableItem cloneableItem4 = cloneableItem3.Clone();
            Console.WriteLine
                ("コピー後のコピー元オブジェクト:ID=" +
                 cloneableItem3.ItemId +
                 " NAME=" +
                 cloneableItem3.ItemName);
            Console.WriteLine
                ("コピー後のコピー先オブジェクト:ID=" +
                 cloneableItem4.ItemId +
                 " NAME=" +
                 cloneableItem4.ItemName);
            cloneableItem4.ItemId = 4;
            cloneableItem4.ItemName = "helm";
            Console.WriteLine
                ("編集後のコピー元オブジェクト :ID=" +
                 cloneableItem3.ItemId +
                 " NAME=" +
                 cloneableItem3.ItemName);
            Console.WriteLine
                ("編集後のコピー先オブジェクト :ID=" +
                 cloneableItem4.ItemId +
                 " NAME=" +
                 cloneableItem4.ItemName);

            Console.ReadKey(true);
        }
    }
}
 
【実行結果】
[参照先のみコピーした場合]
コピー前のコピー元オブジェクト:ID=1 NAME=sword
コピー後のコピー元オブジェクト:ID=1 NAME=sword
コピー後のコピー先オブジェクト:ID=1 NAME=sword
編集後のコピー元オブジェクト :ID=2 NAME=shield
編集後のコピー先オブジェクト :ID=2 NAME=shield
[参照先のみコピーした場合]
コピー前のコピー元オブジェクト:ID=3 NAME=armor
コピー後のコピー元オブジェクト:ID=3 NAME=armor
コピー後のコピー先オブジェクト:ID=3 NAME=armor
編集後のコピー元オブジェクト :ID=3 NAME=armor
編集後のコピー先オブジェクト :ID=4 NAME=helm

javaでのStateパターン

Stateパターンとはデザインパターンの一種で、1つ1つの状態をそのままクラスとして表現するパターンです。
通常のコーディングだとIF文で状態毎の処理を記述させるためそれぞれの状態でどのような処理が行われるのか読み取りにくいのですが、このパターンを適用すれば状態遷移図上の状態とクラスが1対1で結びつくようになるので、処理が単純明快になります。
 
今回は、朝・昼・夜の各状態で出現モンスターが変わるサンプルコードを作成してみました。
このパターンは記述内容としては決して難しくないので、サンプルコードを見た方が理解が早いと思います。
 
【サンプルコード】
・StateEnum.java
// Stateの番号や共通処理を定義するEnumクラス
//
// 今回のデザインパターンと直接関係ありませんが、
// 共通の定数を使用するため定義します。
public enum StateEnum {
    unknown(0),
    morning(1),
    day(2),
    night(3);

    private int id;

    private StateEnum (int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public static StateEnum valueOf(int id) {
        StateEnum[] array = values();
        for(StateEnum num : array) {
            if (id == num.getId()){
                return num;
            }
        }
        return null;
    }

    public static int stateValidateJudge(int id) {
        if (id < 0 || id > 3) {
            System.out.println("不明なStateです。UnknownStateと仮定します。");
            return 0;
        } else {
            return id;
        }
    }

    public static int stateChangeJudge(int hour) {
        if (hour < 0 || hour > 23) {
            System.out.println("不正な時刻です。");
            return -1;
        } else if (hour >= 5 && hour < 10) {
            return morning.getId();
        } else if (hour >= 10 && hour < 18) {
            return day.getId();
        } else {
            return night.getId();
        }
    }

}

・State.java
// State毎に作成するクラス
// 本来のStateパターンではインターフェースですが、
// 今回は共通処理が存在するため、抽象クラスで定義します。
public abstract class State {

    protected int stateId;

    public State(int stateId) {
        this.stateId = StateEnum.stateValidateJudge(stateId);
    }

    // Stateの変更判定だけする形にします。
    // 本来のStateパターンではどのオブジェクトに変更するかも定義しますが、
    // 密結合を避けるため、今回は利用者側にそれを判断させる形にします。
    public int stateChangeJudge(int hour) {
        int futureStateId = StateEnum.stateChangeJudge(hour);
        if (stateId == futureStateId) {
            return 0; // 変更無し
        } else {
            return futureStateId;
        }
    };

    public abstract void stateMethod(Context context);

}

・UnknownState.java
// 1回も状態遷移をしていない場合のState
// 通常使わない想定です。
public class UnknownState extends State{

    private static State unknownState = new UnknownState();

    private UnknownState() {
        super(StateEnum.unknown.getId());
    }

    // 複数のインスタンスを用意する必要がないのでシングルトン
    public static State getInstance() {
        return unknownState;
    }

    public void stateMethod(Context context) {
        context.contextMethod("まおう");
    }

}

・MorningState.java
// 朝の場合のState
public class MorningState extends State{

    private static State morningState = new MorningState();

    private MorningState() {
        super(StateEnum.morning.getId());
    }

    // 複数のインスタンスを用意する必要がないのでシングルトン
    public static State getInstance() {
        return morningState;
    }

    public void stateMethod(Context context) {
        context.contextMethod("とりモンスター");
    }

}

・DayState.java
// 昼の場合のState
public class DayState extends State{

    private static State dayState = new DayState();

    private DayState() {
        super(StateEnum.day.getId());
    }

    // 複数のインスタンスを用意する必要がないのでシングルトン
    public static State getInstance() {
        return dayState;
    }

    public void stateMethod(Context context) {
        context.contextMethod("いぬモンスター");
        context.contextMethod("ねこモンスター");
    }

}

・NightState.java
// 夜の場合のState
public class NightState extends State{

    private static State nightState = new NightState();

    private NightState() {
        super(StateEnum.night.getId());
    }

    // 複数のインスタンスを用意する必要がないのでシングルトン
    public static State getInstance() {
        return nightState;
    }

    public void stateMethod(Context context) {
        context.contextMethod("こうもりモンスター");
    }

}

・Context.java
// Stateの管理を行うクラス
public class Context {

    private State state = null;

    public Context() {
        state = UnknownState.getInstance();
    }

    public void setState(State state) {
        this.state = state;
    }

    public void stateChangeJudge(int hour) {
        int futureStateId = state.stateChangeJudge(hour);
        switch (futureStateId){
          case 1:
            setState(MorningState.getInstance());
            break;
          case 2:
            setState(DayState.getInstance());
            break;
          case 3:
            setState(NightState.getInstance());
            break;
          case -1:
            System.out.println("不正な操作のため状態変更はしません。");
            break;
          default: // 0が返る場合。状態変更無し。
            break;
        }

    }

    public void stateMethod() {
        state.stateMethod(this);
    }

    public void contextMethod(String monster) {
        System.out.println(monster + " があらわれた!");
    }

}

・StateMain.java
// メインクラス
public class StateMain {
    public static void main(String[ ] args) {
        Context context = new Context();
        for (int i = 0; i < 24; i++) {
            System.out.println("現在 " + i + "時");
            context.stateChangeJudge(i);
            context.stateMethod();
            System.out.println();
        }
    }
}

【実行結果】
現在 0時
こうもりモンスター があらわれた!

現在 1時
こうもりモンスター があらわれた!

現在 2時
こうもりモンスター があらわれた!

現在 3時
こうもりモンスター があらわれた!

現在 4時
こうもりモンスター があらわれた!

現在 5時
とりモンスター があらわれた!

現在 6時
とりモンスター があらわれた!

現在 7時
とりモンスター があらわれた!

現在 8時
とりモンスター があらわれた!

現在 9時
とりモンスター があらわれた!

現在 10時
いぬモンスター があらわれた!
ねこモンスター があらわれた!

現在 11時
いぬモンスター があらわれた!
ねこモンスター があらわれた!

現在 12時
いぬモンスター があらわれた!
ねこモンスター があらわれた!

現在 13時
いぬモンスター があらわれた!
ねこモンスター があらわれた!

現在 14時
いぬモンスター があらわれた!
ねこモンスター があらわれた!

現在 15時
いぬモンスター があらわれた!
ねこモンスター があらわれた!

現在 16時
いぬモンスター があらわれた!
ねこモンスター があらわれた!

現在 17時
いぬモンスター があらわれた!
ねこモンスター があらわれた!

現在 18時
こうもりモンスター があらわれた!

現在 19時
こうもりモンスター があらわれた!

現在 20時
こうもりモンスター があらわれた!

現在 21時
こうもりモンスター があらわれた!

現在 22時
こうもりモンスター があらわれた!

現在 23時
こうもりモンスター があらわれた!

WindowsバッチでJavaScriptの関数の結果を標準出力で受け取る

表題の通りですが、WindowsバッチでJavaScriptの関数の結果を標準出力で受け取ることができます。
標準出力なので、パイプやリダイレクトも可能です。
 
JavaScriptで書かれた処理を自動実行したい場合や、JavaScriptのちょっとした仕様調査を行いたい場合等に活用できると思います。
 
mshta.exeをWindowsバッチから呼び出す方法もあるのですが、mshta.exeはセキュリティ的にあまり良くない実行ファイルでありセキュリティソフトにブロックされることがあるので、WindowsPowerShellWindowsバッチから呼び出す方法を紹介します。
32bitマシンと64bitマシンで微妙に呼び出し方が異なる用なのですが、現在では64bitマシンが主流のため、64bitマシンを前提とします。
 
ここから先はサンプルコードを見た方が早いと思うので、サンプルコードを紹介します。
Windowsバッチ(~.bat)は1つのコマンドが長くなるので「^(キャレット)」でコマンドを改行しています。
 
【サンプルコード】
・js_test.ps1
# JavaScriptを実行するオブジェクトを作成
$sc = New-Object -ComObject MSScriptControl.ScriptControl
$sc.Language = "Javascript"

# 引数1つの関数「func」を登録
$func = "
    function func(arg1){
        var hello = 'Hello\r\n';
        return hello+arg1;
    }
"
$sc.Eval($func)

# 登録された関数を実行
# 戻り値が標準出力される
$sc.CodeObject.func('Code')
 
・js_test.bat
@echo off

rem 64bitマシンの場合、32bit版のpowershellを明示的に指定する必要がある
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe ^
-ExecutionPolicy RemoteSigned -File .\js_test.ps1
pause
 
・js_test_pipe.bat
@echo off

rem 標準出力なのでパイプに渡すこともできる
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe ^
-ExecutionPolicy RemoteSigned -File .\js_test.ps1 | sort
pause
 
・js_test_redirect.bat
@echo off

rem 標準出力なのでリダイレクトでファイル出力もできる
del .\js_test.txt
C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe ^
-ExecutionPolicy RemoteSigned -File .\js_test.ps1 > .\js_test.txt
dir .\js_test.txt
type .\js_test.txt
pause
 
【実行結果】
・js_test.bat
Hello
Code
続行するには何かキーを押してください . . .
 
・js_test_pipe.bat
Code
Hello
続行するには何かキーを押してください . . .
 
・js_test_redirect.bat
C:\tmp\js_test.txt が見つかりませんでした。
 ドライブ C のボリューム ラベルは  です
 ボリューム シリアル番号は  です

 C:\tmp のディレクト

2020/03/15  16:33                13 js_test.txt
               1 個のファイル                  13 バイト
               0 個のディレクトリ  24,925,503,488 バイトの空き領域
Hello
Code
続行するには何かキーを押してください . . .

javaでのMediatorパターン

Mediatorパターンはデザインパターンの一つで、複雑に絡み合ったオブジェクト間の関係をMediator(調停者)が整理するパターンです。
例えば、10個のオブジェクト間で連携を取る必要がある場合、オブジェクト同士で直接連携を取ると、「自分以外の9個のオブジェクトに対する調整処理 × 10個のオブジェクト」で90通りの調整処理が必要になってしまいます。
しかし、調停者のオブジェクトを新たに作成し、その調停者を通して調整することにすれば、「調停者に対する調整処理 ×10個のオブジェクト」で10通りの調整処理で済みます。
 
ある資源に対して作用するプロセスやボタン等が多い場合に効果を発揮するパターンです。
 
以下、サンプルコードです。
誰かが実施すれば良い作業に対して、複数の作業者が作業しようとする例です。
 
【サンプルコード】
・Mediator.java
// 作業を管理する調停者の抽象クラス
public abstract class Mediator {

    // 作業名
    private String name;

    public Mediator(String name) {
        this.name = name;
    }

    public String getName(){
        return name;
    }

    // 作業実施を報告するメソッド
    public abstract void setColleague(Colleague colleague);

    // 作業実施状況を管理するメソッド
    public abstract boolean isDoneConsultation();
}
 
・ConcreteMediatorA.java
// 作業を管理する丁寧な調停者の具象クラス
public class ConcreteMediatorA extends Mediator {

    // 作業を完了させた同僚
    private Colleague doneColleague = null;

    public ConcreteMediatorA(String name) {
        super(name);
    }

    public void setColleague(Colleague colleague) {
        if (doneColleague == null) {
            doneColleague = colleague;
            System.out.println
                (this.getName() + "の実施ありがとう、" +
                 colleague.getName() + "さん。");
        // 管理することでここに飛ばないようにすることができる
        } else {
            System.out.println
                (colleague.getName() + "さん、残念ですが" +
                 this.getName() + "はもう実施済みなんですよね…。");
        }
    }

    public boolean isDoneConsultation() {
        if (doneColleague == null) {
            System.out.println
                ("まだ" + this.getName() +"は実施されていません。");
            return false;
        } else {
            System.out.println
                (this.getName() + "はもう" +
                 doneColleague.getName() + "さんが実施しました。");
            return true;
        }
    }

}
 
・ConcreteMediatorB.java
// 作業を管理するフランクな調停者の具象クラス
public class ConcreteMediatorB extends Mediator {

    // 作業を完了させた同僚
    private Colleague doneColleague = null;

    public ConcreteMediatorB(String name) {
        super(name);
    }

    public void setColleague(Colleague colleague) {
        if (doneColleague == null) {
            doneColleague = colleague;
            System.out.println
                (this.getName() + "サンキュー、" +
                 colleague.getName() + "さん。");
        // 管理することでここに飛ばないようにすることができる
        } else {
            System.out.println
                (colleague.getName() + "さん、" +
                 this.getName() + "はもうやっちゃったんだよね…。");
        }
    }

    public boolean isDoneConsultation() {
        if (doneColleague == null) {
            System.out.println
                (this.getName() +"はまだやってないぜ。");
            return false;
        } else {
            System.out.println
                (this.getName() + "はもう" +
                 doneColleague.getName() + "さんがやったよ。");
            return true;
        }
    }

}
 
・Colleague.java
// 作業を実施する同僚の抽象クラス
public abstract class Colleague {

    private String name;

    // 行う作業の調停者
    protected Mediator mediator;

    public Colleague(String name){
        this.name = name;
    }

    public String getName(){
        return name;
    }

    public void setMediator(Mediator mediator){
        this.mediator = mediator;
    }

    // 作業の実施状況のアドバイスを求める
    public abstract boolean needsAdvice();

    // 作業を実施する
    public abstract void run();
}
 
・ConcreteColleagueA.java
// 作業を実施する丁寧な同僚の具象クラス
public class ConcreteColleagueA extends Colleague {

    public ConcreteColleagueA(String name) {
        super(name);
    }

    public boolean needsAdvice() {
        System.out.println(mediator.getName() + "は終わりましたか?");
        return mediator.isDoneConsultation();
    }

    public void run() {
        System.out.println(mediator.getName() + "を実施しました。");
        mediator.setColleague(this);
    }

}
 
・ConcreteColleagueB.java
// 作業を実施するフランクな同僚の具象クラス
public class ConcreteColleagueB extends Colleague {

    public ConcreteColleagueB(String name) {
        super(name);
    }

    public boolean needsAdvice() {
        System.out.println(mediator.getName() + "終わった?");
        return mediator.isDoneConsultation();
    }

    public void run() {
        System.out.println(mediator.getName() + "やったよ。");
        mediator.setColleague(this);
    }

}
 
・MediatorMain.java
// メインクラス
public class MediatorMain {

    public static void main(String[] args){

        // オブジェクトの生成
        Mediator mediatorA = new ConcreteMediatorA("作業A");
        Mediator mediatorB = new ConcreteMediatorB("作業B");
        Colleague colleagueA = new ConcreteColleagueA("作業者A");
        Colleague colleagueB1 = new ConcreteColleagueB("作業者B1");
        Colleague colleagueB2 = new ConcreteColleagueB("作業者B2");

        // 作業Aに着手
        // A→B1→B2の順番に作業実施
        // 作業者同士で連携を取ると、runメソッドが2×3=6つ必要になってしまう
        run(colleagueA, mediatorA);
        run(colleagueB1, mediatorA);
        run(colleagueB2, mediatorA);

        // 作業Bに着手
        // B1→B2→Aの順番に作業実施
        // 作業者同士で連携を取ると、runメソッドが2×3=6つ必要になってしまう
        run(colleagueB1, mediatorB);
        run(colleagueB2, mediatorB);
        run(colleagueA, mediatorB);

    }

    // 作業実施メソッド
    public static void run(Colleague colleague, Mediator mediator) {
        colleague.setMediator(mediator);
        if (!colleague.needsAdvice()) {
            colleague.run();
        }
        System.out.println();
    }

}
 
【実行結果】
作業Aは終わりましたか?
まだ作業Aは実施されていません。
作業Aを実施しました。
作業Aの実施ありがとう、作業者Aさん。

作業A終わった?
作業Aはもう作業者Aさんが実施しました。

作業A終わった?
作業Aはもう作業者Aさんが実施しました。

作業B終わった?
作業Bはまだやってないぜ。
作業Bやったよ。
作業Bサンキュー、作業者B1さん。

作業B終わった?
作業Bはもう作業者B1さんがやったよ。

作業Bは終わりましたか?
作業Bはもう作業者B1さんがやったよ。

javaでのChainOfResponsibilityパターン

Chain of Responsibilityパターンとは、その名の通り責任が連鎖する構造を表すためのパターンです。
あるオブジェクトで解決できない問題を別のオブジェクトにたらい回すようにするのがツボで、そうすることで解決できなかった場合の処理を簡略化できます。
 
ソースコードを見た方が理解が早いと思うので、いつも通りサンプルコードを示したいと思います。
某有名RPGを模した例で、3匹の味方モンスターが色々な敵モンスターと戦い、その結果を表示するというサンプルコードです。
(なお、今回のサンプルコードでは、このデザインパターンとは直接関係のないEnumクラスも出てきますが、これは同じ定数が何度も出てくるサンプルであるために簡略化のために使用しているので、「責任をたらい回す」という本質とは関係ありません)
 
【サンプルコード】
・MonsterAttributeEnum.java
// モンスターの属性を示すEnumクラス
//
// 今回のデザインパターンと直接関係ありませんが、
// 共通の定数を使用するため定義します。
public enum MonsterAttributeEnum {
    normal(0),
    fire(1),
    water(2),
    grass(3),
    dragon(4);

    private int id;
    public final static int ATTRIBUTE_TOTAL_NUMBER = 5; // 属性の総数

    private MonsterAttributeEnum (int id) {
        this.id = id;
    }

    public int getId() {
        return id;
    }

    public static MonsterAttributeEnum valueOf(int id) {
        MonsterAttributeEnum[ ] array = values();
        for(MonsterAttributeEnum num : array) {
            if (id == num.getId()){
                return num;
            }
        }
        return null;
    }

    // 倒せるかどうかの判定
    public static boolean defeatJudge
        (MonsterAttributeEnum monsterAttributeEnumAlly,
         MonsterAttributeEnum monsterAttributeEnumEnemy) {

        boolean[ ][ ] result = new boolean
            [ATTRIBUTE_TOTAL_NUMBER][ATTRIBUTE_TOTAL_NUMBER];

        result[normal.getId()][normal.getId()] = true;
        result[normal.getId()][fire.getId()] = false;
        result[normal.getId()][water.getId()] = false;
        result[normal.getId()][grass.getId()] = false;
        result[normal.getId()][dragon.getId()] = false;
        result[fire.getId()][normal.getId()] = true;
        result[fire.getId()][fire.getId()] = false;
        result[fire.getId()][water.getId()] = false;
        result[fire.getId()][grass.getId()] = true;
        result[fire.getId()][dragon.getId()] = false;
        result[water.getId()][normal.getId()] = true;
        result[water.getId()][fire.getId()] = true;
        result[water.getId()][water.getId()] = false;
        result[water.getId()][grass.getId()] = false;
        result[water.getId()][dragon.getId()] = false;
        result[grass.getId()][normal.getId()] = true;
        result[grass.getId()][fire.getId()] = false;
        result[grass.getId()][water.getId()] = true;
        result[grass.getId()][grass.getId()] = false;
        result[grass.getId()][dragon.getId()] = false;
        result[dragon.getId()][normal.getId()] = true;
        result[dragon.getId()][fire.getId()] = true;
        result[dragon.getId()][water.getId()] = true;
        result[dragon.getId()][grass.getId()] = true;
        result[dragon.getId()][dragon.getId()] = true;

        return result
            [monsterAttributeEnumAlly.getId()]
            [monsterAttributeEnumEnemy.getId()];

    }

}
 
・EnemyMonster.java
// 敵モンスターを示すクラス
public class EnemyMonster {

    private int attributeId;

    public EnemyMonster(int attributeId) {
        this.attributeId = attributeId;
    }

    public int getAttributeId() {
        return attributeId;
    }

}

・AllyMonster.java
// 味方モンスターを示すクラス
//
// 他の文献では抽象クラスとして定義されますが、
// 当クラスを具象クラスとして定義してもたらい回しは実現できるため、
// 例を単純化するために具象クラスとして定義します。
public class AllyMonster {

    private int attributeId;

    // たらい回し先
    private AllyMonster nextAlly = null;

    public AllyMonster(int attributeId) {
        this.attributeId = attributeId;
    }

    // たらい回し先のセット
    public AllyMonster setNext(AllyMonster nextAlly) {
        this.nextAlly = nextAlly;
        return nextAlly;
    }

    // 敵モンスターと戦うメソッド
    public void battle(EnemyMonster enemyMonster) {

        // Enum上の名前を取得
        MonsterAttributeEnum allyName =
            MonsterAttributeEnum.valueOf(this.attributeId);
        MonsterAttributeEnum enemyName =
            MonsterAttributeEnum.valueOf(enemyMonster.getAttributeId());

        // 倒せるなら倒す
        // 倒せずにたらい回し先がいるならたらい回す
        // たらい回し先がいないなら倒せない
        if (MonsterAttributeEnum.defeatJudge(allyName,enemyName)) {
            System.out.println
                ("敵の" + enemyName + "モンスターは、" +
                 "味方の" + allyName + "モンスターが倒した。");
        } else if (nextAlly != null) {
            nextAlly.battle(enemyMonster);
        } else {
            System.out.println
            ("敵の" + enemyName + "モンスターは、" +
             "誰も倒せなかった…。");
        }
    }

}
 
・MonsterBattleMain.java
// 味方モンスターが敵モンスターと戦うmainクラス
public class MonsterBattleMain {

    public static void main(String[ ] args) {

        // 味方モンスターの定義
        AllyMonster fire =
            new AllyMonster(MonsterAttributeEnum.fire.getId());
        AllyMonster water =
                new AllyMonster(MonsterAttributeEnum.water.getId());
        AllyMonster grass =
                new AllyMonster(MonsterAttributeEnum.grass.getId());

        // 味方モンスターのたらい回し先の設定
        fire.setNext(water).setNext(grass);

        // 敵モンスターとの戦闘
        for (int i = 0; i < 5; i++) {
            System.out.println
                (MonsterAttributeEnum.valueOf(i) + "モンスターが現れた!");
            fire.battle(new EnemyMonster(i));
            System.out.println();
        }
    }

}
 
【実行結果】
normalモンスターが現れた!
敵のnormalモンスターは、味方のfireモンスターが倒した。

fireモンスターが現れた!
敵のfireモンスターは、味方のwaterモンスターが倒した。

waterモンスターが現れた!
敵のwaterモンスターは、味方のgrassモンスターが倒した。

grassモンスターが現れた!
敵のgrassモンスターは、味方のfireモンスターが倒した。

dragonモンスターが現れた!
敵のdragonモンスターは、誰も倒せなかった…。

C#でのHelloWorld(フォームアプリ)

今回は、C#のフォームアプリについて、HelloWorldを表示するアプリケーションを作成したいと思います。
単純に手順通りにHelloWorldを表示させるだけだと今後の開発でつまずきかねないので、「Visual Studio Community」というIDEが何をしていてなぜ動くのかということも簡単に補足しています。
 
【手順】
1.Visual Studio Community を開く。
2.「ファイル > スタートページ」でスタートページを表示させ、スタートページ上の「新しいプロジェクトを作成」をクリック。
3.「Windowsフォームアプリケーション」を選択する。名前は任意で良い。これで「OK」を押下すると、「場所」で指定した場所にプロジェクト(作業フォルダ)が生成される。
4.画面左の「ツールボックス」をクリックしてツールボックスを表示させる。
f:id:akira2kun:20200301184244j:plain

5.ツールボックスの「すべてのWindowsフォーム」から「Button」をクリックし、Form上にドラッグする。同様に、「Label」をクリックし、Form上にドラッグする。「Label」に関しては、右下のプロパティの「Text」で表示されている文字を削除しCtrl+Sで保存する(ここに書かれている文字はフォーム上で初期状態で表示される)。
f:id:akira2kun:20200301184513j:plain

6.Form上のボタンをダブルクリックするとソースコードの編集画面になる。ソースコードは下記を入力する。入力したらCtrl+Sで保存する。
// 今後アプリケーションを拡張する上での参考のため、コメントを付与する。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms; // フォームアプリ用の共通部品を提供するクラス

namespace HelloWorld2
{
    // 雛形のFormを派生させ、Form1を作成。
    // このソースファイルにはForm1で必要な処理のみ記述する。
    // partialクラスなので、Form1のソースファイルは標準で別に存在する。
    // (標準で用意されているソースファイルにはMainメソッド等が存在する)
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        // メソッド名は、画面右下のプロパティの(Name)と結びつく。
        private void button1_Click(object sender, EventArgs e)
        {
            // ラベルのオブジェクト名は、画面右下のプロパティの(Name)と結びつく。
            label1.Text = "Hello World!";
        }
    }
}

7.「開始」ボタンをクリックしてビルド・実行。ボタンをクリックし、ラベルに「Hello World!」と表示されることを確認する。
f:id:akira2kun:20200301184542j:plain

----------------------------
 
余談ですが、フォームアプリは「Visual Studio Community」が無くても作成可能です。
「System.Windows.Forms」がフォームアプリ用の共通部品を提供するクラスであるため、このクラスを取り込めばフォームアプリは作成可能です。
例えば、通常のテキストエディタで下記のソースコードを作成し、コンパイルすると、何もないフォームを作成できます。
 
Visual Studio Community」を使用してフォームアプリ開発を行う理由は、「Visual Studio Community」が無いと開発できないからではなく、視覚的に操作でき共通処理も全て自動で記述してくれる「Visual Studio Community」の機能を用いることで生産性が劇的に向上するため、と言うことができます。
 
【サンプルコード】
・Sample.cs
using System;
using System.Drawing;
using System.Windows.Forms;

class Sample
{
    static void Main()
    {
        Application.Run(new Form());
    }
}
 
コンパイルコマンド
C:\Windows\Microsoft.NET\Framework\v4.0.30319\csc.exe Sample.cs

javaでのVisitorパターン

Visitorパターンは、Compositeパターンを派生させたパターンであり、入れ物・中身の構造に対して複数の処理を記述する必要がある時に適用します。
Entryクラスとは別にVisitorクラスを作成し、Visitorクラスの方に処理を記述することで、構造と処理を分けることができます。
 
【サンプルコード】
・Element.java
// Visitorの受け入れメソッドを定義するインターフェース
public interface Element {
    public abstract void accept(Visitor visitor);
}
 
・Entry.java
// 入れ物と中身を同一視するための抽象クラス
public abstract class Entry implements Element {
    // 名前を外部から参照できるようにpublicメソッドを定義
    public abstract String getName();
}
 
・Book.java
// 中身のクラス
public class Book extends Entry {

    private String name;

    public Book(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

}
 
・GameSoft.java
// 中身のクラス
public class GameSoft extends Entry {

    private String name;

    public GameSoft(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

}
 
・Bag.java
import java.util.ArrayList;
import java.util.Iterator;
// 入れ物のクラス
public class Bag extends Entry {

    private String name;
    private ArrayList<Entry> item = new ArrayList<Entry>();

    public Bag(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public Entry add(Entry entry) {
        item.add(entry);
        return this;
    }

    // Iteratorを外部から参照できるようにpublicメソッドを定義
    public Iterator<Entry> iterator() {
        return item.iterator();
    }

    public void accept(Visitor visitor) {
        visitor.visit(this);
    }

}
 
・Visitor.java
// Visitorの抽象クラス
public abstract class Visitor {
    public abstract void visit(Book book);
    public abstract void visit(GameSoft gameSoft);
    public abstract void visit(Bag bag);
}
 
・ConcreteNormalVisitor.java
import java.util.Iterator;
// 中身・入れ物に対する処理を記述するVisitorクラス
public class ConcreteNormalVisitor extends Visitor {

    public void visit(Book book) {
        System.out.println("これは" + book.getName() + "という本です。");
    }

    public void visit(GameSoft gameSoft) {
        System.out.println
            ("これは" + gameSoft.getName() + "というゲームソフトです。");
    }

    public void visit(Bag bag) {
        System.out.println("これは" + bag.getName() + "という袋です。");
        Iterator<Entry> iterator = bag.iterator();
        while (iterator.hasNext()) {
            Entry entry = (Entry) iterator.next();
            System.out.print("袋「" + bag.getName() + "」の中身:");
            entry.accept(this);
        }
    }

}
 
・ConcreteNariVisitor.java
import java.util.Iterator;
// 中身・入れ物に対する処理を記述するVisitorクラス
public class ConcreteNariVisitor extends Visitor {

    public void visit(Book book) {
        System.out.println("これは" + book.getName() + "という本ナリ。");
    }

    public void visit(GameSoft gameSoft) {
        System.out.println
            ("これは" + gameSoft.getName() + "というゲームソフトナリ。");
    }

    public void visit(Bag bag) {
        System.out.println("これは" + bag.getName() + "という袋ナリ。");
        Iterator<Entry> iterator = bag.iterator();
        while (iterator.hasNext()) {
            Entry entry = (Entry) iterator.next();
            System.out.print("袋「" + bag.getName() + "」の中身:");
            entry.accept(this);
        }
    }

}
 
・EntryMain.java
// メインクラス
public class EntryMain {

    public static void main(String[] args) {

        // 袋、本・ゲームソフトを作成
        Bag bigBag = new Bag("BigBag");
        Bag emptyBag = new Bag("EmptyBag");
        Bag bagOfBook = new Bag("BagOfBook");
        Bag bagOfSoft = new Bag("BagOfSoft");
        Book bigBook = new Book("BigBook");
        Book book1 = new Book("Book1");
        Book book2 = new Book("Book2");
        GameSoft soft1 = new GameSoft("Soft1");
        GameSoft soft2 = new GameSoft("Soft2");

        // 袋の中に詰める
        // 袋の中に詰めるのは袋でも本・ゲームソフトでも良い
        // (入れ物のクラスなのか中身のクラスなのか意識しなくて良い)
        bagOfBook.add(book1);
        bagOfBook.add(book2);
        bagOfSoft.add(soft1);
        bagOfSoft.add(soft2);
        bigBag.add(emptyBag);
        bigBag.add(bagOfBook);
        bigBag.add(bagOfSoft);
        bigBag.add(bigBook);

        // 説明を見る
        // 袋でも本・ゲームソフトでも同じように説明を見ることができる
        // (入れ物のクラスなのか中身のクラスなのか意識しなくて良い)
        // 説明の仕方もVisitorクラス毎で分けることができる
        System.out.println("[BigBag(入れ物クラス)の説明です。]");
        bigBag.accept(new ConcreteNormalVisitor());
        System.out.println();
        System.out.println("[Book1(中身クラス)の説明です。]");
        book1.accept(new ConcreteNormalVisitor());
        System.out.println();
        System.out.println("[BigBag(入れ物クラス)の説明ナリ。]");
        bigBag.accept(new ConcreteNariVisitor());
        System.out.println();
        System.out.println("[Book1(中身クラス)の説明ナリ。]");
        book1.accept(new ConcreteNariVisitor());

    }
}
 
【実行結果】
[BigBag(入れ物クラス)の説明です。]
これはBigBagという袋です。
袋「BigBag」の中身:これはEmptyBagという袋です。
袋「BigBag」の中身:これはBagOfBookという袋です。
袋「BagOfBook」の中身:これはBook1という本です。
袋「BagOfBook」の中身:これはBook2という本です。
袋「BigBag」の中身:これはBagOfSoftという袋です。
袋「BagOfSoft」の中身:これはSoft1というゲームソフトです。
袋「BagOfSoft」の中身:これはSoft2というゲームソフトです。
袋「BigBag」の中身:これはBigBookという本です。

[Book1(中身クラス)の説明です。]
これはBook1という本です。

[BigBag(入れ物クラス)の説明ナリ。]
これはBigBagという袋ナリ。
袋「BigBag」の中身:これはEmptyBagという袋ナリ。
袋「BigBag」の中身:これはBagOfBookという袋ナリ。
袋「BagOfBook」の中身:これはBook1という本ナリ。
袋「BagOfBook」の中身:これはBook2という本ナリ。
袋「BigBag」の中身:これはBagOfSoftという袋ナリ。
袋「BagOfSoft」の中身:これはSoft1というゲームソフトナリ。
袋「BagOfSoft」の中身:これはSoft2というゲームソフトナリ。
袋「BigBag」の中身:これはBigBookという本ナリ。

[Book1(中身クラス)の説明ナリ。]
これはBook1という本ナリ。