Java問題 ~予備知識編~

予備知識問題

Java言語のイメージをつけれればよいかな?という程度にクイズゲーム的な感じでやってみてください。
「予備知識」と書きましたが、実装するための「予備知識」という意味です。もしかしたら「理解」と受け取る人もいるかもしれないので、一応迄に。。。
※ 問題は随時追加していきます。

問題

問題1

Q1: Java言語の特徴として正しい説明はつぎのどれですか、2つ選択してください。

  1. Windows上で作成したプログラムを再コンパイルせずにLinux上で実行することができる
  2. Java SEの基本ライブラリは無償であるが、グラフィックやネットワーク関連は有償で提供される
  3. 分散型アプリケーションを作成することができる
  4. メモリ管理はプログラマが行う

答え:1, 3


問題2

Q2: 社内で使用するアプリケーションをJavaで開発することになりました。GUIでの操作が出k利用ような、独立型のアプリケーションを検討しています。使用すべきエディションはどれですか。1つ選んでください。

  1. Java EE
  2. Java SE
  3. Java ME
  4. Java DB
  5. Java FX

答え:2


問題3

Q3: 百貨店向けにブラウザベースのショッピングサイト・アプリケーションを開発する必要があります。使用すべきエディションは次のどれですか。1つ選んでください。

  1. Java EEのみ
  2. Java SEのみ
  3. Java MEのみ
  4. Java SEおよびJava EE
  5. Java SEおよびJava ME
  6. Java EEおよびJava ME

答え:4


問題4

Q4: JVMが行っていることとして、正しい説明は次のどれですか。3つ選んでください。

  1. クラスファイルのロード
  2. バイトコードの解釈
  3. クラスの実行
  4. アプリケーションのロギング
  5. クラスファイルのアセンブル
  6. ソースコードをコンパイルする

答え:1, 2, 3


問題5

Q5: クラスファイルに含まれるものは次のどれですか。1つ選んでください。

  1. スクリプトコード
  2. 実行コード
  3. ソースコード
  4. バイトコード

答え:4


問題6

Q6: ソースファイルの作成規則として正しいものは次のうちどれですか。2つ選んでください。

  1. ソースファイル名は、半角英数字のみ使用できる
  2. 「3.java」というソースファイル名も使用できる
  3. 拡張子は、「.java」もしくは、「.Java」を使用する
  4. ソースファイル内のインデントは、半角空白もしくはタブで行う

答え:2, 4


問題7

Q7: Test.javaとして次のコードがあります。

class Test {
  public static void main(String[] args) {
    System.out.println("Hello");
  }
}

コンパイルするときのコマンドとして正しいのはどれですか?1つ選んでください。

  1. javac Test
  2. javac Test.java
  3. javac Test.class
  4. java Test
  5. java Test.java
  6. java Test.class

答え:2


問題8

Q8: コンソール(標準出力)に表示するコードはどれですか。2つ選んでください。

  1. System.out.print(Test);
  2. System.out.print('Test');
  3. System.out.print(Test);
  4. System.out.print(&100);
  5. System.out.print("100");

答え:4, 5


問題9

Q9: メインメソッドの定義として正しいものはどれですか。1つ選んでください。

  1. static void main(String args[])
  2. public void main(String args[])
  3. public static void main(String[] args)
  4. static main(String args[])
  5. public static void main(String args)

答え:3


問題10

Q10: 次のコードがしていされています。正しいものはどれですか。1つ選んでください。

public class Foo {
   public static void main() {
      System.out.println("Hello");
   }
}
  1. コンパイルエラーが発生する
  2. コンパイルは成功するが、実行時エラーが出る
  3. コンパイルは成功し、実行すると「Hello」と表示する
  4. コンパイルは成功するが、何も表示しない

答え:2


問題11

Q11: 次のプログラムにある「メソッド」をすべて答えなさい。正しいものを次の中から1つ選んでください。

public class Sample {
   public static void main(String[] args) {
      System.out.println("Hello");
   }
}
  1. mainメソッド
  2. printlnメソッド
  3. Systemメソッドとmainメソッド
  4. mainメソッドとprintlnメソッド

答え:「4」メソッドには「()」がつく、ちなみに「System」はクラスを示している。
public static void main(String[] args)はメインメソッドの定義


問題12

Q12: 次のプログラムの実行結果のうち、正しいものを次の中から1つ選んでください。ただし「」かっこの内部が表示された内容とします。改行の位置がポイントになります。

public class Sample {
   public static void main(String[] args) {
      System.out.println("Java プログラム");
      System.out.print("ただいま");
      System.out.print("作成中");
   }
}
  1. 「Java プログラム
    ただいま
    作成中」
  2. 「Java プログラム
    ただいま作成中」
  3. 「Java
    プログラム
    ただいま
    作成中」
  4. 「Java プログラムただいま作成中」

答え:「2」println()は改行がつく、print()は改行がつかない。


問題13

Q13: 次の変数名のうち不適切なものを2つ選んでください。

  1. goto
  2. media
  3. back
  4. apps
  5. assert

答え:「1, 5」


問題14

Q14: 次のプログラムはエラーになります。理由を1つ選んでください。

public class Sample {
    public static void main(String[] goo) {
        System.out.println("Hello!");
        /*
          表示したい文字があればここに書く
          /* System.out.println("文字列");*/
               :}
        */
    }
}
  1. 「}」が不適切な位置にあるのでエラーになる
  2. メインメソッドの引数名「goo」が違うのでエラーになる
  3. 「*/」が不適切な位置に、二つあるのでエラーになる
  4. コメントの中にメソッドが書いてあるのでエラーになる

答え:「1, 5」


問題15

Q15: 二つの引数を比較して小さいほうの値を返すメソッド「min」の正しい記述をを1つ選んでください。

  1. Math.min(10, 3);
  2. Math.min(10. 3);
  3. Math.min(10: 3);
  4. Math.min(10; 3);
  5. Math.min(10@ 3);

答え:「1」


問題16

Q16: 次のコードを実行したとき、①mainメソッド ②printlnメソッドが呼び出される回数を答えてください。

public class Sample {
   public static void main(String[] args) {
       System.out.println("Hello Moning");
       hello();
       System.out.println("Hello Night");
   }
   public static void hello() {
       System.out.println("hello!");
   }
}

答え:「3回」


問題17

Q17: 次のコードを実行したとき、①コンストラクタ ②printlnメソッドが呼び出される回数を選んでください。

public class Sample {
   public static void main(String[] args) {
      hello();
      TestA test = new TestA();
   }
   public static void hello() {
       System.out.println("hello!");
   }
}

public class TestA {
   private String name;
   private int age:

   public TestA() {
       name = "taro";
       age = 1;
   }

   public void talk() {
      System.out.println("My name is " + name + ".");
      System.out.println("I am " + age + " years old.");
   }
}
  1. 1回
  2. 2回
  3. 3回

答え:「1回」


XMLファイルの書き方

XMファイルの書き方

Javaプログラミングでよく使用するXMLファイルですが、 かなり使える優れものというところをアピールしたく。この記事を書くことにしました。
※本当は、XMLの勉強をしたからです。

XMLとは

オラクルによると下記のようなものです。
しかし、WebLogicサーバー(ウェブサーバー)用のドキュメントのようです。

Extensible Markup Language (XML)とは、ドキュメント内のデータの内容および構造を示すのに使用するマークアップ言語です。

日本語にすると「拡張マークアップ言語」であり、とりあえずは、「拡張するのに使えるよ」というところです。

どうやって拡張するの?

同じようにオラクル社のドキュメントでは下記のようにあります。

ML ドキュメントの記述方法には、DTD および スキーマの 2 種類があります。

XML ドキュメントを定義する DTD

<!DOCTYPE address_book [
<!ELEMENT person (name, address?, phone?, email?)>
<!ELEMENT name (#PCDATA)>
<!ELEMENT address (street, city, state, zip)>
<!ELEMENT phone (#PCDATA)>
<!ELEMENT email (#PCDATA)>
<!ELEMENT street (#PCDATA)>
<!ELEMENT city (#PCDATA)>
<!ELEMENT state (#PCDATA)>
<!ELEMENT zip (#PCDATA)> 

<!ATTLIST person gender CDATA #REQUIRED>
<!ATTLIST phone area_code CDATA #REQUIRED>
]>

XML ドキュメントを定義する スキーマ(Schema)

オラクル社によると以下の通りです。

スキーマは、XML 仕様において最近開発され、DTD に取って代わるものとされています。
スキーマでは、DTD および DTD 以外の XML ドキュメント自体より柔軟かつ詳細に XML ドキュメントを記述します。スキーマ仕様は、現在 World Wide Web Consortium(W3C)によって開発中であり、DTD の制限の多くを克服するものとされています。XML スキーマの詳細については、http://www.w3.org/TR/xmlschema-0/ を参照してください。

XML Schema【XSD】

XML Schemaとは、XML文書の構造を定義するスキーマ言語の一つで、Web技術の標準化を進めるW3C(World Wide Web Consortium)が勧告したもの。 XML自体を用いてスキーマを記述する。

<xsd:schema xmlns:xsd="http://www.w3.org/1999/XMLSchema">

<xsd:element     name="address_book" type="bookType"/>

<xsd:complexType name="bookType">
  <xsd:element name=name="person"     type="personType"/>
</xsd:complexType>

<xsd:complexType name="personType">
  <xsd:element   name="name"     type="xsd:string"/>
  <xsd:element   name="address"  type="addressType"/>
  <xsd:element   name="phone"    type="phoneType"/>
  <xsd:element   name="email"    type="xsd:string"/>
  <xsd:attribute name="gender"   type="xsd:string"/> 
</xsd:complexType>

<xsd:complexType name="addressType">

  <xsd:element   name="street"  type="xsd:string"/>
  <xsd:element   name="city"    type="xsd:string"/>
  <xsd:element   name="state"   type="xsd:string"/>
  <xsd:element   name="zip"     type="xsd:string"/>
</xsd:complexType>

<xsd:simpleType name="phoneType">
  <xsd:restriction base="xsd:string"/>
  <xsd:attribute name="area_code" type="xsd:string"/>
</xsd:simpleType>

</xsd:schema>

規定の使い方

上記のファイルでタグの意味を定義付けていろいろな用途に使用するというのが、世の中にある「通常の使い方」です。
Javaプログラマがよく目にするのは、フレームワークで使用する設定ファイル「config.xml」、ウェブサーバーで使用する「web.xml」などがあると思いますが、列挙すると義理がありません。

しかし、ここではJavaをメインに記載していますので、Javaテクノロジーで使用しているXMLについてどのようなものがあるか以下に列挙します。

  • FXML: JavaFXで使用する画面表示のコンポーネントを記述するものです。これには、GUIエディタ「SceneBuilder」があります。
  • CID設定ファイルを使用してJavaEEでの依存性の注入を行っています。

色々ありますが、XMLファイルでクラスを指定して、そのクラスのインスタンスを取得する。DBのORマッピングを行うなどがあります、

使い方1:デコーダーとエンコーダー

XMLDecoderXMLEncoderというクラスがりますが、これもクラスをXMLファイルに出力したり、読み込んだりするクラスです。

使い方2:パーサー

使うのは、DocumentBuilderFactoryというクラス(API)で、下のような形で使用します。

    /**
     * XMLドキュメント(ファイル)を読み込む。
     * @param directory ファイルの配置しているディレクトリ
     * @param fileName ファイル名
     * @return Documentオブジェクト
     * @throws RpgException ファイルの読み込みエラー
     */
    private static Document loadDocumentBuilder(String directory, String fileName) throws Exception {
        //creating a constructor of file class and parsing an XML files
        Path path = Paths.get(directory, fileName);
        File file = path.toFile();// ("/Monster.xml");
        //an instance of factory that gives a document builder
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        //an instance of builder to parse the specified xml file
        DocumentBuilder db = null;
        Document doc = null;
        try {
            db = dbf.newDocumentBuilder();
            doc = db.parse(file);
            doc.getDocumentElement().normalize();
        } catch (ParserConfigurationException e) {
        }
        return doc;
    }

早い話が、DocumentBuilderFactoryクラスで、ファイルを読み込んで、タグを取得するというものです。

XMLファイルを使用するのに、どちらが便利か?

今回は、タグの名前を自由に決めたいので、「使い方2」を採用します。

以前、作成したXMLUtilでは、下のようにXMLを読み込んで、クラスを生成するという手順を踏みました。最終的に、XMLに対応したクラスを作成しておき、その値をXMLファイルから取得するという形での利用でした。

   /**
     * Job.xmlを読み込む。必ず、Command.xmlを読み込んでから実行する。
     * @return Jobリスト
     * @throws RpgException XMLの設定エラー
     */
    public static Map<String, RpgJob> loadJobs(String directory) throws RpgException {
        Map<String, RpgJob> jobMap = new HashMap<>();
        Map<String, RpgCommand> cmdMap = RpgConfig.getInstance().getCommandMap();
        if (cmdMap.size() <= 0) {
            throw new RpgException(MessageConst.ERR_BEFORE_LOAD_CMD.toString());
        }
        try {
            Document doc = loadDocumentBuilder(directory, "Job.xml");
            if (isDebug) System.out.println("Root element: " + doc.getDocumentElement().getNodeName());
            NodeList list = doc.getElementsByTagName("job");

            for (int i = 0; i < list.getLength(); i++) {
                Node node = list.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                    RpgJob j = createJob(node, cmdMap);
                    jobMap.put(j.getJobId(), j);
                }
            }
        } catch (RpgException e) {
            e.printStackTrace();
            throw new RpgException(MessageConst.ERR_XML_PERSE.toString() + ": " + e.getMessage());
        }
        return jobMap;
    }

    /**
     * RpgJobクラスを生成する。依存するCommandListをセット。
     * @param node jobタグ
     * @return RpgJob 職業クラス
     * @throws RpgException XML設定エラー
     */
    private static RpgJob createJob(Node node, Map<String, RpgCommand> map) throws RpgException {
        Element e = (Element) node;
        String id = getTagNode(e, "id").item(0).getTextContent();
        String name = getTagNode(e, "name").item(0).getTextContent();
        String disc =  getTagNode(e, "discription").item(0).getTextContent();
        String commandList =  getTagNode(e, "commandList").item(0).getTextContent();
        String[] list = commandList.split(",");

        List<RpgCommand> cmdList = new ArrayList<>();
        for (String cmd : list) {
            RpgCommand cmdCls = map.get(cmd.trim());
            cmdList.add(cmdCls);
        }

        // RogConfigの値を変えないように気を付ける
        Map<String, RpgStatus> templateMap = RpgConfig.getInstance().getStatusMap();
        Map<String, RpgStatus> statusMap = new HashMap<>();
        statusMap.putAll(templateMap);
        Set<String> set = statusMap.keySet();
        // ステータス上昇値の設定
        for (String key : set) {
            if (isDebug) System.out.println("Status: " + key);
            String statsUp = getTagNode(e, key.toLowerCase()).item(0).getTextContent();
            RpgStatus st = new RpgStatus();
            st.setKigo(key);
            st.setValue(Integer.parseInt(statsUp));
            statusMap.put(key, st);
        }
        RpgJob job = null;
        job = new RpgJob(id, name, disc, cmdList);
        // ステータス上昇値のマップを設定する。
        job.setStatusUpMap(statusMap);
        return job;
    }

これを、使用してフレキシブルな形でクラスの生成をできるようにしたいと思っています。

結局

以下のような実装になりました。単純に読み込んだXMLファイルの文字列(タグ名、値)からクラスを生成する形をとろうと思います。

   /**
     * 各種設定ファイルを読み込むときに、ファイル名に対応した
     * クラスを生成して返却する。
     *
     * @param fileName
     * @return ConfigRpptを継承しているクラス
     */
    public static ConfigIF createConfig(String fileName) {
        ConfigIF conf = null;
        switch (fileName) {
            case StoryConfig.WORLD_XML:
                conf = new World();
                break;
            case StoryConfig.NATURE_XML:
                conf = new Nature();
                break;
            case StoryConfig.CREATURE_XML:
                conf = new Creature();
                break;
            case StoryConfig.REGIONS_XML:
                conf = new Region();
                break;
            case StoryConfig.EFFECTS_XML:
                conf = new Effects();
                break;
            case StoryConfig.STM_XML:
                conf = new STM();
                break;
            case StoryConfig.CPMMADS_XML:
                conf = new Command();
                break;
            case StoryConfig.JOB_XML:
                conf = new Job();
                break;
            case StoryConfig.MONSTER_TYPE_XML:
                conf = new MonsterType();
            case StoryConfig.MONSTERS_XML:
                /** TODO-[MonsterクラスはPlayerクラスを継承する] */
                //conf = new Monster();
                break;
            default:
                System.out.println("想定外の設定ファイル名です。 : " + fileName);
                System.exit(-1);
        }
        return conf;
    }

BlueJの使い方~インストール、クラス作成、サンプルプロジェクト~

BlueJの使い方

Oracle製のIDE「BlueJ」を使ってみようと思います。
そもそも、「なんで使ってみようと思ったのか?」に関して、以下の理由があります。
下の動画のように、作成したクラスがクラス図(簡易バージョン)で表現されるところです。
<HelloWorldの実行動画>

他にも、継承関係も表示できる用ですので、クラス間の関係性を説明するときに、自分で確認するときにも便利です。

ライブラリの追加

スタックオーバーフローより
「Tools -> Preferences -> Libraries -> Add file」ということで。

「ツール→カスタマイズ→ライブラリタブ」を開いて、Jarファイルを追加、BlueJを再起動することで使用可能になるようです。

BlueJインストール

こちらのページからダウンロードできます。MSIファイルなので、クリックするだけでインストールできました。

そして、ドキュメント、サンプルプロジェクトもそろっているようです。

そんなわけで、上記の赤丸部分をクリックしてダウンロードした。ファイルの中身を見てみます。

注意点

コンパイルするときには、修正したクラスを全て、コンパイルする必要があります。

サンプルプロジェクト

ダウンロードしたprojects.zipを展開すると、Chapter01~16までプロジェクトが入っていました。
これらの中にあるフォルダが、BlueJで開くべきプロジェクトになります。

Chapter1のプロジェクト(figure)

開いたプロジェクトは下のような形でした。

これに、メインクラスを作成し、メインメソッドを追加して実行しました。

<実行結果>

サンプルプロジェクトを見る

ダウンロードしたZIPファイルを展開したあと、「projects」-> 「chapter01」-> プロジェクトとフォルダを下がっていく形になります。
具体的には、「projects」-> 「chapter01」-> 「home」を開きます。

プロジェクトを開いた結果

ここから、Javaプログラムの解説になります。

Java プログラム

開いたプロジェクトは、下のようなクラスがあります。

  • Picture
  • Circle
  • Square
  • Triangle
  • Person
  • Canvas

クラスの関係について

上記のクラス図から、Circle, Square, Triangleの3クラスがPictureクラスに呼び出されています。

そして、Picture以外のクラスがCanvasクラスを呼び出しています。

なので、ここに「」クラスを作成し、Picture、Personクラスを起動するメインメソッドを実装、起動します。

Pictureクラス

このクラスは、Circle, Square, Triangleの3クラスを呼び出しています。
詳細は、フィールド変数に上記の3クラスを持っています。
そして、コンストラクタで各クラスのインスタンス化を行い。
draw()メソッドで描画を行っています。

なので、メインメソッドでは、インスタンス化とdraw()を呼び出しを行っています。

/**
 * This class represents a simple picture. You can draw the picture using
 * the draw method. But wait, there's more: being an electronic picture, it
 * can be changed. You can set it to black-and-white display and back to
 * colors (only after it's been drawn, of course).
 *
 * This class was written as an early example for teaching Java with BlueJ.
 * 
 * @author  Michael Kölling and David J. Barnes
 * @version 2016.02.29
 */
public class Picture
{
    private Square wall;
    private Square window;
    private Triangle roof;
    private Circle sun;
    private boolean drawn;

    /**
     * Constructor for objects of class Picture
     */
    public Picture()
    {
        wall = new Square();
        window = new Square();
        roof = new Triangle();
        sun = new Circle();
        drawn = false;
    }

    /**
     * Draw this picture.
     */
    public void draw()
    {
        if(!drawn) {
            wall.moveHorizontal(-140);
            wall.moveVertical(20);
            wall.changeSize(120);
            wall.makeVisible();

            window.changeColor("black");
            window.moveHorizontal(-120);
            window.moveVertical(40);
            window.changeSize(40);
            window.makeVisible();

            roof.changeSize(60, 180);
            roof.moveHorizontal(20);
            roof.moveVertical(-60);
            roof.makeVisible();

            sun.changeColor("yellow");
            sun.moveHorizontal(100);
            sun.moveVertical(-40);
            sun.changeSize(80);
            sun.makeVisible();
            drawn = true;
        }
    }

    /**
     * Change this picture to black/white display
     */
    public void setBlackAndWhite()
    {
        wall.changeColor("black");
        window.changeColor("white");
        roof.changeColor("black");
        sun.changeColor("black");
    }

    /**
     * Change this picture to use color display
     */
    public void setColor()
    {
        wall.changeColor("red");
        window.changeColor("black");
        roof.changeColor("green");
        sun.changeColor("yellow");
    }
}

Mainクラス

このクラスは、筆者が作成したクラスです。単純にPictureクラスとPersonクラスをインスタンス化してそれぞれ描画するメソッドを起動しています。

public class Main
{
  public static void main(String[] args) {
      Picture pic = new Picture();
      Person per = new Person();

      pic.draw();
      per.makeVisible();
  }
}

Extension(拡張)

拡張機能に関して、こちらの記事にありました。

BlueJはSwingを使用して作成されているのか。。。拡張するのにSwingを使用して拡張するようです。

次の例では、ユーザーが開いたすべての BlueJ プロジェクトの名前をログに記録する拡張機能を実装しSystem.out 、他の拡張機能の使用方法を示します。インストールすると、次のようにも表示されます。

  • BlueJ ヘルプメニューの「Installed Extensions」パネルにあるエントリ
  • (あまり役に立たない) BlueJ の [ツール] メニューへのメニュー エントリ、および既存のクラスのソース コードにコメントを追加するものを含む、クラスおよびオブジェクト用に表示されるメニューへのエントリ。
  • Tools/Preferences/Extensions パネルのエントリで、お気に入りの色を入力 (および保存) します。
    拡張機能の完全なソース コードはこちらです。

単体テスト

こちらにあるドキュメントを見るとやり方が書いてあるのですが、ちょっと違うようで。。。

テストクラスを作成する

下の図のように、対象クラスを右クリックします。そして、「テストクラスの作成」を選択します。

すると、下のようなクラスが作成されます。
対象にしているクラスはXMLUtilクラスです。

作成したクラスは、下のように表示されます。

Java はじめて3 〜コメントの書き方〜

イントロダクション

コメントの書き方をやります。プログラムが小さくてすぐに終わるようなものであれば、コメントは不要かもしれませんが。超大作になった場合、コメントがないとわけがわからなくなります。「コードを覚えてればよくね?」と思った方は書かなくても良いでしょう(笑)。あとで苦労するのも良い経験になります。

コメントの書き方

コメントは大きく2種類あります。

  1. JavaDocコメント
  2. プログラム用コメント

このコメントはそれぞれに以下のように記述します。

// プログラム用1行コメント
/*
 * プログラム用複数行コメント
 * こんな感じです。
 */

複数行に渡るコメント、最近はあまり使われないように思える。。。。

/**
 * JavaDocコメント
 * @param メソッドの引数など、IDEを使用すれば自動出力してくれるので楽チンです。
 * @return メソッドの返却値です。
 * @see 参照するものURLやURI(ファイルパスとURLを含む)
 */

最近は知多のような書き方が多いように思えます。

// JavaDocコメント
// @param メソッドの引数など、IDEを使用すれば自動出力してくれるので楽チンです。
// @return メソッドの返却値です。
// @see 参照するものURLやURI(ファイルパスとURLを含む)

JavaDocとコメント

複数行コメントとの違いは、初めのアスタリスクが2個、もしくは1個という違いです。
具体的には、次のよう違いです。

「/**」で始まる→JavaDoc
「/*」で始まる→通常コメント

簡単にはこんな感じです。
これで、クラスファイルの数が1000とか2000になっても大丈夫です(笑)。大規模開発でないとこうはなりませんが。。。

じゃんけんゲームのサンプルコード(リンクはGithubへのものです。)>

ちなみに、クラスファイルを一つでじゃんけんゲームを作成してみました。クラス内に、フィールド変数として
「勝敗判定MAP」などがあります。「final」がついているものは定数です。

今後学びますので、今のうちに軽く眺めておくと理解も早まります。

import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.Scanner;

/**
 * じゃんけんゲームメインクラス。
 * オブジェクト指向プログラミングでないパターンの実装
 *
 * @author 実装者の名前
 *
 */
public class FirstJankenMain {
    /** 勝敗判定フラグ:勝ち(ユーザー) */
    private static final int YOU_WIN = 0;
    /** 勝敗判定フラグ:負け(ユーザー) */
    private static final int YOU_LOOSE = 1;
    /** 勝敗判定フラグ:あいこ(ユーザー) */
    private static final int AIKO = 2;
    /** グー */
    private static final String GU = "0";
    /** チョキ */
    private static final String CHOKI = "1";
    /** パー */
    private static final String PA = "2";

    /** 勝敗判定MAP **/
    private Map<String, Integer> judgeMap;
    /** 手のMAP(追加実装) */
    private Map<String, String> teMap;

    /**
     * メインメソッド
     * @param args プログラム引数
     */
    public static void main(String[] args) {
        // 0.じゃんけんゲーム起動
        FirstJankenMain main = new FirstJankenMain();
        // 1.勝敗判定MAP作成
        main.createJudgeMap();
        Random random = new Random();

        // 追加実装: 「じゃんけん」と「あいこ」の判定が行えてない
        boolean isJanken= true;
        // 無限ループ
        while(true) {
            // 2.「じゃんけん」or「あいこで」のメッセージ表示
            main.printJankenAiko(isJanken);
            // 3.ユーザーの入力(待ち)
            String input = main.acceptInput();
            if (main.inputCheck(input) == false) {
                System.out.println("0-2の値を入力してください。");
                continue;
            }
            // CPUの手を取得する(JavaSEのAPIを使用するのでテストしない)
            String cpuTe = String.valueOf(random.nextInt(2));
            // 4.「ポン!」or「しょ!」を表示
            main.printPonOrSho(isJanken);
            // <追加>
            main.printTe(input, cpuTe);
            // 5.勝敗判定
            int judge = main.judgeWinLoose(input, cpuTe);
            // 6.勝敗判定の表示
            if (main.printJudge(judge)) {
                // 追加実装:「じゃんけん」と「あいこ」の判定が行えてない
                isJanken = true;
                break;
            } else {
                // 追加実装:「じゃんけん」と「あいこ」の判定が行えてない
                isJanken = false;
            }
        }
        // 7.じゃんけんゲーム終了
    }

    /**
     * 1.勝敗判定MAP作成
     */
    private void createJudgeMap() {
        // 勝敗判定MAPのインスタンスを生成する
        judgeMap = new HashMap<String, Integer>();
        // プレーヤーの勝ちケース
        judgeMap.put(GU + CHOKI, YOU_WIN);
        judgeMap.put(CHOKI + PA, YOU_WIN);
        judgeMap.put(PA + GU, YOU_WIN);
        // プレーヤーの負けケース
        judgeMap.put(GU + PA, YOU_LOOSE);
        judgeMap.put(CHOKI + GU, YOU_LOOSE);
        judgeMap.put(PA + CHOKI, YOU_LOOSE);
        // あいこのケース
        judgeMap.put(GU + GU, AIKO);
        judgeMap.put(CHOKI + CHOKI, AIKO);
        judgeMap.put(PA + PA, AIKO);

        // 手のマップ
        teMap = new HashMap<String, String>();
        teMap.put(GU, "グー");
        teMap.put(CHOKI, "チョキ");
        teMap.put(PA, "パー");
    }

    /**
     * 2.「じゃんけん」or「あいこで」のメッセージ表示
     *
     * @param isJanken true: 「じゃんけん」false: 「あいこ」
     */
    private void printJankenAiko(boolean isJanken) {
        // 追加実装、各手と入力値の票を表示する
        System.out.println("****************");
        System.out.println("*グー   = 0    *");
        System.out.println("*チョキ = 1    *");
        System.out.println("*パー   = 2    *");
        System.out.println("****************");
        // isJankenがtrueの時は「じゃんけん」を表示する
        if (isJanken) {
            System.out.println("じゃんけん ...");
        } else {
            System.out.println("あいこで ...");
        }
    }

    /**
     * 3.ユーザーの入力(待ち)
     *
     * @return 0: グー, 1: チョキ 2: パー
     */
    private String acceptInput() {
        // System.in = 標準入力
        Scanner scan = new Scanner(System.in);
        String input = scan.nextLine();
        return input;
    }

    /**
     * 4.「ポン!」or「しょ!」を表示
     */
    private void printPonOrSho(boolean isJanken) {
        if (isJanken) {
            // 「じゃんけん」の場合は「ポン!」
            System.out.println("ポン!");
        } else {
            // 「あいこで」の場合は「しょ!」
            System.out.println("しょ!");
        }
    }

    /**
     * 5.勝敗判定
     *
     * @param playerTe プレーヤーの手
     * @param cpuTe CPUの手
     * @return 勝敗判定 true: プレーヤーの勝ち false: プレーヤーの負け
     */
    private int judgeWinLoose(String playerTe, String cpuTe) {
        // 勝敗判定MAPのキーはプレーヤーの手とCPUの手を連結したもの
        // 例:「01」= プレーヤー「グー」、CPU「チョキ」
        // 勝敗判定マップから勝敗判定結果を取得する。
        String key = playerTe + cpuTe;
        int result = judgeMap.get(key);
        return result;
    }

    /**
     * 6.勝敗判定の表示
     *
     * @param resultJudge 判定結果
     * @return true: 終了 false: もう一度
     */
    private boolean printJudge(int resultJudge) {
        boolean isFinish = true;
        // 勝敗判定結果を表示する
        switch(resultJudge) {
        case YOU_WIN:
            System.out.println("YOU WIN!");
            break;
        case YOU_LOOSE:
            System.out.println("YOU LOOSE!");
            break;
        case AIKO:
            isFinish = false;
            System.out.println("DRAW!");
            break;
        }
        return isFinish;
    }

    /**
     * <追加実装>
     * プレーヤーの手とCPUの手を表示する。
     *
     * @param playerTe プレーヤーの手
     * @param cpuTe CPUの手
     */
    private void printTe(String playerTe, String cpuTe) {
        System.out.println("ユーザー:" + teMap.get(playerTe));
        System.out.println("CPU:" + teMap.get(cpuTe));
    }

    /**
     * <追加実装>
     * じゃんけんの手として適当な値であるか判定する。
     *
     * @param input ユーザー入力
     * @return true; じゃんけんの手として適当な値 / false: じゃんけんの手として不適当な値
     */
    private boolean inputCheck(String input) {
        // 判定フラグ
        boolean isJankenTe = false;
        // 正規表現で判定する
        if (input.matches("[0-2]")) {
            isJankenTe = true;
        }
        return isJankenTe;
    }
}

これを動かすと、このような形で動きます。

でわでわ。。。

<<< 前回  次回 >>>

サイトマップ
ゲームを作りながら覚えるJavaの基本

関連ページ

  1. Java Basic Level 1 〜Hello Java〜
  2. Java Basic Level2 〜Arithmetic Calculate〜
  3. Java Basic Level3 〜About String class〜
  4. Java Basic Summary from Level1 to 5
  5. Java クラスの書き方〜ワンポイントレッスン的な〜


Java Basic 中級編 ~③メインメソッドを修正しないで拡張する~

イントロダクション

前回は、メインメソッドを修正しない形での実装を行いました。

この段階では、まだ役割分担を行っただけでこれがどんな効果があるのか?というところがわからないと思います。

なので、今回は、「〇×あてゲーム」を拡張することを考えていきたいと思います。
作成したプログラムコードはGithubにアップしてあります。

メインメソッドを修正しないで拡張する

今までに「役割分担」を行ったので、この役割分担を活用していきます。
Lv2Mainクラスはメインメソッドを動かすクラスなので、これ以外のクラスを実装していきます。

  • Lv2Main
  • MarubatsuConsole
  • MarubatsuUtils

Lv2Mainを見る

まずは、メインメソッドを見てみます。Lv2Main

/**
 * Javaの基本レベル2:小さなレベルのプログラムを拡張する。
 * 今回は、〇か×か当てるゲーム。
 */
public class Lv2Main {
    /** 終了フラグ */
    private static boolean isFinish;

    public static void main(String[] arg) {
        isFinish = false;
        // 標準入力を受け取るクラスをインスタンス化
        Scanner scan = MarubatsuUtils.getScanner();

        while (true) {
            // ゲーム開始文言
            MarubatsuConsole.printGameStart();
            // 標準入力を受け取る
            int input = scan.nextInt();
            // isFinishがtrueならば処理終了。
            if (isFinish) {
                MarubatsuConsole.printTerminated();
                break;
            }
            // 0か1の値を返却する
            int res = MarubatsuUtils.nextInt(2);

            // 0: 〇 1: ×で当たったかどうかの判定
            boolean isAtari = MarubatsuUtils.judgeAtariOrNot(res, input);
            MarubatsuConsole.printAtatiorNot(isAtari, input);

            // 続けるのかどうか判定する
            if (MarubatsuConsole.printNextPlayOrNot(scan)) {
                break;
            }
        }
    }
}

コメントを並べただけですが、以下の順序で処理を行っています。

  1. 標準入力を受け取るクラスをインスタンス化
  2. ゲーム開始文言
  3. 標準入力を受け取る
  4. isFinishがtrueならば処理終了。
  5. 0か1の値を返却する
  6. 0: 〇 1: ×で当たったかどうかの判定
  7. 続けるのかどうか判定する

上の番号で行くと2~7がループ処理の中にあります。
なので、7の「続けるか判定する」部分でNo(続けない)を選択するまで無限ループします。

拡張ポイントを見つける

そして、役割分担を行ったところ、つまりは、下のクラスを使用している部分が拡張ポイントになります。
Githubにアップしてあります。

具体的には、以下のコメント部分です。

  • ゲーム開始文言
  • isFinishがtrueならば処理終了。の終了表示部分
  • 0か1の値を返却する
  • 0: 〇 1: ×で当たったかどうかの判定
  • 続けるのかどうか判定する

もしも、他の部分を拡張したいと思たのならば、メインメソッドを少し修正する必要があります。
何かの処理をほかのクラスに任せてある状態(MarubatsuConsole, MarubatsuUtilsを使用している状態)ならば
メインメソッドを修正する必要がありません。
しかし、直接メインメソッドを修正する必要がある場合はやはり、修正する必要があります。

このような実装方法を理解すると、下のようなプログラムが作れます。

※ビルドするのに3分くらい時間がかかっています。
このプログラムは、メインの処理部分も、別クラスにしているので、まったく別のプログラムを起動するように修正することもできます。
しかし、現状では、テキストRPGを作成するところに注力しているので役割分担を行いそれぞれの役割を拡張して実装しています。
具体的には、テキストファイルを読み、それをもとにデータ(ステータスやアイテム)を生成してそれをゲームの中で使用する形での実装を行っております。
ちょっと残骸が残っていますが。。。

拡張するとき

先に示したように、別のクラスを呼び出している(使用している)部分を拡張、つまり処理を追加することができるので、次の部分を拡張することができます。

  • ゲーム開始文言
  • isFinishがtrueならば処理終了。の終了表示部分
  • 0か1の値を返却する
  • 0: 〇 1: ×で当たったかどうかの判定
  • 続けるのかどうか判定する

具体的には、ゲーム開始文言を変更することができる。とか、当たったかどうかの履歴を付ける。とか
そこらへんは、実装者のアイディア次第でどこまでも広げることができます。

ゲームの開始文言を変更する

先ほど、拡張する例としてゲームの開始文言を変更するということを上げました。これを実際にやってみます。

やっている手順としては、以下の通りです。

  1. JUnitテストケースの作成
  2. 現状のプログラム実行確認
  3. プログラムの修正、実行確認(目視で確認)

最後の実行確認(目視で確認)に関しては、すべてプログラムで確認することができます。
そのためには、ちょっと面倒なことをする必要があるので、割愛しました。
具体的には標準出力の出力先を変更して(別のPrintStreamを使用)そのストリーム内の出力内容と期待値を比較するという形になります。

現状としては、単純に〇×あてゲームの初期表示文言を変更するだけなので、目視で確認しました。

0: 〇 1: ×で当たったかどうかの判定

この部分は上記の動画では、挙動がおかしくなっていました。なので、これを修正し想定通りの実行結果が得られるようにプログラムを修正します。

実行した結果は下のようになります。

******************************
*「〇×あてゲーム」 0: 〇 1: ×。 *
******************************
※0は「〇」を表し1は「×」を表します。
1
はずれ:×
続けますか? 0: 続ける  1: やめる
0
******************************
*「〇×あてゲーム」 0: 〇 1: ×。 *
******************************
※0は「〇」を表し1は「×」を表します。
1
はずれ:×
続けますか? 0: 続ける  1: やめる
1

その前に、問題点を明確にします。

今回の問題点は「あたりのときの値、つまりは、『〇』が当たりなのか?『×』が当たりなのか?」が明確でないというところです。
なので、これも修正します。

先ほどと同じようにまずは、修正ポイント(先ほどは拡張ポイントと記載しました。)を見つけます。
実行結果を見ると、「はずれ:×」のように当たりはどちらなのか?がわからない状態ですので、これを明確にします。

具体的には、「「〇」があたりです。「×」が当たりですなどの文言を表示するように修正」する形で対応しようと考えております。

当然、他に良いアイディアがあれば、それを実装するとよいと思います。

当たり判定処理の修正

今回作成した「当たり判定処理」は、MarubatsuUtils#judgeAtariOrNotで実装しているのでこれを修正します。

現状のプログラムは下のようになっています。

    /**
     * 生成した乱数と、入力値が等しいか判定する。
     *
     * @param res 生成した乱数
     * @param input 入力値
     * @return true: 等しい false: 違う
     */
    public static boolean judgeAtariOrNot(int res, int input) {
        // 0: 〇 1: ×で当たったかどうかの判定
        return res == input;
    }

このプログラムの問題点は、〇と×のどちらが当たりなのか表示されない点です。
なので、これを表示するようにプログラムを修正する必要があります。

テストケースを作成する

先ほど初期表示の文言を表示するテストケースを作成しました。
これに追加して、「〇と×のどちらが当たりなのか表示する」テストケースを実装します。

仕様から考える

今回の要件(どのように動いたらよいか?)は「〇と×のどちらが当たりなのか表示する」ということです。
これを確かめるプログラムを考えます。

確認項目をリストアップ

  1. あたりは「〇」「×」どちらか表示する
  2. 予想を入力したユーザーの入力は「〇」「×」どちらか表示する

簡単ですが、2項目になります。

これもテストケースを作成する

ちなみに、このクラスのテストケースは以前、他のテストケースを作成していたので、これに今回のテストケースを追加します。

やったことは、同じです。

  1. 現状のプログラム実行確認
  2. プログラムの修正
  3. プログラムの実行確認

ちょっと長めの動画になりましたが、行ったことをそのまま動画にしてあります。
ポイントとしては、テストケースのプログラムの実装、作成方法、考え方(この記事に記載)を実際に行った
動画にしてあります。
別な言い方をすると、自分がこの作業を行ったものを動画にしました。

でわでわ。。。

Java Basic 中級編 ~②メインメソッドを修正しない形を作る~

イントロダクション

前回アプリケーションを作り、それを運用すること、拡張することについて考えてみました。

まとめると次のようなことが必要になるということを記載しました。

  • 各プログラム間(Javaのクラス同士)の依存度を限りなく低くする。
  • プログラムを拡張するのに、元のプログラムをほとんど変更しない。
  • Javaを使えるレベルの知識(技術)がある人なら、誰が見てもわかるようなコードを書く。
  • 各クラス(部品)の単体テスト(UnitTest)ケースを用意しておき、修正したら即テスト、部品を取り換えるだけでよいように、作成した資源(プログラムのコード)を結合テスト・総合テストと実施できるような体制を整える

今回は、プログラムを拡張していくための基本になる考え方と実践方法について記載していきたいと思っています。
例として「〇×あてゲーム」を拡張していく形でプログラム(クラス)間の依存度を低くした形のプログラミング方法について記載していきたいと思います。

メインメソッドを修正しない形を作る

前回作成したプログラムは下のものになります。

public class Lv2Main {
    /** 終了フラグ */
    private static boolean isFinish;

    public static void main(String[] arg) {
        isFinish = false;
        // 標準入力を受け取るクラスをインスタンス化
        Scanner scan = new Scanner(System.in);

        while (true) {
            System.out.println("「〇×あてゲーム」 0: 〇 1: ×。");
            // 標準入力を受け取る
            int input = scan.nextInt();
            // isFinishがtrueならば処理終了。
            if (isFinish) {
                System.out.println("プログラムを終了します。");
                break;
            }
            // 0か1の値を返却する
            Random rdm = new Random();
            int res = rdm.nextInt(1);

            // 0: 〇 1: ×で当たったかどうかの判定
            boolean isAtari = res == input;
            String value = input == 0 ? "〇" : "×";
            if (isAtari) {
                System.out.println("あたり:" + value);
            } else {
                System.out.println("はずれ:" + value);
            }
            System.out.println("続けますか? 0: 続ける  1: やめる");
            int next = scan.nextInt();
            if (next == 1) {
                break;
            }
        }
    }
}

この状態のプログラムはよく見かける。。。と思われる形のプログラムです。
つまりは、処理を1つのファイル内にすべて書いている形のプログラムということです。

このブログ的に表現すると「Javaの基本:上巻」に当たるプログラムの書き方になります。

「Javaの基本:下巻」に当たるプログラムの書き方はこれから説明していきます。

Step1. 役割分担を行う

上のプログラムで行っていることを、例えばチームで作業をするように、役割分担を行いそれぞれのクラスにそれぞれの役割を与えます。

例えば、次のような役割分担を行います。

  1. 〇×あてゲームを起動する役割
  2. 標準入力を受け取る、などの標準入出力をコントロールする役割
  3. 〇×あてゲームの各種判定を行う役割

もともとのプログラムは、全部の処理が書いてあるので、これを切り貼りして改造します。

1-2. クラスを作成する

上記の通り、2つの役割を担当するクラスを作成します。

  1. 〇×あてゲームを起動する役割:Lv2Main ※作成済み
  2. 標準入力を受け取る、などの標準入出力をコントロールする役割: MarubatsuConsole
  3. 〇×あてゲームの各種判定、ユーティリティの提供を行う役割: MarubatsuUtils

これらのクラスに必要な処理を切り貼りして、各クラスに移植します。
その結果を以下に記載致します。

※リンクはGithubにアップしたコードです。
Lv2Main

package jp.zenryoku.tutorial.level2;

import java.util.Scanner;

/**
 * Javaの基本レベル2:小さなレベルのプログラムを拡張する。
 * 今回は、〇か×か当てるゲーム。
 */
public class Lv2Main {
    /** 終了フラグ */
    private static boolean isFinish;

    public static void main(String[] arg) {
        isFinish = false;
        // 標準入力を受け取るクラスをインスタンス化
        Scanner scan = MarubatsuUtils.getScanner();

        while (true) {
            // ゲーム開始文言
            MarubatsuConsole.printGameStart();
            // 標準入力を受け取る
            int input = scan.nextInt();
            // isFinishがtrueならば処理終了。
            if (isFinish) {
                MarubatsuConsole.printTerminated();
                break;
            }
            // 0か1の値を返却する
            int res = MarubatsuUtils.nextInt(2);

            // 0: 〇 1: ×で当たったかどうかの判定
            boolean isAtari = MarubatsuUtils.judgeAtariOrNot(res, input);
            MarubatsuConsole.printAtatiorNot(isAtari, input);

            // 続けるのかどうか判定する
            if (MarubatsuConsole.printNextPlayOrNot(scan)) {
                break;
            }
        }
    }
}

MarubatsuConsole>※修正した後のコードがアップしてあります。

package jp.zenryoku.tutorial.level2;

import java.util.Scanner;

public class MarubatsuConsole {
    /**
     * 〇×あてゲームの開始文言を表示
     */
    public static void printGameStart() {
        System.out.println("「〇×あてゲーム」 0: 〇 1: ×。");
    }

    /**
     * 〇×あてゲームの終了文言を表示
     */
    public static void printTerminated() {
        System.out.println("プログラムを終了します。");
    }

    /**
     * 当たったかどうかを表示する。
     * @param isAtari 当たり判定の結果
     */
    public static void printAtatiorNot(boolean isAtari, int input) {
        String value = input == 0 ? "〇" : "×";
        if (isAtari) {
            System.out.println("あたり:" + value);
        } else {
            System.out.println("はずれ:" + value);
        }
    }

    /**
     * 〇×あてゲームを続けるかを表示、Yes or Noを取得する
     * @param scan
     * @return true: 続ける  false: やめる
     */
    public static boolean printNextPlayOrNot(Scanner scan) {
        boolean playNext = false;
        System.out.println("続けますか? 0: 続ける  1: やめる");
        int next = scan.nextInt();
        if (next == 1) {
            playNext = true;
        }
        return playNext;
    }
}

MarubatsuUtils>※修正した後のコードがアップしてあります。

package jp.zenryoku.tutorial.level2;

import java.util.Random;
import java.util.Scanner;

public class MarubatsuUtils {
    /** 標準入力を受け取るクラス */
    private static Scanner scan;
    /** 乱数の生成クラス */
    private static Random rdm;

    /**
     * 標準入力を受け取るクラスを生成、取得する。
     * ※シングルトン実装
     * @return Scanner
     */
    public static Scanner getScanner() {
        if (scan == null) {
            scan = new Scanner(System.in);
        }
        return scan;
    }

    /**
     * 乱数生成クラスがインスタンス化されていなければ、インスタンス化します。
     * すでにインスタンス化しているときは、既存のインスタンスを使用します。
     * ※シングルトン実装
     *
     * @param bound
     * @return 生成した乱数
     * @see <a href="https://docs.oracle.com/javase/jp/8/docs/api/java/util/Random.html">Random</a>
     */
    public static int nextInt(int bound) {
        if (rdm == null) {
             rdm = new Random();
        }
        return rdm.nextInt(bound);
    }

    /**
     * 生成した乱数と、入力値が等しいか判定する。
     *
     * @param res 生成した乱数
     * @param input 入力値
     * @return true: 等しい false: 違う
     */
    public static boolean judgeAtariOrNot(int res, int input) {
        // 0: 〇 1: ×で当たったかどうかの判定
        return res == input;
    }

}

メインメソッドは、すっきりしたコードになったと思います。

メインメソッドでは、どんな処理をしているかが一目瞭然になり。各クラスはその処理だけが書いてある形になります。

もちろん、引数と返り血があるので、呼び出し元に多少なりとも影響が出ます。

しかし、これでメインメソッドを変更しなくても〇×あてゲームを拡張する準備ができました。

ポイント

メインメソッドには、大まかな処理の順序を書く。具体的には下のような形です。

// <無限ループ開始>
// ■ゲーム開始文言を表示
// ■標準入力を受け取る
// ■isFinishがtrueならば処理終了。
// ■0か1の値を返却する
// ■0: 〇 1: ×で当たったかどうかの判定
// ■続けるのかどうか判定する

<やったこと>

  1. これらの処理をはじめの状態では、すべてLv2Mainクラスに記述していましたが、これをクラス別に分けました。
  2. 作成した各クラスをメインメソッドで呼び出して実行する。この時に初めの動きと変わらないことを確認しました。

これにより、プログラムのコードがすっきりして(したと自分は思います。。。)、メインメソッドを修正しなくても次の部分の処理が、修正が可能になりました。

1. ■ゲーム開始文言を表示: MarubatsuConsoleの修正
2. ■標準入力を受け取る: MarubatsuUtilsの修正
3. ■0か1の値を返却する: MarubatsuUtilsの修正
4. ■0: 〇 1: ×で当たったかどうかの判定: MarubatsuUtilsの修正
5. ■続けるのかどうか判定する: MarubatsuConsoleの修正

「そのように作ったんだから当たり前だろ?」と思った方、正常でございます。
このような作り方が、クラスを拡張する、アプリケーションを拡張するときに、役立つのです。

今回の「〇×あてゲーム」のような小さなレベルのプログラムはファイル一枚で何も問題はありませんが、会社の業務で使用するような大きなアプリケーションでは、いろんな機能が必要なので、役割分担を行い、機能拡張がしやすい形で、プログラムを組んでいきます。

拡張する準備をする

初めに「必要になること」について記載したのですが、触れていない部分があります。次の部分です。

  • 各クラス(部品)の単体テスト(UnitTest)ケースを用意しておき、修正したら即テスト、部品を取り換えるだけでよいように、作成した資源(プログラムのコード)を結合テスト・総合テストと実施できるような体制を整える

具体的にどのようなことか?これについて、記載したいと思います。

JavaならばJUnit

JavaのテスティングフレームワークといえばJUnitです。
具体的に、どのように使うか記載したいと思います。

役割分担をしたクラスのテストクラス作成

作成した各クラスのテストケースを作成するためのクラスを作成します。
クラス名は、「対象のクラス名 + Test」の形で作成します。

  • MarubatsuConsole: MarubatsuConsoleTest
  • MarubatsuUtils : MarubatsuUtilsTest

作成したコードは次の通りです。ただし、MarubatsuConsoleTestは、標準出力の内容を取得して。。。とちょっと面倒なので、今回は実装しません。

<JUnit実行結果>

package jp.zenryoku.tutorial.level2;

import org.junit.jupiter.api.Test;

import java.util.Scanner;

import static org.hamcrest.core.Is.is;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.matchers.JUnitMatchers.either;

public class MarubatsuUtilsTest {
    /** テスト対象クラス(全てstaticメソッドなのでインスタンス不要) */
    private static MarubatsuUtils target;

    @Test
    public void testGetScanner() {
        Scanner scan = MarubatsuUtils.getScanner();
        // インスタンスが取得できていることを確認
        assertNotNull(scan);
    }
    @Test
    public void testNextInt() {
        int res = MarubatsuUtils.nextInt(2);
        // インスタンスが取得できていることを確認
        boolean isRondom = res == 0 || res == 1;
        assertTrue(isRondom);
    }

    @Test
    public void testIsAtari() {
        assertTrue(MarubatsuUtils.judgeAtariOrNot(0, 0));
        assertTrue(MarubatsuUtils.judgeAtariOrNot(1, 1));

        assertFalse(MarubatsuUtils.judgeAtariOrNot(0, 1));
        assertFalse(MarubatsuUtils.judgeAtariOrNot(1, 0));
    }
}

でわでわ。。。

Java Basic 中級編 ~①アプリケーション設計を考える~

イントロダクション

Javaの基本文法が理解できたところで、実際に動くものを作ってみたいと思うのが人情だと思います。
基本文法だけでも、簡単なプログラムを作ることができます。
例えば、下のようなものです。

単純なゲームループ

1. アプリケーションを作るために

今までに、Javaの基本を学習してきました。大まかに次のようなことを学習しました。

  1. メインメソッドを動かすこと
  2. Stringやintなどのデータ型があり、それぞれに意味があること
  3. 条件分岐、繰り返しなどの文法(if文、switch, while, forなど)
  4. 自分で作ったクラスもデータ型として宣言することが出来て、呼び出す(動かす)事ができること
  5. JavaAPIで提供しているクラス(java.lang.Math, java.util.Scannerなど)を使用できること

これらのことを、このブログでは「Java Basic」と呼んでいました。

この基本に対して1段上の領域があります。それは「オブジェクト指向」と呼ばれている考え方なのですが、これの解釈が人によって千差万別なので、この言葉は使わないようにしようと考えております。

2. アプリケーションを作り運用することも考える

どちらかといえば、「エクストリーム・プログラミング」のほうが近いように思います。

エクトリーム・プログラミングに関しては上記のリンク先を参照してください。
早い話が、次のようなものです。

ソフトウェア品質 を向上させ、変化する顧客の要求への対応力を高めることを目的としたソフトウェア開発プロセスである。アジャイルソフトウェア開発の一つとして、短い開発サイクルで頻繁に「リリース」することを推奨することで、生産性を向上させ、新しい顧客の要求を採用するためのチェックポイントを導入することを意図している。

これを実現するために、次のようなことが求められます。

  • 各プログラム間(Javaのクラス同士)の依存度を限りなく低くする。
  • プログラムを拡張するのに、元のプログラムをほとんど変更しない。
  • Javaを使えるレベルの知識(技術)がある人なら、誰が見てもわかるようなコードを書く。
  • 各クラス(部品)の単体テスト(UnitTest)ケースを用意しておき、修正したら即テスト、部品を取り換えるだけでよいように、作成した資源(プログラムのコード)を結合テスト・総合テストと実施できるような体制を整える

個人で実現するのは、結構な苦労ですが、一人でアプリのリリースまでやろうと考えるならやっておきたいところです。

3. 基本が大前提

Javaでなくてもそうですが、基本ができないと応用はできません。
基本というのは、上記でいうところの「Java Basic」は、たとえて言うならば、「Javaの基本」という本の上巻に当たります

「じゃ、実際どの部分が基本なの?」という疑問が出ると思います。
これは、自分の見解ですが、下のように考えています。

つまりは、Javaだけでなくプログラミングの基本には次のような段階があると思うということです。

  • 「Javaの基本:上巻」は小さなレベルのプログラム(アプリケーション)が作れるレベルの基本。
  • 「Javaの基本:下巻」は小さなレベルのプログラムを拡張して、どんどん新しい機能を追加していけるレベルの基本

今後は、「Javaの基本」という本の下巻に当たる部分を学習します。

今までの学習方法(古い時代の学習方法だと思います。。。)は、とりあえず何かしらのアプリケーションを組み続けてそこから、自分で上にも書きましたが、次のことを理解していきました。

  • 各プログラム間(Javaのクラス同士)の依存度を限りなく低くする。
  • プログラムを拡張するのに、元のプログラムをほとんど変更しない。
  • Javaを使えるレベルの知識(技術)がある人なら、誰が見てもわかるようなコードを書く。
  • 各クラス(部品)の単体テスト(UnitTest)ケースを用意しておき、修正したら即テスト、部品を取り換えるだけでよいように、作成した資源(プログラムのコード)を結合テスト・総合テストと実施できるような体制を整える

これらのようなことを理解するのには、自分の場合、大体3年くらいかかりました。
あくまでも、基本が理解できたというレベルです。

そこから、このような基本を応用し、実践していくことが必要になりますが、それこそは実際にやってみるしかありません。

基本ができたら、そこから先は自分の作りたいものをどんどん作っていくということが最大の課題になると思います。自分も時間を見つけてやっています。いまだに完成までいかないものがあります。。。

やはり、「モチベーションの維持と、健康の維持」が大きな課題となります。

4. 小さなレベルのプログラムを拡張する

先ほどから、「小さなレベルのプログラムを拡張する」と記載していますが、これは一体どういうことなのか?これについて、記載したいと思います。

基本はプログラムを修正しない

小さなレベルのプログラムを拡張するときに、なるべくプログラムを修正しないようにプログラムを組んでいくというところが、このレベルでの基本になります。
それには、クラスとクラスの関係をうまく作るということがカギになるのですが、具体的なコードを見ていくほうが早いので、サンプルコードを見ていきましょう。

〇×あてゲーム

単純なアプリケーション「〇×あてゲーム」を作成しました。下のようなコードで動きました。
※コードはGithubにあります

/**
 * Javaの基本レベル2:小さなレベルのプログラムを拡張する。
 * 今回は、〇か×か当てるゲーム。
 */
public class Lv2Main {
    /** 週r等フラグ */
    private static boolean isFinish;

    public static void main(String[] arg) {
        isFinish = false;
        // 標準入力を受け取るクラスをインスタンス化
        Scanner scan = new Scanner(System.in);

        while (true) {
            System.out.println("「〇×あてゲーム」 0: 〇 1: ×。");
            // 標準入力を受け取る
            int input = scan.nextInt();
            // isFinishがtrueならば処理終了。
            if (isFinish) {
                System.out.println("プログラムを終了します。");
                break;
            }
            // 0か1の値を返却する
            Random rdm = new Random();
            int res = rdm.nextInt(2);

            // 0: 〇 1: ×で当たったかどうかの判定
            boolean isAtari = res == input;
            String value = input == 0 ? "〇" : "×";
            if (isAtari) {
                System.out.println("あたり:" + value);
            } else {
                System.out.println("はずれ:" + value);
            }
            System.out.println("続けますか? 0: 続ける  1: やめる");
            int next = scan.nextInt();
            if (next == 1) {
                break;
            }
        }
    }
}

次回

このプログラムをなるべく修正しなくても機能拡張できるように修正していきます。
現状のプログラムは、修正しようとしたら必ずメインメソッドを修正する必要があります。これを解消するために試行錯誤します。

でわでわ。。。

Java SpringBoot + Thymeleaf 画面を作成する ~ControllerからHTMLまで~

イントロダクション

前回はSpringBootでの開発セットアップを行いました。

前回の状態では、エラーページしか確認できませんでした。

これは、サーバーが起動していることを確認するという意味で、必要な確認なのですが。。。動かないのでは面白くありません。

SpringBoot + Thymeleafで画面を作る

毎度おなじみ、「ハローワールド」をやってみようと思います。

作成するもの

  1. Javaファイル(コントローラークラス)
  2. HTMLファイル(Thymeleaf使用)

コントローラークラスを作る

まずは、作成して動かすことに注力します。
プロジェクトを作成したら初めに「SampleWebAppApplication」というクラスが作成されていると思います。

このクラスがアプリケーションを起動するクラスです。しかし、このクラスはいじる必要がありません

Controllerがアプリを動かすために

自動生成された「SampleWebAppApplication」はアプリケーションを起動するためのクラスなので、本当にアプリを起動するだけなのです。

起動確認するときに、エラーページしか出ないのは表示するものが何もないからです。

それでは、早速コントローラークラスを作成します。まずは、ルールがあるのでそれに注意します。
先ほど自動生成されたクラス「SampleWebAppApplication」はパッケージ「com.example.demo」というパッケージに作成されています。

パッケージはウィンドウズでいうところの「フォルダ」に相当します。上に貼り付けているものは、パッケージを表示していいます。

一番上にある「src/main/java .... 」の部分はフォルダ構成上下のようになっています。

  1. プロジェクトのフォルダがあります。
  2. その中に、srcというフォルダがあります。
  3. 同様にmainというフォルダ
  4. javaというフォルダ
  5. そして、ここからが「パッケージ」と呼ばれるフォルダになります。「com」フォルダ
  6. 「example」フォルダ
  7. 「demo」フォルダ

このようにフォルダ構成が出来上がってるのですが、これを「パッケージ」と呼び下のように表現することで、どのファイルのクラスを参照するかわかるようにしています。
Javaのプログラムでいうと「インポート文」がそれにあたります。

import con.example.demo.*;

上のインポート文は「con.example.demo」パッケージにあるクラスを全てインポートするという意味です。

Controllerのみで動かす

パッケージに関して意味が分かったところで、上記で記載した「ルール」に関してお話ししたいと思います。
自動生成されたクラス「SampleWebAppApplication」よりも下のパッケージ内にコントローラーなどの読み込むクラスを作成するというルールがあります。

なので、今回作成するコントローラークラスはパッケージ「com.example.demo.controller」に作成することにします。
パッケージの作成方法は下のように行います。

  1. 作成するパッケージの親になるパッケージを右クリック※com.example.demoパッケージを右クリックしました。
  2. そして、「パッケージ」という文言が見つかればよいのですが、残念ながら表示されていませんので「その他」をクリックします。すると下のようなウィンドウが開きます。
  3. そこでパッケージを選択してやれば作成できるというところです。

さらに、ここからクラスも作成します。手順は上と同じですが、選択するものが「クラス」になります。

そして、ここでは「HelloController」というクラスを作成します。

package com.example.demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.ModelAndView;

@RestController
public class HelloController {

    @RequestMapping("/")
    public String index(ModelAndView mav) {
        return "Hello World";
    }
}

<表示結果>

ここでは、HTMLの作成は必要がありません。

実は、上記の「@RequestMapping」アノテーションを付けたメソッドは返却文字列を指定した場合はHTMLになります。なので下のようにコードを変更してやると。。。

@RequestMapping("/")
public String index(ModelAndView mav) {
    return "<html>"
            + "<title>Hello World</title>"
            + "<body><h1>こんにちは</h1>"
            + "<p>~PGボックスより~</p>"
            + "</body>"
            + "</html>";
}

<表示結果>

このように表示できます。しかし、「もっとオシャレに!」「もっと機能的に!」ということを考えるとHTMLファイルを使用して動かした方が効率的です。

もちろん、ウェブデザイナーさんの協力を頂き、その実力を発揮してもらうならナチュラルなHTMLがベストです。JSPなどは専門外のはず。。。

HTMLを作成する

そこで「Thymeleaf」というフレームワークを使用します。これはコントローラーとHTMLを橋渡しするためのフレームワークです。
具体的に見ていきましょう。まずはHTMLをみてみましょう。

<!DOCTYPE html>
<html>
<head lang="ja" xmlns="http://www.thymeleaf.org">
    <meta charset="UTF-8">
    <title>Sample Index Page</title>
</head>
<body>
    <h1>Recommended index</h1>
    <p>おススメの漫画・アニメを検索できます。</p>

</body>
</html>

上のようなHTMLを作成します。この状態ではコントローラーとのやり取りがない状態です。
しかし、まずはこの状態で動かしてみます。作成したHTMLファイルは「src/main/resources/templates」フォルダの下に作成しましょう。

そして、コントローラーも書き換えます。

@RequestMapping("/")
public ModelAndView index(ModelAndView mav) {
    mav.setViewName("book");
    return mav;
}

<実行結果>

このように動いたところで、コントローラーから値を渡すことを考えます。

コントローラーからHTMLへ。。。

今回のJavaプログラムはサーバーサイド・プログラムなので(※クライアントでも動かすことができます。)サーバー側での処理を行うことができますが、クライアント(ブラウザ上)でのプログラムはJSとかHTMLに任せることになります。

なので、サーバーでの処理結果をクライアントに渡すにはちょっとテクニックが必要になります。
具体的には、以下の通りです。

  1. リクエストパラメータを使用する
  2. レスポンスに処理結果を埋め込む(渡す)

こんなことを言われても「???」となってしまう人は正常です。ご安心ください。

具体的に下のようなコードで処理の結果を渡します。プログラムは微妙に修正しています。
<Java>

@RequestMapping("/")
public ModelAndView index(ModelAndView mav) {
    mav.setViewName("book");
    mav.addObject("key", 12);
    return mav;
}

<HTML>

<!DOCTYPE html>
<html>
<head lang="ja" xmlns="http://www.thymeleaf.org">
    <meta charset="UTF-8">
    <title>Sample Index Page</title>
</head>
<body>
    <h1>Recommended index</h1>
    <p>おススメの漫画・アニメを検索できます。</p>
    <p th:text="${key}">ここに値が入ります。</p>

</body>
</html>

<実行結果>

赤枠で囲った部分がJava側からHTMLへ渡した値です。

とりあえずは、ここまでに致します。

でわでわ。。。

<<< 前回

Java SpringBoot セットアップ ~Eclipse All in Oneでやると早い~

SpringBootの開発 セットアップ

SpringBootのセットアップを行うのに、いろいろな手順があり、混乱するので(自分の行った中で)もっとも簡単な方法を記載します。

使用するIDE(開発ツール)はEclipseのAll in Oneです。

大まかな手順

  1. プロジェクトを作成するときにSpringBootのスタータープロジェクトを作成する
  2. プロジェクト作成時に、ThymeleafとDB(H2DB)とSpringWebを追加する
  3. プロジェクト作成後にPOMファイルを「Maven install」で実行する
  4. Mavenプロジェクトの更新

プロジェクトの作成

Eclipseを起動してから、下のようにプロジェクトを右クリックし「新規」をクリック以下の手順に従います。

  1. Springスタータープロジェクトを選択し、「次へ」をクリック

  2. プロジェクトの名前などを入力する。使用するJDKによって「Javaバージョン」の値を変更する
     ※JDK1.8であれば「8」を選択する

  3. 使用するDBやフレームワーク(ツール)を選択する、今回はDBに「H2DB」、画面の作成に「Thymleaf」を使用するのでそれぞれのチェックボックスにチェックを入れる
    そして、Spring Webにチェックを入れる

  4. 最後に完了を押下する

※画像には「MySampleWebbApp」とあったが、「SampleWebApp」プロジェクトを作成した形で手順を進めます。

作成したプロジェクトは下のような形でEclipseのプロジェクトエクスプローラー、もしくは、パッケージエクスプローラーに表示されるはずです。
作成後のプロジェクト名が「MySampleWebbApp」になっていますが、「SampleWebbApp」での作成も確認しています。

プロジェクトを作成したばかりの状態では、バックグラウンド処理で何かしらのインストールが走っているので、ちょっと待ちます。

最後にプロジェクトを右クリック、実行、Spring Bootアプリケーションをクリック
これだけで、セットアップは完了です。

そして、ブラウザを開き、http://localhost:8080 にアクセスします。
ここでは、エラーページしか出ませんが、Tomcat(Webサーバー)が動いていることを確認するのが目的なのでこれでよいのです。

Eclipseでウェブ開発を始める~Java Servlet 二歩目 Servletを動かす~

イントロダクション

前回はEclipseを使用して、JavaServletの起動環境を作成しました。

観光構築だけで、起動確認はしていなかったので、これを確認するところから始めます。

Servletを動かす

前回作成したサーブレットは下のようなもの(コード)です。

/**
 * Servlet implementation class SrvLesson1
 */
@WebServlet("/SrvLesson1")
public class SrvLesson1 extends HttpServlet {
    private static final long serialVersionUID = 1L;

    /**
     * @see HttpServlet#HttpServlet()
     */
    public SrvLesson1() {
        super();
    }

    /**
     * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        response.getWriter().append("Served at: ").append(request.getContextPath());
    }

    /**
     * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
     */
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }
}

これを実行すると、下のような表示ができます。

「?????」と表示されているのは、文字コード(charset)が指定されていないからです。
これを指定するには下の王なコードを書きます。

response.setContentType("text/html; charset=UTF-8;");

そして、実行結果は下のようになります。

Tomcatについて

TomcatはWebコンテナ、APサーバーなどと呼ばれる分類に入る「サーバー」です。
サーバーは複数個所から、アクセスを受けて、それぞれのリクエスト(入力やボタン押下)に対する処理を行い、レスポンスという形で処理の結果をクライアントに返します。

絵にすると下のような感じです ※画力が。。。

コード解説

JavaServeltの実装にはいくつかのポイントになる部分があります。

  1. クライアントとサーバーの関係がある。
    クライアント(ブラウザ(IEやChrome, FireFox, Safariなど))とサーバー(Javaで作成したサーブレットが動くTomcatのようなアプリケーション)。クライアントはサーバーにリクエストを送信、サーバーはクライアントへレスポンスを送信する。

  2. HTMLのFormタグにある「action」属性にサーブレットを指定する。
    action="SukiriWeb2/SrvLesson1"などのように指定すると対象のURLに指定したサーブレットが起動する。

  3. レスポンスは、HTMLでブラウザに表示される。

コード内容

リクエストの種類には「GET」と「POST」があり、サーブレットに実装(オーバーライド)している「doGet()」と「doPost()」がそれぞれに対応する。
つまり。GETリクエストの時は「doGet()」POSTリクエストの時は「doPost()」が動く。

それぞれの処理の最後に「response.getWriter()」とあるが、これは返却するレスポンスに、返却する内容を追加するためのオブジェクトを取得するということです。

具体的には、PrintWriterクラスですが、このクラスにHTMLを書きこむことで、ブラウザに表示するHTMLを返却することができます。

つまりは、そのような仕組みになっている(フレームワーク化されている)ということです。

コードをいじってみる

返却する値を作成しているのは、サンプルにあるコードは下のようなものです。

StringBuilder build = new StringBuilder();
build.append("<!doctype html>");
build.append("<html>");
build.append("    <head>");
build.append("        <title>aaa</title>");
build.append("<body>はじめのサーブレット</body></html>");

そして、HTMLを返却するだけでなく今まで学習してきた処理を行うこともできます。

try {
    PreparedStatement pre = H2dbManager.getInstance().createPreparedStatement(SQLConst.INSERT_USER_TABLE);
    pre.setString(1, userName);
    pre.setString(2, passwd);
    pre.setInt(3, 1);
    if (pre.execute()) {
        throw new SQLException("ユーザー登録に失敗しました。");
    }
} catch (SQLException e) {
    // TODO 自動生成された catch ブロック
    e.printStackTrace();
    return false;
}

「一度書けばどこでも動く」というのがJavaのウリです(笑)

上の処理は、H2DBへのアクセスを行うための「H2dbManager」クラスを作成したので、そこからSQLを発行するために以下のようなことを行っています。

  1. PreparedStatementクラスを取得
  2. 実行するSQLの設定(パラメータを設定できるようにしている)
  3. パラメータの設定
  4. SQLの実行

ユーザー登録の処理です。詳細に関しては、以下に記載します。

H2DBManagerクラス

/**
 * プライベートコンストラクタ、H2DBのコネクションを取得する。
 * {@link H2dbManager#getInstance()}
 */
private H2dbManager() {
    try {
        Class.forName("org.h2.Driver");
        // 「~」はマイドキュメントを示す。
        con = DriverManager.getConnection("jdbc:h2:tcp://localhost/~\\workspace\\SukiriWeb2\\WebContent\\WEB-INF\\database");
        Statement stmt = con.createStatement();
        stmt.execute(SQLConst.CREATE_USER_TBL);
        stmt.execute(SQLConst.CREATE_ITEM_TBL);
        stmt.execute(SQLConst.CREATE_ITEM_TYPE_TBL);
    } catch (SQLException e) {
        e.printStackTrace();
        System.exit(-1);
    } catch (Exception e) {
        e.printStackTrace();
        System.exit(-1);
    }
}

/**
 * このクラスのインスタンスを取得する。
 * @return H2dbManagerインスタンス
 */
public static H2dbManager getInstance() {
    if (instance == null) {
        instance = new H2dbManager();
    }
    return instance;
}