1

私は SOLID オブジェクト指向プログラミングに関連する原則を独学で学んでいますが、文字 D (依存関係反転の原則) のすべての詳細を理解するのに苦労しています。

ウィキペディア ( http://en.wikipedia.org/wiki/Dependency_inversion_principle ) でそのエントリを読んでいますが、図に示されているすべてのことを理解していません:

http://en.wikipedia.org/wiki/Dependency_inversion_principle#/media/File:DIPLayersPattern_v2.png )

2 種類の矢印があることに気付きました。1 つは破線で、もう 1 つは実線です。

私の現在の理解に基づいて、破線は Java の「implements」キーワードに相当するものを表し、実線はキーワード「extends」を表します。

これは正しい解釈ですか?

4

2 に答える 2

3

破線は、ソース コードの依存関係を意味します。空の三角形のある実線は、特別な種類の依存関係 (クラス継承またはインターフェイス実装) を意味します。

この図では、ポリシー サービスとメカニズム サービスは抽象化されており、ポリシー サービスはより高いレベルの抽象化です。メカニズムとユーティリティは詳細です。

Policy は Mechanism と Utility に対して明らかなランタイム依存関係を持ち (Policy から始まる制御フローはこれらの詳細に到達します)、下位レベルでは Mechanism は Utility に対して同様のランタイム依存関係を持ちます。ただし、ソース コード レベルでは、ポリシーはポリシー サービスのみに依存し、メカニズムもポリシー サービスに依存します。これが依存関係の逆転です。

于 2015-04-21T17:30:49.240 に答える
3

これにより、いくつかのことが解決される場合があります。

UML 矢印の説明

画像は Visual Studio のものですが、ほとんどの情報はほとんどの UML ドキュメントと同等であると思います。

破線は「implements」に相当するものを表します

UML でのエンティティの命名にも少し戸惑っています...Policyの下位レベル モジュールへのコントラクトを記述するインターフェイスに依存しているようですMechanism

ただし、継承を表すと思われる2番目(実線)では(少なくとも私は信じています)。 具体的な参照時に DIP を適用する前ではなくMechanism、インターフェイス (抽象化) を実装します。PolicyPolicyMechanism

主に伝えようとしているのは、クラスは他のクラスに依存してはならないということですが、具象ではなく抽象化 (インターフェース) に依存することはできます。

これの最も簡単な例: 下位レベルのモジュールに依存する元の Foo/Logger。

// "Low level Module" Mechanism equivilant
public class Logger {
    public void logInformation(String logInfo) {
        System.out.println(logInfo);
    }
}

// "High level module" Policy equivalent.
public class Foo {
    // direct dependency of a low level module.
    private Logger logger = new Logger();

    public void doStuff() {
        logger.logInformation("Something important.");
    }
}

上記でFooは、 の具体的な実装に依存していLoggerます。これはそのままリファクタリングできます (これを行う方法はいくつかありますが、これは 1 つにすぎません)。

public interface ILogger {
    void logInformation(String logInfo);
}

public class Logger implements ILogger {
    @Override
    public void logInformation(string logInfo) {
        System.out.println(logInfo);
    }
}

public class Foo {
    private ILogger logger;
    public void setLoggerImpl(ILogger loggerImpl) {
        this.logger = loggerImpl;
    }

    public void doStuff() {
        logger.logInformation("Something important.");
    }
}

このリファクタリングでFooは、 は に依存しなくなりましLoggerたが、インターフェイスを利用するILoggerようになりました。つまり、実行時やオブジェクトのインスタンス化などで ILogger の実装を切り替えることができます。

あなたはそのように消費することができますFoo

Foo foo = new Foo();
ILogger logger = new Logger();
foo.setLoggerImpl(logger);
foo.doStuff();

もちろん、これはコンソールに「重要な何か」と出力されます。コンソールにログを記録するのではなく、データベースにログを記録したい場合はどうなるでしょうか。

public class LoggerToDb implements ILogger {
    @Override
    public void logInformation(string logInfo) {
        DbContext databaseContext = new DbContext();
        databaseContext.insertLog(logInfo);
    }
}

次のように使用できるようになりました。

Foo foo = new Foo();
ILogger logger = new LoggerToDb();
foo.setLoggerImpl(logger);
foo.doStuff();

に依存していないFooため、実装を変更する必要がなかったことに注意してください。代わりに、このアプローチを使用すると、抽象化に新しい具象を提供し、触れることさえせずにそれを交換できます。かなりニートIMO。FooLoggerILoggerFooFoo

上記の例では、オブジェクトを構築して実装を提供していることに注意してください。これは、Java の Spring などの IOC フレームワークでも実行できます。

于 2015-04-21T17:05:02.987 に答える