オブジェクト向けプログラミングの入門-継承ではなくアセンブリ


オブジェクト向けプログラミングの入門


この文章は学習崔凡均の情報学習講座の内容をまとめた.

継承


継承の定義
つまり상위 클래스를 확장です.
継承後、親として宣言されたpublicまたはprotectedのすべての変数とメソッドを使用できます.
使用シナリオの継承
高度な機能を再利用、拡張する方法として.
継承を説明する例としてSpringフレームワークのWebリクエストを処理するための初期クラス階層を導入した.

上の赤い枠線AbstractControllerは、Webリクエストに関する基本的な機能を提供します.
次のメインフレームBaseCommandControllerは、Webリクエストの基本機能+パラメータ処理機能を拡張しています.
最後に、AbstractForm Controlは、高度な機能+Form関連機能(フォームの表示、フォーム転送の処理)を拡張します.
これにより、親機能を再利用および拡張する方法として継承できます.
ただし、機能を継承して再使用するといくつかの問題が発生します.
  • 高度な変更困難
  • 類増加
  • 継承誤用
  • 受け継がれた欠点を生かす


    1.親の変更が困難


    最初に理解したいのは、親を変更するのは難しいことです.

    親を変更すると、すべての子に影響します.
    また、親対子のパッケージが弱くなる可能性があります.
    機能を再利用するには、子クラスが親の動作を知らなければならないからです.

    2.ランクを上げる


    2つ目は等級を増やすことです.

    左図の説明


    最初は、ストレージを圧縮し、暗号化機能を拡張するクラスだけで十分でした.

    右図の説明


    時間が経つにつれて、Cacheable(キャッシュ)機能が2つの既存の機能に追加されます.
    独立して使用する機能を合わせて使用する場合があります.
    これにより、CompressedEncrypted(圧縮+暗号化)、EncryptedCompressed(暗号化+圧縮)などのクラスが作成されます.(右図)
    新しい機能(e.g.圧縮+暗号化+キャッシュ)をサポートする必要がある場合は、別のクラスが表示されます.
    圧縮+暗号化+キャッシュ機能クラスの追加

    3.誤用相続


    最後に,継承を用いた拡張方式が生じる可能性のある欠点は,継承自体を誤用できることである.
    この欠点を説明するには、以下にArrayListを継承するContainerクラスを参照してください.
    //Container.java
    public class Container extends ArrayList {
    private int maxSize;
    private int currentSize;
    public Container(int maxSize) {
    this.maxSize = maxSize;
    }
    public void put(Luggage lug) throws NotEnoughSpaceException {
    if (!canContain(lug)) throw new NotEnoughSpaceException();
    super.add(lug);
    currentSize += lug.size();
    }
    public void extract(Luggage lug) {
    super.remove(lug);
    this.currentSize -= lug.size();
    }
    public boolean canContain(Luggage lug) {
    return maxSize >= currentSize + lug.size();
    }
    }

    Container.Javaコードの説明


    containerクラスはLugageリストを管理するクラスです.
    ディレクトリ管理機能は直接実現されるのではなく、ArrayListを継承し実現している.
    管理の実施方法を見てみましょう.

  • Luggageの追加public void put(Luggage lug) throws NotEnoughSpaceException {...}から見ると、super.add(lug);により水和物が添加される.

  • Luggageの削除public void extract(Luggage lug) {...}から見ると、super.remove(lug)によって水和物が除去される.
  • コード自体に問題はありません.
    また、実際には以下のように使用されています.

    では、問題はどこから来たのでしょうか.
    下の写真を見てください.

    ここで、コンテナに荷物を追加する方法はadd()ではなくput()です.
    ContainerクラスがArrayListのAdd()をサポートする理由
    >>ArrayListを引き継いだからです.
    add()で荷物を追加する場合、右側のコードのコメントのようにコンテナの積載量が減少しないため、無制限に荷物を積む場合が発生します.
    そこで,継承方式による再利用+拡張の3つの欠点を理解した.
    では、継承、再利用、拡張ではなく、他の方法について説明します.

    くみたて


    アセンブリとは
    機能を再利用したいクラスがあれば、必要に応じて機能を提供するクラスを生成して使用できます.
    くみたてほうしき
    フィールドが他のオブジェクトを参照するように組み立てる.
    または、必要に応じてオブジェクトを作成/呼び出します.
    アセンブリの利点
    複数のオブジェクトを組み合わせて、より複雑な機能を提供できます.
    次の例を見てください.
    FlowControlで暗号化機能を使用したい.
    継承方式では、FlowControllerと暗号化機能をサポートするオブジェクトを継承して実装することができます.
    しかし、組立方式は以下の通りである.
    public class FlowController {
    private Encryptor encryptor = new Encryptor();//フィールドアセンブリ
    public void process() {
    ...
    byte[] encryptedData = encryptor.ecrypt(data):
    ...
    }
    }

    アセンブリの利点


    クラスの成長問題の解決


    機能を再利用することで,以前に見られたクラスの増加の問題を解決できる.

    圧縮、暗号化、キャッシュ機能を使用する場合は、
    左側の継承で機能を再利用するわけではありません.
    右図に示すように、この機能を提供するオブジェクトを組み合わせて使用すればよい.

    継承誤用の解決


    組立方式を用いることで,継承を誤用する可能性を低減できる.
    次のコードを見てください.
    public class Container extends ArrayList {
    private int maxSize;
    private int currentSize;
    public void put(Luggage lug) {
    if (!canContain(lug)) throw new NotEnoughSpaceException();
    super.add(lug);
    currentSize += lug.size();
    }
    ...
    }
    継承方式では、親クラスのすべての機能を外部で使用できます.
    だから継承誤用部分に記入する問題が発生する可能性があります.
    アセンブリ方式では、オブジェクトの外部で提供される機能を選択できます.
    (不要な機能は提供されません.)
    public class Container {
    private int maxSize;
    private int currentSize;
    private List luggages = new ArrayList<>();
    public void put(Luggage lug) {
    if (!canContain(lug)) throw new NotEnoughSpaceException();
    luggages.add(lug);
    }
    ...
    }
    このように機能を再利用する場合、継承よりも組立方式の方がより多くのメリットをもたらす.

    継承ではなく組立を推奨する理由


    1.余分なクラスがない


    2.誤用相続防止


    継承VS組立方式を選択


    まず、機能を再利用して組み立てることができないかどうかをチェックします.
    継承は、真のサブタイプのみが使用されます.