Javaデモ/CUI対戦デモ(10) オブジェクト指向プログラミング

 どちらかのPartyが全滅するまでループするだけの超単純なデモです。

長時間 掛かったりする訳では無いので実際に実行してみると良いでしょう。


このデモの多態性(ポリモーフィズム)は あくまでも形式的な多態性(ポリモーフィズム)なので、多態性(ポリモーフィズム)に関しては別途 勉強が必要です。

下記『Javaデモ/Swingグラフィック「Thread、Figure」(2)』は「抽象化(アブストラクション)、継承(インヘリタンス)、多態性(ポリモーフィズム)」を具現化しているので、多態性(ポリモーフィズム)の教材としても最適です。

なお、このデモでは「合成(コンポジション)、集約(アグリゲーション)」が曖昧になっています。


Javaデモ/Swingグラフィック「Figure、Thread」(2) オブジェクト指向プログラミング

https://sphinx1335.blogspot.com/2026/03/javaswingsinthreadsynchronizedvolatile0.html


また、この「CUI対戦デモ」プログラムが難しい場合は下記の『単純化している「CUI対戦デモ」』で勉強してみると良いでしょう。


Javaデモ/単純化している「CUI対戦デモ」 オブジェクト指向プログラミング

https://sphinx1335.blogspot.com/2026/03/javacui.html


集約とコンポジションの例えについて

https://qiita.com/gatapon/items/5e3292f897ab4f817001


>コンポジション

>保持されているものが保持しているものの一部である場合

>この書籍では「車とエンジン」で例えられています。


>集約

>独立して存在できる何かのコレクションがある場合

>こちらは空港と飛行機で例えられています。


その他の要件としては親オブジェクトが消滅する時に、確実に子オブジェクトも消滅するなら合成(コンポジション)と考えて良いです。



CUI(character user interface)の対戦デモです、そして「抽象化(アブストラクション)、継承(インヘリタンス)」のデモでもあります。

あくまでもデモなので、手抜きをしています、「getter、setter」メソッドを どうするとか、「治癒、防御」を どうするかとか。

また「火炎、雷電、剣」などに対して個別に耐性を変えたいなら、配列などにデータを設定すれば良いでしょう。


「ArrayList<CharEssence> d1oMember」は合成(コンポジション)になりますが、概念的にはPartyを解消してもキャラは独立して存在しても良いので集約(アグリゲーション)とも考えられます。

「ArrayList<Factor> dalFactor」は合成(コンポジション)になりますが、概念として厳密に言うと、アイテムは独立して存在しても良いので集約(アグリゲーション)、スキルは独立して存在でき無いので合成(コンポジション)になります。

「剣(Sword)」がアイテム、「火炎(Fire)、雷電(Thunder)、烈風(Burst)、暴風(Stormr)」がスキル。

スキルはキャラの消滅と同時に確実に消滅し、(このデモではアイテムはドロップしないが)アイテムはキャラが消滅してもドロップとかも有り得る。

と言うことでチャント コレクションを分けた方が良かったですね。



アニメ「ブラッククローバー」のアスタ君のように「断魔、宿魔、滅魔」など、剣を何本でも持てるようにしました。

例えば「勇者、魔法使い」などは多数の「スキル、アイテム」を持つので「クラスの継承、インターフェースの実装」では対応できません(ここでは"魔法使い"は作っていませんが)。

「継承(インヘリタンス)」で対応できない場合に「集約(アグリゲーション)、合成(コンポジション)、委譲(デリゲーション)」などを使います、オブジェクト内に機能拡張用オブジェクトを作ることを「集約(アグリゲーション)、合成(コンポジション)」と言い、自オブジェクト・メソッド内で「集約(アグリゲーション)、合成(コンポジション)」オブジェクト・メソッドを呼び出している場合は委譲(デリゲーション)と言います。


オブジェクト指向プログラミングの1番の肝は多態性(ポリモーフィズム)であり、その多態性(ポリモーフィズム)の肝は「共通の機能」(メソッド)を抽出することです(2番目の肝は「集約(アグリゲーション)、合成(コンポジション)」です)。

ちなみに肝となる「共通の機能」が複数 存在する場合もあります。

この場合、「CharEssence」クラスでは「Influence()、Passive()」が肝となる「共通の機能」となりますが、その肝となる「共通の機能」が抽出できるか どうかがオブジェクト指向プログラミングができるか どうかの基準になります。

肝となる「共通の機能」(メソッド)は、その機能ごとに完全にシグネチャが一致していなければなしません(コンストラクタや その他のメソッドは必ずしもシグネチャが一致しなくても良いです)。



勉強のために「魔法使い、スライム、ゴブリン」などを追加してみると良いでしょう。

また、「攻撃(Attack)」クラスにも「氷」系クラスなどを追加してみると良いかもしれません。

ハードルは高いかもしれませんが、「要因(Factor)」クラスに「治癒、防御」クラスなどの追加を考察して見るのも良いかもしれまん。


キャラクターの継承関係は下記のとおりです。


─キャラクター(CharEssence)

␣├人間(Human)

␣│└勇者(Hero)

␣└怪物(Monster)

␣␣└竜(Dragon)


要因の継承関係は下記のとおりです。


─要因(Factor)

␣└攻撃(Attack)

␣␣├火炎(Fire)

␣␣├雷電(Thunder)

␣␣├烈風(Burst)

␣␣├暴風(Stormr) 

␣␣└剣(Sword)



import java.util.ArrayList;

import java.util.Random;


public class Main {

  public static void main(String[] args) {

    Party oFriend = new Party();

    Party oEnemy = new Party();

    CharEssence oCharEsnc;


    oCharEsnc = new Hero("アスタ", 200);

    oCharEsnc.setFactor(new Sword("断魔", 12));

    oCharEsnc.setFactor(new Sword("宿魔", 16));

    oCharEsnc.setFactor(new Sword("滅魔", 18));

    oFriend.add(oCharEsnc);


    oCharEsnc = new Hero("桐ヶ谷 和人", 120);

    oCharEsnc.setFactor(new Sword("エリュシデータ", 16));

    oCharEsnc.setFactor(new Sword("ダークリパルサー", 18));

    oCharEsnc.setFactor(new Burst(null, 6));

    oCharEsnc.setFactor(new Storm(null, 8));

    oCharEsnc.setFactor(new Fire(null, 12));

    oCharEsnc.setFactor(new Thunder(null, 16));

    oFriend.add(oCharEsnc);


    oCharEsnc = new Hero("ユノ", 120);

    oCharEsnc.setFactor(new Burst(null, 16));

    oCharEsnc.setFactor(new Storm(null, 18));

    oFriend.add(oCharEsnc);


    oCharEsnc = new Hero("レオポルド", 160);

    oCharEsnc.setFactor(new Fire(null, 18));

    oFriend.add(oCharEsnc);


    oCharEsnc = new Dragon("竜1", 320);

    oCharEsnc.setFactor(new Fire(null, 12));

    oCharEsnc.setFactor(new Thunder(null, 16));

    oEnemy.add(oCharEsnc);


    oCharEsnc = new Dragon("竜2", 280);

    oCharEsnc.setFactor(new Fire(null, 8));

    oCharEsnc.setFactor(new Thunder(null, 12));

    oEnemy.add(oCharEsnc);


    do {

      oFriend.AutoImpact(oEnemy);

      oEnemy.AutoImpact(oFriend);

    } while (0 < oFriend.size() & 0 < oEnemy.size());

  }

}


class Party {

  static Random random = new Random();

  ArrayList<CharEssence> d1oMember = new ArrayList<CharEssence>();


  int size() {

    return d1oMember.size();

  }


  CharEssence get(int index) {

    return d1oMember.get(index);

  }


  void add(CharEssence one) {

    d1oMember.add(one);

  }


  void remove(int index) {

    d1oMember.remove(index);

  }


  String getInfo(int index) {

    return get(index).getInfo();

  }


  void AutoImpact(Party oOppo) {

    oOppo.AutoPassive(this);

  }


  void AutoPassive(Party oOppo) {

    for (int i = 0; i < oOppo.size(); i++) {

      int iKey = random.nextInt(oOppo.get(i).getFactorQuantity());

      Factor oFactor = oOppo.get(i).getFactor(iKey);

      System.out.println("【影響を与える】" + oOppo.getInfo(i));

      System.out.println("【要因】" + oFactor.getInfo());

      if (1 <= this.size()) {

        int j = random.nextInt(this.size());

        this.get(j).Passive(oFactor);

        if (this.get(j).fHP <= 0) {

          this.remove(j);

          System.out.println("【Remove】this.size()=" + this.size());

        }

        if (this.size() <= 0) {

          break;

        }

      }

      System.out.println();

    }

  }

}


enum Enum_Factor {

  Fire, Thunder, Burst, Storm, Sword,

};


abstract class CharEssence {

  String sCategory, sName;

  double fHP;

  double fDefiance = 1;

  ArrayList<Factor> dalFactor = new ArrayList<Factor>();


  CharEssence(String sPmName, int iPmHP) {

    sName = sPmName;

    fHP = iPmHP;

  }


  void Impact(CharEssence oOppo, Factor oFactor) {

    oOppo.Passive(oFactor);

  }


  void Passive(Factor oFactor) {

    double d = 1;

    int iSignum = 0;

    if (oFactor instanceof Attack) {

      // System.out.println("if (oFactor instanceof Attack)");

      iSignum = -1;

      d = fDefiance;

    }

    fHP += iSignum * oFactor.iForce / d;

    System.out.println("【影響を受けた】" + getInfo());

  }


  String getInfo() {

    return String.format( //

        "Category=%s; Name=%s; HP=%.3f;", sCategory, sName, fHP);

  }


  void setFactor(Factor oFactor) {

    dalFactor.add(oFactor);

  }


  Factor getFactor(int key) {

    return dalFactor.get(key);

  }


  int getFactorQuantity() {

    return dalFactor.size();

  }

}


abstract class Human extends CharEssence {

  Human(String sPmName, int iPmHP) {

    super(sPmName, iPmHP);

  }

}


class Hero extends Human {

  Hero(String sPmName, int iPmHP) {

    super(sPmName, iPmHP);

    fDefiance = 1.5;

    sCategory = "Hero";

  }

}


abstract class Monster extends CharEssence {

  Monster(String sPmName, int iPmHP) {

    super(sPmName, iPmHP);

  }

}


class Dragon extends Monster {

  Dragon(String sPmName, int iPmHP) {

    super(sPmName, iPmHP);

    fDefiance = 2;

    sCategory = "Dragon";

  }

}


abstract class Factor {

  Enum_Factor eCategory;

  String sName;

  int iForce;


  Factor(String sPmName, int iPmForce) {

    sName = sPmName;

    iForce = iPmForce;

    eCategory = Enum_Factor.valueOf( //

        this.getClass().getSimpleName());

  }


  String getCategory() {

    return eCategory.name();

  }


  String getInfo() {

    // System.out.println("Factor#getInfo()");

    String msg = "";

    msg += String.format("Category=%s; ", getCategory());

    if (null != sName) {

      msg += String.format("Name=%s; ", sName);

    }

    return msg;

  }

}


class Attack extends Factor {

  Attack(String sPmName, int iPmForce) {

    super(sPmName, iPmForce);

  }

}


class Fire extends Attack {

  Fire(String sPmName, int iPmForce) {

    super(sPmName, iPmForce);

    // eCategory = Enum_Factor.Fire;

  }

}


class Thunder extends Attack {

  Thunder(String sPmName, int iPmForce) {

    super(sPmName, iPmForce);

    // eCategory = Enum_Factor.Thunder;

  }

}


class Burst extends Attack {

  Burst(String sPmName, int iPmForce) {

    super(sPmName, iPmForce);

    // eCategory = Enum_Factor.Burst;

  }

}


class Storm extends Attack {

  Storm(String sPmName, int iPmForce) {

    super(sPmName, iPmForce);

    // eCategory = Enum_Factor.Storm;

  }

}


class Sword extends Attack {

  Sword(String sPmName, int iPmForce) {

    super(sPmName, iPmForce);

    // eCategory = Enum_Factor.Sword;

  }

}


コメント

このブログの人気の投稿