Java超|11章のまとめ

親の力を受け継ぎ、子で個性を加え、同じ命令でも実体ごとに動きが変わる。11章は、Javaのクラス設計を一段強くする「継承の戦闘理論」を学ぶ章です。

11章では、Javaのオブジェクト指向の中でも特に重要な継承を中心に学びました。

継承は、すでに作ってあるクラスを土台にして、新しいクラスを効率よく作るためのしくみです。
まったくゼロから作り直すのではなく、親クラスのフィールドやメソッドを受け継ぎながら、必要な特徴だけを子クラスに追加できます。

ドラゴンボール風にたとえると、まず SaiyanWarrior クラスというサイヤ人戦士の共通の型があります。

この SaiyanWarrior クラスには、戦士の名前、流派、状態表示など、サイヤ人戦士なら共通して持っていそうな情報や動作をまとめます。

そこから、より特別な戦士を表す SuperSaiyanWarrior クラスを作ることができます。
さらに、その先に GodSaiyanWarrior クラスのような、神流の覚醒戦士を表すクラスを作ることもできます。

親クラスに共通部分をまとめ、子クラスでは個性を加える。

この考え方が、11章全体を通して大切な流れです。

11章で扱った内容は、単に extends の書き方だけではありません。

学んだ内容役割
継承親クラスの機能を子クラスへ受け継ぐ
スーパークラスとサブクラス親と子の関係を整理する
protected子クラスから使えるメンバを作る
オーバーライド親のメソッドを子クラス用に作り直す
ポリモーフィズム同じ呼び出しで実体ごとに違う動きをさせる
オーバーロードとの違い同名メソッドの使い分けを整理する
final変更、継承、上書きを制限する
Object クラスすべてのクラスの共通の親
toString()オブジェクトを文字列で表す
equals()オブジェクトが同じかどうか調べる
getClass()オブジェクトの実体のクラス情報を調べる

つまり11章は、既存のクラスをどう活かし、どう広げ、どこを守り、どう共通化するかを学ぶ章です。

ここが見えてくると、Javaのオブジェクト指向は、単なる文法の暗記ではなく、クラス同士の関係を設計する考え方だと分かってきます。

継承は親の力を受け継いで新しいクラスを作るしくみ

11章の中心にあるのは、継承です。

継承では、スーパークラスをもとにしてサブクラスを作ります。
サブクラスは、スーパークラスのフィールドやメソッドを受け継ぎながら、自分に必要な新しい機能を追加できます。

ドラゴンボール風にたとえると、SaiyanWarrior クラスには、サイヤ人戦士としての共通情報があります。

SaiyanWarrior クラスの共通要素内容
name戦士の名前
styleName流派名
show()戦士情報を表示する
setWarrior()名前や流派を設定する

そこから SuperSaiyanWarrior クラスを作る場合、SaiyanWarrior の共通要素を受け継ぎます。

そのうえで、スーパーサイヤ人だけが持つ変身段階や、覚醒状態を表示する機能を追加できます。

SuperSaiyanWarrior クラスで追加する要素内容
transformationLevel変身段階
setTransformationLevel()変身段階を設定する
show() の作り直しスーパーサイヤ人らしい情報表示にする

このように、親クラスには共通部分を書き、子クラスには個別の特徴を書きます。

同じようなコードを何度も書かなくて済むので、継承はコードの再利用に役立ちます。

たとえば、名前や流派を設定する処理は、普通のサイヤ人戦士でもスーパーサイヤ人戦士でも必要です。
その共通処理を SaiyanWarrior にまとめておけば、SuperSaiyanWarrior 側で同じ処理をもう一度書く必要がありません。

ドラゴンボール風に言えば、スーパーサイヤ人は特別な存在ですが、まずサイヤ人戦士としての基本を持っています。
だから、サイヤ人戦士としての共通能力を親クラスから受け継ぎ、その上にスーパーサイヤ人としての個性を追加するのです。

サブクラスはスーパークラスのメンバを受け継ぐ

継承を使うと、サブクラスはスーパークラスのメンバを受け継げます。

たとえば、SaiyanWarrior クラスに show() がある場合、SuperSaiyanWarrior クラスはその show() を使えます。

SuperSaiyanWarrior 側に同じ処理をもう一度書かなくてもよいのが大きなポイントです。

ドラゴンボール風にたとえると、スーパーサイヤ人はスーパーサイヤ人としての特別な変身能力を持っています。
しかし、スーパーサイヤ人である前に、サイヤ人戦士でもあります。

そのため、サイヤ人戦士としての名前、流派、基本表示の機能を受け継げます。

考え方ドラゴンボール風のイメージ
スーパークラスサイヤ人戦士の共通設計
サブクラススーパーサイヤ人などの特別な戦士
継承サイヤ人戦士としての基本を受け継ぐ
追加スーパーサイヤ人としての特徴を加える

このように、継承は「共通部分を親にまとめ、違いだけを子に書く」ためのしくみです。

ただし、継承したからといって、親クラスのすべてのメンバに自由に直接アクセスできるわけではありません。

そこで次に大切になるのが、private や protected のようなアクセス修飾子です。

protected は親子関係の中で使いやすいアクセス指定

11章では、メンバへのアクセス範囲も学びました。

スーパークラスの private メンバには、サブクラスから直接アクセスできません。
一方で、protected メンバなら、サブクラスから直接アクセスできます。

アクセス修飾子サブクラスから直接アクセスできるか役割
privateできないそのクラス内部だけで守る
protectedできる子クラスにも使わせる
publicできる外部にも公開する

ドラゴンボール風にたとえると、SaiyanWarrior クラスが持つ情報の中には、親クラスだけで厳重に管理したいものもあります。

一方で、SuperSaiyanWarrior クラスにも使わせたい情報もあります。

たとえば、name や styleName を SuperSaiyanWarrior の show() で表示したいなら、protected にしておくと扱いやすくなります。

情報private が向く場合protected が向く場合
name親クラスだけで厳密に管理したい子クラスでも表示したい
styleName外部や子から直接触らせたくない子クラスの処理でも使いたい
transformationLevelSuperSaiyanWarrior 内だけで使う親子で共有する必要がない

protected は、カプセル化を完全に崩すものではありません。

親子関係の中で必要な情報を共有しやすくするための指定です。

たとえば、スーパーサイヤ人戦士が自己紹介するとき、名前と流派は親クラスにある情報ですが、子クラスの show() でも使いたい場合があります。

そのようなときに protected を使うと、親クラスの情報を子クラス側で自然に利用できます。

図:11章全体の継承の流れ

この図が示していること

この図では、11章で学ぶ継承の全体像を表しています。

一番上には Object クラスがあります。
その下に SaiyanWarrior、SuperSaiyanWarrior、GodSaiyanWarrior が続きます。

この階層から、Javaのクラスは最終的に Object クラスにつながり、そこから toString()、equals(Object obj)、getClass() のような共通の基本機能を受け継ぐことが分かります。

また、SaiyanWarrior には共通部分を置き、SuperSaiyanWarrior では変身段階やオーバーライドした show() を追加しています。

左側の protected は、親子関係の中で子クラスにも使わせたいメンバを表します。

右側の final は、変更させたくない部分を固定するための仕組みを表します。

この図から分かることは、11章の内容がバラバラではなく、継承、アクセス制御、オーバーライド、final、Object クラスという流れでつながっているということです。

オーバーライドは親のメソッドを子クラスらしく作り直すしくみ

オーバーライドも、11章の大きなテーマでした。

オーバーライドとは、スーパークラスと同じメソッド名、同じ引数の形式を持つメソッドを、サブクラス側で定義しなおすことです。

たとえば、SaiyanWarrior クラスに show() があるとします。

public void show()
{
    System.out.println("戦士の名前を表示します。");
    System.out.println("流派を表示します。");
}

SuperSaiyanWarrior クラスでは、変身段階も表示したいので、同じ show() を作り直します。

public void show()
{
    System.out.println("スーパーサイヤ人の名前を表示します。");
    System.out.println("流派を表示します。");
    System.out.println("変身段階を表示します。");
}

このように、同じ show() でも、SuperSaiyanWarrior のオブジェクトで呼び出したときは SuperSaiyanWarrior 側の show() が動きます。

ドラゴンボール風にたとえると、通常のサイヤ人戦士には通常の自己紹介があります。
スーパーサイヤ人戦士には、変身段階まで含めた自己紹介があります。

クラスshow() の内容
SaiyanWarrior名前と流派を表示
SuperSaiyanWarrior名前、流派、変身段階を表示

オーバーライドは、継承が単なる受け継ぎで終わらず、子クラスらしい振る舞いを表現できることを示しています。

親から受け継いだ型をそのまま使うだけではなく、子クラスの性質に合わせて中身を変える。

これがオーバーライドの大切な役割です。

スーパークラス型でまとめて扱えることが多態性につながる

11章では、サブクラスのオブジェクトをスーパークラス型の変数で扱えることも学びました。

これは、サブクラスのオブジェクトがスーパークラスの一種でもあるからです。

たとえば、SuperSaiyanWarrior は SaiyanWarrior を継承しています。

そのため、次のように書けます。

SaiyanWarrior warrior1 = new SuperSaiyanWarrior();

変数の型は SaiyanWarrior です。
しかし、実際に入っているオブジェクトは SuperSaiyanWarrior です。

ここで show() を呼び出すと、実体である SuperSaiyanWarrior 側の show() が動きます。

変数の型実体show() で動くもの
SaiyanWarriorSaiyanWarriorSaiyanWarrior の show()
SaiyanWarriorSuperSaiyanWarriorSuperSaiyanWarrior の show()

これがポリモーフィズム、多態性です。

ドラゴンボール風にたとえると、戦士管理本部は全員をサイヤ人戦士としてまとめて扱えます。

しかし、実際に show() を命じると、通常のサイヤ人戦士は通常の戦士らしく、スーパーサイヤ人戦士はスーパーサイヤ人らしく自己紹介します。

同じ命令でも、実体によって動きが変わる。

これが多態性の大切な感覚です。

オーバーライドとオーバーロードは似ているが別物

11章では、オーバーライドとオーバーロードの違いも整理しました。

名前は似ていますが、意味はかなり違います。

用語意味ドラゴンボール風のイメージ
オーバーロード同じメソッド名で、引数の形式が異なるメソッドを複数定義する同じ技名で使い方違いを用意する
オーバーライド親クラスと同じメソッド名、同じ引数の形式のメソッドを子クラスで定義する親の技を子が自分流に作り直す

見分けるときは、引数の形を見ると分かりやすいです。

判断ポイント結論
同じ名前で引数が違うオーバーロード
同じ名前で引数も同じ、継承関係があるオーバーライド

ドラゴンボール風にたとえると、useKiBlast()、useKiBlast(int power)、useKiBlast(int power, String target) のように、同じ技名で条件違いを用意するのがオーバーロードです。

一方、SaiyanWarrior の show() を SuperSaiyanWarrior でスーパーサイヤ人用に作り直すのがオーバーライドです。

似ているのは、同じ名前のメソッドが出てくることです。
しかし、目的はまったく違います。

オーバーロードは、使い方の種類を増やすためのしくみです。
オーバーライドは、継承したメソッドを子クラス用に作り直すためのしくみです。

final はそれ以上変更させないための修飾子

11章では、継承やオーバーライドが便利である一方で、すべてを自由に変えさせればよいわけではないことも学びました。

そこで登場したのが final です。

final は、付ける場所によって意味が変わります。

final を付ける場所意味
メソッドオーバーライドできない
クラス継承できない
フィールド値を変更できない

ドラゴンボール風にたとえると、final は「ここは変えてはいけない」という封印のようなものです。

final の対象ドラゴンボール風のイメージ
final メソッドこの技の型は変えるな
final クラスこの戦士の型は完成形なので派生させるな
final フィールドこの数値は戦士管理本部の決まりだから変えるな

継承はクラスを自由に広げるためのしくみです。

しかし、設計では、自由に広げる部分と、固定して守る部分を分けることも大切です。

たとえば、戦士ごとの戦い方は変えてもよいかもしれません。
でも、戦士登録の基本ルールや、最大登録数のような固定値は勝手に変えられると困ります。

final は、その境界線をコードに表すための修飾子です。

Java のクラスは最終的に Object クラスにつながる

11章の後半では、Object クラスも学びました。

Javaでは、スーパークラスを指定しないクラスは、自動的に Object クラスのサブクラスになります。

たとえば、次のように書いた場合です。

class SaiyanWarrior
{
}

extends を書いていなくても、考え方としては次のようになります。

class SaiyanWarrior extends Object
{
}

つまり、Javaのすべてのクラスは最終的に Object クラスにつながります。

ドラゴンボール風にたとえると、サイヤ人戦士、スーパーサイヤ人戦士、スカウター、戦闘服、任務カードなど、いろいろなクラスがあっても、Javaの世界ではすべて「オブジェクト」としての共通土台を持っています。

Object クラスから受け継ぐ代表的なメソッドには、次のものがあります。

メソッド役割
toString()オブジェクトを表す文字列を返す
equals(Object obj)オブジェクトが同じかどうかを調べる
getClass()オブジェクトのクラス情報を返す

Object クラスを理解すると、Javaのクラスがバラバラに存在しているのではなく、共通の親につながっていることが分かります。

図:ポリモーフィズムと Object の基本メソッド

この図が示していること

この図では、11章後半で学んだポリモーフィズムと Object クラスの基本メソッドを整理しています。

左側では、SaiyanWarrior[] 配列に SaiyanWarrior オブジェクトと SuperSaiyanWarrior オブジェクトが一緒に入っています。

同じ warriors[i].show() という呼び出しでも、実体が SaiyanWarrior なら SaiyanWarrior の show()、実体が SuperSaiyanWarrior なら SuperSaiyanWarrior の show() が動きます。

右側では、Object クラス由来の基本メソッドをまとめています。

メソッド役割
toString()オブジェクトの名乗り方を決める
equals(Object obj)同じ実体かどうかを確認する
getClass()実体のクラス情報を確認する

この図から分かることは、スーパークラス型でまとめて扱っても、実体ごとの個性は消えないということです。

さらに、どのクラスも Object クラス由来の基本機能を持っていることも分かります。

toString() はオブジェクトの名乗り方を決める

toString() は、オブジェクトを文字列で表すメソッドです。

オブジェクトを System.out.println() に渡したとき、この toString() の戻り値が表示されます。

System.out.println(warrior1);

このように書いたとき、内部では warrior1.toString() が呼び出され、その戻り値が表示されます。

Object クラス由来の toString() をそのまま使うと、SaiyanWarrior@1a2b3c のような機械的な表示になりがちです。

そこで、自分のクラスで toString() をオーバーライドすると、分かりやすい文字列を返せるようになります。

状態表示例
toString() をオーバーライドしないSaiyanWarrior@1a2b3c
toString() をオーバーライドする名前:悟空 流派:亀仙流

ドラゴンボール風にたとえると、toString() は戦士の自己紹介です。

自分の名前や流派を分かりやすく名乗らせるために、toString() を作り直します。

equals() は同じオブジェクトかを調べる基本メソッド

equals(Object obj) は、2つの変数が同じオブジェクトを指しているかどうかを調べるメソッドです。

Object クラス由来の equals(Object obj) では、基本的に同じ実体を指している場合に true、別のオブジェクトなら false を返します。

ドラゴンボール風にたとえると、2枚の戦士登録札があるとします。

その2枚が同じ戦士本人を指しているなら true です。
似ていても別々に作られた戦士を指しているなら false です。

比較結果
同じオブジェクトを指しているtrue
別々のオブジェクトを指しているfalse

また、String クラスでは equals(Object obj) がオーバーライドされていて、文字列の内容が同じかどうかを見るように作り直されています。

クラスequals(Object obj) の考え方
Object 由来同じ実体かどうか
String文字列の内容が同じかどうか

このように、equals(Object obj) はクラスごとに「同じ」の意味を作り直せるメソッドでもあります。

getClass() はオブジェクトの正体を調べる

getClass() は、オブジェクトが属しているクラスの情報を返すメソッドです。

戻り値は Class クラスのオブジェクトです。

スーパークラス型の配列で複数のオブジェクトをまとめて扱っているときでも、getClass() を使えば、そのオブジェクトが実際にはどのクラスなのかを確認できます。

たとえば、次のような配列を考えます。

SaiyanWarrior[] warriors = new SaiyanWarrior[2];

warriors[0] = new SaiyanWarrior();
warriors[1] = new SuperSaiyanWarrior();

配列の型は SaiyanWarrior[] です。

しかし、中に入っている実体は異なります。

配列要素実体getClass() の結果
warriors[0]SaiyanWarriorclass SaiyanWarrior
warriors[1]SuperSaiyanWarriorclass SuperSaiyanWarrior

ドラゴンボール風にたとえると、名簿では全員がサイヤ人戦士として並んでいても、実際には通常のサイヤ人戦士やスーパーサイヤ人戦士が混ざっています。

getClass() を使うと、その正体を確認できます。

11章の内容をつなげて見る

11章で学んだ内容は、それぞれ別々の知識に見えるかもしれません。

しかし、実際にはすべてつながっています。

11章の考え方内容
共通部分は親にまとめるSaiyanWarrior に name、styleName、show() などを置く
個性は子に追加するSuperSaiyanWarrior に transformationLevel などを追加する
必要なら作り直すshow() をオーバーライドする
まとめて扱うSaiyanWarrior 型で SuperSaiyanWarrior を扱う
実体ごとに動きを変えるポリモーフィズム
守る部分は固定するfinal を使う
すべてのクラスは Object につながるtoString()、equals(Object obj)、getClass() を受け継ぐ

このように見ると、11章は「親から子へ受け継ぐ」という1つの流れで整理できます。

まず、親クラスに共通部分をまとめます。
次に、子クラスで個性を追加します。
必要に応じて、親のメソッドを子クラス用に作り直します。
さらに、スーパークラス型でまとめて扱いながら、実体に応じた動きをさせます。
そして、変えてはいけない部分は final で守ります。
最後に、すべてのクラスが Object につながっていることを理解します。

ドラゴンボール風にたとえると、サイヤ人戦士という共通の型があり、そこからスーパーサイヤ人、神流の覚醒戦士のような個性ある型が生まれます。

でも、それぞれが完全にバラバラなのではありません。

共通部分は親にあります。
個性は子にあります。
同じ命令でも、実体によって動きが変わります。
そして、すべてのクラスは Object という共通の土台につながっています。

サンプルプログラム

ここでは、11章で学んだ継承、protected、オーバーライド、ポリモーフィズム、toString()、equals(Object obj)、getClass() の考え方をまとめて確認できるサンプルプログラムを示します。

class SaiyanWarrior
{
    protected String name;
    protected String styleName;

    public SaiyanWarrior()
    {
        name = "戦士未登録";
        styleName = "流派未設定";
    }

    public SaiyanWarrior(String name, String styleName)
    {
        this.name = name;
        this.styleName = styleName;
    }

    public void setWarrior(String name, String styleName)
    {
        this.name = name;
        this.styleName = styleName;
    }

    public void show()
    {
        System.out.println("戦士の名前は" + name + "です。");
        System.out.println("流派は" + styleName + "です。");
    }

    public final void confirmRule()
    {
        System.out.println("戦士管理本部の基本規則を確認しました。");
    }

    public String toString()
    {
        return "名前:" + name + " 流派:" + styleName;
    }
}

class SuperSaiyanWarrior extends SaiyanWarrior
{
    private int transformationLevel;

    public SuperSaiyanWarrior(String name, String styleName, int transformationLevel)
    {
        super(name, styleName);
        this.transformationLevel = transformationLevel;
    }

    public void setTransformationLevel(int transformationLevel)
    {
        this.transformationLevel = transformationLevel;
    }

    public void show()
    {
        System.out.println("スーパーサイヤ人の名前は" + name + "です。");
        System.out.println("流派は" + styleName + "です。");
        System.out.println("変身段階は" + transformationLevel + "です。");
    }

    public String toString()
    {
        return "名前:" + name + " 流派:" + styleName + " 変身段階:" + transformationLevel;
    }
}

class Main
{
    public static void main(String[] args)
    {
        SaiyanWarrior warrior1 = new SaiyanWarrior("天津飯", "鶴仙流");
        SaiyanWarrior warrior2 = new SuperSaiyanWarrior("悟空", "亀仙流", 1);

        SaiyanWarrior[] warriors = new SaiyanWarrior[2];
        warriors[0] = warrior1;
        warriors[1] = warrior2;

        for(int i = 0; i < warriors.length; i++){
            warriors[i].show();
            warriors[i].confirmRule();

            System.out.println("文字列表現:" + warriors[i]);
            System.out.println("実体のクラス:" + warriors[i].getClass());
            System.out.println();
        }

        System.out.println("warrior1 と warrior2 は同じですか?");
        System.out.println(warrior1.equals(warrior2));

        SaiyanWarrior warrior3 = warrior1;

        System.out.println("warrior1 と warrior3 は同じですか?");
        System.out.println(warrior1.equals(warrior3));
    }
}

実行結果の例です。

戦士の名前は蒼真です。
流派は鶴仙流です。
戦士管理本部の基本規則を確認しました。
文字列表現:名前:天津飯 流派:鶴仙流
実体のクラス:class SaiyanWarrior

スーパーサイヤ人の名前は悟空です。
流派は亀仙流です。
変身段階は1です。
戦士管理本部の基本規則を確認しました。
文字列表現:名前:悟空 流派:亀仙流 変身段階:1
実体のクラス:class SuperSaiyanWarrior

warrior1 と warrior2 は同じですか?
false
warrior1 と warrior3 は同じですか?
true

このサンプルでは、SaiyanWarrior 型の配列に、SaiyanWarrior オブジェクトと SuperSaiyanWarrior オブジェクトを入れています。

配列の型は同じでも、実体は異なります。

そのため、show() を呼び出すと、実体に応じたメソッドが動きます。

配列要素実体show() で動くもの
warriors[0]SaiyanWarriorSaiyanWarrior の show()
warriors[1]SuperSaiyanWarriorSuperSaiyanWarrior の show()

また、toString() をオーバーライドしているため、オブジェクトを表示したときに分かりやすい文字列が表示されます。

getClass() を使うと、実体のクラス情報を確認できます。

equals(Object obj) を使うと、同じオブジェクトを指しているかどうかを確認できます。

final が付いた confirmRule() は、子クラス側でオーバーライドできない共通ルールとして扱えます。

図:11章で学んだ力を1つの設計にまとめる

この図が示していること

この図では、11章で学んだ知識が1つのクラス設計につながることを表しています。

SaiyanWarrior には、name、styleName、show()、toString()、final confirmRule() のような共通部分があります。

SuperSaiyanWarrior では、transformationLevel を追加し、show() や toString() を自分用にオーバーライドしています。

SaiyanWarrior[] 配列には、SaiyanWarrior オブジェクトと SuperSaiyanWarrior オブジェクトを一緒に入れられます。

そして warriors[i].show() を呼び出すと、実体ごとに違う show() が動きます。

右側の Object 基本機能カードでは、toString()、equals(Object obj)、getClass() が、すべてのクラスで使える共通機能として整理されています。

中央の final ロックは、変えてはいけない共通ルールを固定する役割を示しています。

この図から分かることは、11章の内容は単なる文法の寄せ集めではなく、親で共通化し、子で個性を出し、Object の基本機能で確認し、final で守るというクラス設計の流れになっているということです。

11章でいちばん大事な感覚

11章で学んだことをひとつの感覚にまとめるなら、親クラスを土台にして、新しいクラスを作り、その関係の中で役割を分けていく考え方です。

ドラゴンボール風にたとえると、共通のサイヤ人戦士の型があり、そこからスーパーサイヤ人や神流の覚醒戦士のような個性ある型が生まれます。

しかし、それぞれが完全にバラバラなのではありません。

共通部分は親にあります。
個性は子にあります。
同じ命令でも、実体によって動きが変わります。
守るべき部分は final で固定します。
そして、すべてのクラスは Object という共通の土台につながっています。

11章の考え方内容
共通部分は親にまとめるSaiyanWarrior に name、styleName、show() などを置く
個性は子に追加するSuperSaiyanWarrior に transformationLevel などを追加する
必要なら作り直すshow() や toString() をオーバーライドする
まとめて扱うSaiyanWarrior 型で SuperSaiyanWarrior を扱う
実体ごとに動きを変えるポリモーフィズム
守る部分は固定するfinal を使う
すべてのクラスは Object につながるtoString()、equals(Object obj)、getClass() を受け継ぐ

この感覚がつかめると、継承は単なる文法ではなく、クラス設計そのものを考えるための強力な考え方だと分かります。

Javaのオブジェクト指向では、クラス同士の関係をどう作るかがとても大切です。

親クラスに何を置くのか。
子クラスで何を追加するのか。
どのメソッドを作り直すのか。
どの処理は final で守るのか。
Object 由来のメソッドをどう活用するのか。

こうした視点を持つことで、コードはただ動くだけでなく、読みやすく、拡張しやすく、保守しやすいものになります。