疎結合コードのインターフェースの使用


10

バックグラウンド

特定の種類のハードウェアデバイスの使用状況に依存するプロジェクトがありますが、必要なことを行うのであれば、誰がそのハードウェアデバイスを作成するかは重要ではありません。そうは言っても、同じことをするはずの2つのデバイスでも、同じ製造元以外のデバイスでは違いがあります。したがって、インターフェイスを使用して、関連するデバイスの特定のメーカー/モデルからアプリケーションを分離し、代わりに、インターフェイスに最高レベルの機能をカバーさせることを考えています。これが私のアーキテクチャが次のようになると私が考えているものです:

  1. 1つのC#プロジェクトでインターフェイスを定義しますIDevice
  2. 別のC#プロジェクトで定義されたライブラリに、デバイスを表すために使用される具象があります。
  3. 具体的なデバイスにIDeviceインターフェースを実装してもらいます。
  4. IDeviceインタフェースは次のような方法かもしれないGetMeasurementかをSetRange
  5. アプリケーションに具象に関する知識を持たせ、具象をデバイスを利用する(実装しない)アプリケーションコードに渡しIDeviceます。

アプリケーションに影響を与えずに、使用中のデバイスを変更できるため(これは時々発生するようです)、これが適切な方法であると確信しています。言い換えると、具象の実装方法GetMeasurementSetRange具象を通して実際に機能する方法は重要ではありません(デバイスのメーカーによって異なる場合があります)。

私の心の中で唯一の疑問は、今やアプリケーションとデバイスの具象クラスの両方がIDeviceインターフェースを含むライブラリに依存しているということです。しかし、それは悪いことですか?

また、デバイスとIDevice同じ名前空間に属していない限り、アプリケーションがデバイスについて知る必要がない方法もわかりません。

質問

これは、アプリケーションとそれが使用するデバイスとの間の依存関係を切り離すためのインターフェースを実装するための正しいアプローチのように思えますか?


これは絶対に正しい方法です。あなたは本質的にデバイスドライバーをプログラミングしていますが、これはまさに正当な理由で、デバイスドライバーが伝統的に書かれている方法とまったく同じです。デバイスの機能を使用するには、これらの機能を少なくとも抽象的な方法で認識するコードに依存する必要があるという事実を回避することはできません。
Kilian Foth、2016年

@KilianFothええ、私は感じがありました、私の質問に一部を追加するのを忘れていました。#5を参照してください。
スヌープ2016年

回答:


5

分離されたソフトウェアがどのように機能するかについて、かなり理解していると思います:)

私の頭の中の唯一の疑問は、今やアプリケーションとデバイスの具象クラスの両方が、IDeviceインターフェイスを含むライブラリに依存していることです。しかし、それは悪いことですか?

する必要はありません!

また、デバイスとIDeviceが同じ名前空間にない限り、アプリケーションがデバイスを認識する必要がない方法もわかりません。

これらすべての懸念は、プロジェクト構造で対処できます。

私が通常行う方法:

  • すべての抽象的なものをCommonプロジェクトに入れます。のようなものMyBiz.Project.Common。他のプロジェクトは自由に参照できますが、他のプロジェクトを参照できない場合があります。
  • 抽象化の具体的な実装を作成するとき、それを別のプロジェクトに入れました。のようなものMyBiz.Project.Devices.TemperatureSensors。このプロジェクトはプロジェクトを参照しCommonます。
  • 次にClient、アプリケーションへのエントリポイントであるプロジェクト(のようなものMyBiz.Project.Desktop)があります。起動時に、アプリケーションはブートストラッププロセスを実行します。ここで、抽象化/コンクリート実装のマッピングを構成します。私は私のコンクリートをインスタンス化することができますIDevicesようにWaterTemperatureSensorIRCameraTemperatureSensor、ここで、または私は後で私のために右のコンクリートの型をインスタンス化するために、工場やIoCコンテナのようなサービスを設定することができます。

ここで重要なのは、Clientプロジェクトだけが抽象Commonプロジェクトとすべての具象実装プロジェクトの両方を認識する必要があるということです。ブートストラップコードへのabstract-> concreteマッピングを制限することで、アプリケーションの残りの部分が具体的な型を至福に気付かないようにすることができます。

疎結合コードftw :)


2
DIはここから自然に続きます。これは主にプロジェクト/コード構造の問題でもあります。IoCコンテナーがあるとDIに役立ちますが、これは必須条件ではありません。依存関係を手動で注入することでDIを実現することもできます!
MetaFight 2016年

1
@StevieVええ、アプリケーション内のFooオブジェクトがIDeviceを必要とする場合、依存関係の反転はnew Foo(new DeviceA());、FooがDeviceA自体をインスタンス化する(private IDevice idevice = new DeviceA();)内にプライベートフィールドを持つのではなく、コンストラクター()を介して注入するのと同じくらい簡単です。 Fooは最初のケースではDeviceAを認識していません
anotherdave

2
@anotherdave yoursとMetaFightの入力は非常に役に立ちました。
スヌープ2016年

1
@StevieV時間を稼ぐとき、Spring(または他のいくつかのインバージョンの制御/依存性注入フレームワークの反転)とは異なる、(「造った人」である "Uncle Bob" Martinからの)概念としてのDependency Inversionの素晴らしい紹介があります。 C♯の世界で人気のある例:))
anotherdave

1
@anotherdave間違いなくチェックアウトする予定です。ありがとうございます。
スヌープ

3

はい、それは正しいアプローチのようです。いいえ、特に実装を制御している場合は、アプリケーションとデバイスライブラリがインターフェイスに依存することは悪いことではありません。

何らかの理由でデバイスがインターフェースを常に実装するとは限らないことが懸念される場合は、アダプターパターンを利用して、デバイスの具体的な実装をインターフェースに適合させることができます。

編集

5番目の懸念事項に対処するには、次のような構造を考えてください(私はあなたがデバイスの定義を制御していると想定しています)。

コアライブラリがあります。その中には、IDeviceと呼ばれるインターフェースがあります。

デバイスライブラリには、コアライブラリへの参照があり、すべてIDeviceを実装する一連のデバイスを定義しています。さまざまな種類のIDeviceを作成する方法を知っているファクトリもあります。

アプリケーションには、コアライブラリとデバイスライブラリへの参照を含めます。これで、アプリケーションはファクトリを使用して、IDeviceインターフェイスに準拠するオブジェクトのインスタンスを作成します。

これは、懸念に対処するための多くの可能な方法の1つです。

例:

namespace Core
{
    public interface IDevice { }
}


namespace Devices
{
    using Core;

    class DeviceOne : IDevice { }

    class DeviceTwo : IDevice { }

    public class Factory
    {
        public IDevice CreateDeviceOne()
        {
            return new DeviceOne();
        }

        public IDevice CreateDeviceTwo()
        {
            return new DeviceTwo();
        }
    }
}

// do not implement IDevice
namespace ThirdrdPartyDevices
{

    public class ThirdPartyDeviceOne  { }

    public class ThirdPartyDeviceTwo  { }

}

namespace DeviceAdapters
{
    using Core;
    using ThirdPartyDevices;

    class ThirdPartyDeviceAdapterOne : IDevice
    {
        private ThirdPartyDeviceOne _deviceOne;

        // use the third party device to adapt to the interface
    }

    class ThirdPartyDeviceAdapterTwo : IDevice
    {
        private ThirdPartyDeviceTwo _deviceTwo;

        // use the third party device to adapt to the interface
    }

    public class AdapterFactory
    {
        public IDevice CreateThirdPartyDeviceAdapterOne()
        {
            return new ThirdPartyDeviceAdapterOne();
        }

        public IDevice CreateThirdPartyDeviceAdapterTwo()
        {
            return new ThirdPartyDeviceAdapterTwo();
        }
    }
}

namespace Application
{
    using Core;
    using Devices;
    using DeviceAdapters;

    class App
    {
        void RunInHouse()
        {
            var factory = new Factory();
            var devices = new List<IDevice>() { factory.CreateDeviceOne(), factory.CreateDeviceTwo() };
            foreach (var device in devices)
            {
                // call IDevice  methods.
            }
        }

        void RunThirdParty()
        {
            var factory = new AdapterFactory();
            var devices = new List<IDevice>() { factory.CreateThirdPartyDeviceAdapterOne(), factory.CreateThirdPartyDeviceAdapterTwo() };
            foreach (var device in devices)
            {
                // call IDevice  methods.
            }
        }
    }
}

この実装では、具体的にはどこに行くのですか?
スヌープ

私は自分が何をするかを決めることしかできません。デバイスを制御している場合でも、コンクリートデバイスを独自のライブラリに配置します。デバイスを制御できない場合は、アダプター用に別のライブラリーを作成します。
プライスジョーンズ

唯一のことは、アプリケーションがどのようにして私が必要とする特定の具象にアクセスできるようになるかです。
スヌープ

1つの解決策は、抽象ファクトリパターンを使用してIDeviceを作成することです。
プライスジョーンズ

1

私の頭の中の唯一の疑問は、今やアプリケーションとデバイスの具象クラスの両方が、IDeviceインターフェイスを含むライブラリに依存していることです。しかし、それは悪いことですか?

逆に考える必要があります。この方法を使用しない場合、アプリケーションはさまざまなデバイス/実装のすべてについて知る必要があります。覚えておくべきことは、そのインターフェイスを変更すると、アプリケーションが既に一般に公開されている場合、アプリケーションが壊れる可能性があるため、そのインターフェイスを注意深く設計する必要があるということです。

これは、アプリケーションとそれが使用するデバイスとの間の依存関係を切り離すためのインターフェースを実装するための正しいアプローチのように思えますか?

単にはい

コンクリートが実際にアプリに到達する方法

アプリケーションは、実行時に具象アセンブリ(dll)をロードできます。参照:https : //stackoverflow.com/questions/465488/can-i-load-a-net-assembly-at-runtime-and-instantiate-a-type-knowing-only-the-na

そのような「ドライバー」を動的にアンロード/ロードする必要がある場合は、アセンブリを別のAppDomainにロードする必要があります。


ありがとう、私が最初にコーディングを始めたとき、インターフェースについてもっと知りたいです。
スヌープ

申し訳ありませんが、一部を追加するのを忘れていました(コンクリートが実際にアプリに到達する方法)。あなたはそれに対処できますか?#5を参照してください。
スヌープ2016年

実際にこの方法でアプリを実行して、実行時にDLLをロードしますか?
スヌープ

たとえば具体的なクラスが私に知られていない場合は、はい。デバイスベンダーがIDevice自分のデバイスをアプリで使用できるようにインターフェイスを使用することを決定したと仮定すると、IMOには他の方法はありません。
Heslacher、2016年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.