Java | 1 日 90 分 × 7 日アプリ学習 初級編:コンストラクタ入門アプリ

Web APP Java
スポンサーリンク

4日目のゴール

4日目のテーマは
「コンストラクタで“1つのクラス”を初期化する」から、“複数のクラスが関わる初期化”に意識を広げること です。

ここまでであなたはすでに、

コンストラクタ=new された瞬間に呼ばれる特別メソッド
初期化=「生まれた直後の状態を整えること」
おかしな値をコンストラクタで弾く・補正する
他のクラス(Profile など)を一緒に new して初期化できる

というところまで来ています。

4日目ではここから一歩進んで、

「1つのオブジェクトだけ」ではなく、「関係するオブジェクト全体の初期状態」を考える
「途中で初期化が止まらない」ように設計する
「使う側(main)から見て、初期化が分かりやすい形」を意識する

これを、少し“アプリっぽい”ログイン設定ミニアプリで固めていきます。


今日の題材:ログイン設定ミニアプリ

現実のイメージからクラスを取り出す

現実のアプリを思い浮かべてみてください。

ユーザーがいる
ユーザーにはログインIDやパスワードがある
ログインに関する設定(ロック回数、2段階認証の有無など)がある

これを Java のクラスにすると、こんな登場人物が見えてきます。

User … ユーザーそのもの
LoginSetting … ログインに関する設定(ロック回数、2段階認証など)
Main … それらを new して、状態を確認する

今日は、この2つのクラスが「一緒にちゃんと初期化される」イメージを作っていきます。


まずは素朴な User と LoginSetting を書いてみる

とりあえず動くけど“弱い”バージョン

最初に、あえて「初期化が弱い」形から始めます。

public class LoginSetting {
    int maxFailCount;
    boolean twoFactorEnabled;
}
Java
public class User {
    String loginId;
    String password;
    LoginSetting setting;
}
Java

これを main で使ってみます。

public class Main {
    public static void main(String[] args) {
        User user = new User();

        user.loginId = "taro";
        user.password = "pass123";

        LoginSetting s = new LoginSetting();
        s.maxFailCount = 5;
        s.twoFactorEnabled = true;

        user.setting = s;
    }
}
Java

動きます。
でも、問題が山ほどあります。

User を new しただけでは、何も設定されていない
LoginSetting を new しただけでも、何も設定されていない
設定し忘れてもコンパイルエラーにならない

つまり、

「中途半端な状態の User が簡単に生まれてしまう」

状態です。

4日目では、これをコンストラクタで“ガチッ”と固めていきます。


LoginSetting を“ちゃんと初期化されるクラス”にする

コンストラクタでルールを埋め込む

まずは LoginSetting から整えます。

public class LoginSetting {
    int maxFailCount;
    boolean twoFactorEnabled;

    LoginSetting(int maxFailCount, boolean twoFactorEnabled) {
        if (maxFailCount <= 0) {
            System.out.println("maxFailCount が 0 以下です。3 にします。");
            this.maxFailCount = 3;
        } else {
            this.maxFailCount = maxFailCount;
        }
        this.twoFactorEnabled = twoFactorEnabled;
    }

    void show() {
        System.out.println("ログイン失敗上限: " + maxFailCount);
        System.out.println("2段階認証: " + (twoFactorEnabled ? "有効" : "無効"));
    }
}
Java

ここでやっていることを整理します。

コンストラクタで必ず maxFailCount と twoFactorEnabled を受け取る
maxFailCount が 0 以下なら、3 に補正する
twoFactorEnabled はそのまま使う
show で「設定の中身」を表示できるようにしている

これで、

new LoginSetting(5, true) と書いた瞬間に、
「ログイン失敗上限 5・2段階認証あり」の設定が必ずできる

という状態になります。


User に「設定付きで生まれてもらう」

User のコンストラクタで LoginSetting も一緒に初期化する

次に、User を整えます。

public class User {
    String loginId;
    String password;
    LoginSetting setting;

    User(String loginId, String password, LoginSetting setting) {
        if (loginId == null || loginId.isEmpty()) {
            System.out.println("loginId が空です。'guest' にします。");
            this.loginId = "guest";
        } else {
            this.loginId = loginId;
        }

        if (password == null || password.length() < 4) {
            System.out.println("password が短すぎます。'pass' にします。");
            this.password = "pass";
        } else {
            this.password = password;
        }

        if (setting == null) {
            System.out.println("LoginSetting が null です。デフォルト設定を使います。");
            this.setting = new LoginSetting(3, false);
        } else {
            this.setting = setting;
        }
    }

    void show() {
        System.out.println("=== ユーザー情報 ===");
        System.out.println("ログインID: " + loginId);
        System.out.println("パスワード: " + password);
        System.out.println("--- ログイン設定 ---");
        setting.show();
    }
}
Java

ここでのポイントを深掘りします。

loginId が null や空文字なら “guest” にする
password が null または 4文字未満なら “pass” にする
setting が null なら、「デフォルト設定」を new して使う

つまり、

「User を new した瞬間に、LoginSetting も必ず“何かしらの意味のある状態”で存在する」

という状態になっています。


main から見た“初期化の分かりやすさ”を確認する

使う側のコードがスッキリしているか?

ここまで整えたクラスを、main から使ってみます。

public class Main {
    public static void main(String[] args) {
        LoginSetting strongSetting = new LoginSetting(3, true);

        User user1 = new User("taro", "pass123", strongSetting);

        User user2 = new User("", "a", null);

        user1.show();
        System.out.println("-----");
        user2.show();
    }
}
Java

実行イメージはこんな感じです。

maxFailCount が 0 以下です。3 にします。  // (もし 0 以下を渡した場合)
loginId が空です。'guest' にします。
password が短すぎます。'pass' にします。
LoginSetting が null です。デフォルト設定を使います。
=== ユーザー情報 ===
ログインID: taro
パスワード: pass123
--- ログイン設定 ---
ログイン失敗上限: 3
2段階認証: 有効
-----
=== ユーザー情報 ===
ログインID: guest
パスワード: pass
--- ログイン設定 ---
ログイン失敗上限: 3
2段階認証: 無効

ここで感じてほしいのは、

main は「どう初期化されるか」の細かいルールを知らなくていい
User と LoginSetting のコンストラクタに、ルールが全部閉じ込められている
new User(...) と書いた瞬間に、「もう使っていい状態」の User が必ず手に入る

という安心感です。


「途中で初期化が止まらない」設計を意識する

null のまま放置されるフィールドを作らない

4日目で特に強く意識してほしいのは、

「new したのに、どこかのフィールドが null のまま」という状態を避ける

ということです。

例えば、こういうコードは危険信号です。

User user = new User("taro", "pass123", null);
// あとでどこかで setting を new するつもり…
user.setting = new LoginSetting(5, true);
Java

「あとでやるつもり」が一番危ないです。
書き忘れたら、その User は一生 setting が null のままです。

それに対して、さっきの User のコンストラクタはこうでした。

if (setting == null) {
    System.out.println("LoginSetting が null です。デフォルト設定を使います。");
    this.setting = new LoginSetting(3, false);
} else {
    this.setting = setting;
}
Java

これにより、

「setting が null の User は絶対に生まれない」

という保証ができます。

初期化の意味を一言で言うなら、

「このクラスのオブジェクトは、こういう“最低限の約束”を守った状態で生まれてくる」

というルールを作ることです。


初期化の“責任の分担”を意識する

どこまでをどのクラスが担当するか?

4日目の例では、こういう分担になっていました。

LoginSetting
自分自身の中身(maxFailCount・twoFactorEnabled)のルールを守る
「0 以下なら 3 にする」などのチェックと補正を担当する

User
自分の loginId・password のルールを守る
LoginSetting が null のときに「デフォルト設定を用意する」責任を持つ

Main
User や LoginSetting を new して、
「どんな組み合わせで使うか」を決めるだけ
中身のチェックや補正には踏み込まない

この「誰がどこまで初期化を担当するか」を意識できると、
コードの見通しが一気によくなります。


4日目のミニアプリ完成形をまとめて眺める

コード全体をもう一度

public class LoginSetting {
    int maxFailCount;
    boolean twoFactorEnabled;

    LoginSetting(int maxFailCount, boolean twoFactorEnabled) {
        if (maxFailCount <= 0) {
            System.out.println("maxFailCount が 0 以下です。3 にします。");
            this.maxFailCount = 3;
        } else {
            this.maxFailCount = maxFailCount;
        }
        this.twoFactorEnabled = twoFactorEnabled;
    }

    void show() {
        System.out.println("ログイン失敗上限: " + maxFailCount);
        System.out.println("2段階認証: " + (twoFactorEnabled ? "有効" : "無効"));
    }
}
Java
public class User {
    String loginId;
    String password;
    LoginSetting setting;

    User(String loginId, String password, LoginSetting setting) {
        if (loginId == null || loginId.isEmpty()) {
            System.out.println("loginId が空です。'guest' にします。");
            this.loginId = "guest";
        } else {
            this.loginId = loginId;
        }

        if (password == null || password.length() < 4) {
            System.out.println("password が短すぎます。'pass' にします。");
            this.password = "pass";
        } else {
            this.password = password;
        }

        if (setting == null) {
            System.out.println("LoginSetting が null です。デフォルト設定を使います。");
            this.setting = new LoginSetting(3, false);
        } else {
            this.setting = setting;
        }
    }

    void show() {
        System.out.println("=== ユーザー情報 ===");
        System.out.println("ログインID: " + loginId);
        System.out.println("パスワード: " + password);
        System.out.println("--- ログイン設定 ---");
        setting.show();
    }
}
Java
public class Main {
    public static void main(String[] args) {
        LoginSetting strongSetting = new LoginSetting(3, true);

        User user1 = new User("taro", "pass123", strongSetting);

        User user2 = new User("", "a", null);

        user1.show();
        System.out.println("-----");
        user2.show();
    }
}
Java

このコードを見て、

「User を new した瞬間に、どんな状態の User が生まれるか」
「LoginSetting を new した瞬間に、どんな状態の設定が生まれるか」

を、自分の言葉で説明できたら、4日目はかなりいい感じです。


4日目で絶対に押さえてほしい本質

今日いちばん大事なのは、
「初期化=“オブジェクト単体”ではなく、“関係するオブジェクト全体”の状態を整えること」
という視点を持てたかどうかです。

コンストラクタでやるべきことは、

必須の情報を必ず受け取るようにする
おかしな値が来たら、その場でチェックして補正する
null のまま放置されるフィールドを作らない
必要な他のオブジェクトも、そこで new しておく

そして、

new した瞬間に、もう安心して使える状態にしておく
途中で初期化が止まらないようにする

この感覚が入っていれば、
コンストラクタはもう「文法」ではなく、
“オブジェクトの安全な生まれ方をデザインする道具” になっています。

5日目以降は、
この初期化の考え方を、設定クラス・ゲームのステータス・アプリ全体の構成など、
もっと大きな単位に広げていくと、一気に設計力が伸びていきます。

タイトルとURLをコピーしました