web-dev-qa-db-ja.com

c#インターフェースにゲッターとセッターの定義のみが含まれている場合、それはコードの匂いですか?

私が取り組んでいるプロジェクトには、インターフェースの例として次のコードがあります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    public interface IDeviceEssentials
    {
        string Model { get; set; }
        string Manufacturer { get; set; }
        string BIOSVersion { get; set; }
        string TotalPhysicalMemory { get; set; }
        string TotalVirtualmemory { get; set; }
        string OSName { get; set; }
    }
}

このインターフェースを実装する他のすべてのクラスは、ゲッターとセッターを役に立たないようにする自動プロパティを使用するので、効果的な実装は次のようになります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Test
{
    public interface IDeviceEssentials
    {
        string Model;
        string Manufacturer;
        string BIOSVersion;
        string TotalPhysicalMemory;
        string TotalVirtualmemory;
        string OSName;
    }
}

データを保持するには、単に構造またはクラスで十分です。

上記のコードがゲッターセッターを使用している場合でも、インターフェースの主な使用法はliskov置換を利用する多態性です。これは、(自動プロパティを使用して)それを実装するすべてのクラスによって変数デクレレーションに効果的に削減されます。つまり、上記のインターフェイスをモック化する必要はありません。クラスとして設計されている場合でも、新しいクラスを作成することで単純にモックを作成できます。

質問は:-

  1. 上記のプロパティはデータ表現のみを提供していると思います。データ表現のためだけにインターフェースを使用しても大丈夫ですか?
3
k4vin

いいえ、それ自体はコードの匂いではありません。自動プロパティのみを使用して多くの実装を行うことはコードの匂いです。これは、一般的なコードを基本クラスにリファクタリングし、仮想クラスとして定義して、拡張クラスが必要に応じてそれをオーバーライドできるようにする場所です。

このインターフェースを実装する他のすべてのクラスは、ゲッターとセッターを役に立たないようにする自動プロパティを使用します

いいえ、自動プロパティはインターフェース定義を役に立たなくしません。図示されている2つのインターフェースは同じではありません。実際には、インターフェースでフィールドを定義できないため、2番目のインターフェースは不正です。自動プロパティは引き続きゲッターとセッター(コンパイラーによって挿入されます)を使用するため、自動プロパティの使用はインターフェースの要件を満たします。実装でfieldpublic string Model;を宣言するだけでは、インターフェイスを満たしません。

次の画像を見るとわかるように、実装で自動プロパティを使用しましたが、getterとsetterがまだ実装されています。

enter image description here

それでも意味がない場合は、次のように考えてください。インターフェースはコントラクトを定義します。自動プロパティを使用しないように後で実装をリファクタリングすると、インターフェイスにより、ゲッターとセッターの両方が公開されることが保証されます。消費コードは、具体的な実装に直接アクセスするのではなく、インターフェイスを介して私の実装を使用します。これは、実装をリファクタリングでき、呼び出し元をまったく変更する必要がないことを意味します。

7
slugster

このインターフェースは完璧です。データストレージを担当するオブジェクトでこの種のインターフェイスを使用して、データストレージコードをコンシューマーから隠すことがよくあります。

クラス設計のSOLID原則に従う限り、インターフェースが何を定義するかは問題ではありません。

  • このインターフェイスの背後にあるクラスは、1つのことにのみ責任がありますか?
  • 拡張用に開いていて、変更用に閉じていますか?
  • それはリスコフ置換の原則に従いますか?
  • インターフェース分離の原則に従っていますか?
  • 依存関係の逆転の原則に従っていますか?

これらの各質問に「はい」と答えることができれば、クラスの設計は問題ありません。インターフェースは、この思考プロセスのほぼ副産物になります。

2
Stephen

コードの重複を心配しているようです。それを回避する1つの方法は、他の回答で言及されている抽象クラスです。しかし、継承には固有の問題がある可能性があるため、継承よりも構成を使用するという別の原則が存在します。

IDeviceEssentialsは基本的には構造体なので、自動プロパティのみを持ち、ロジックを持たない具象クラスにすることができます。次に、このクラスを、具象デバイスを表す他のクラスのメンバーとして使用します。必要に応じて、インターフェースIDeviceEssentialsProviderを導入します。

例(私はC#に堪能ではないので、調整する必要があるかもしれません):

public class DeviceEssentials
{
    string Model { get; set; }
    string Manufacturer { get; set; }
    string BIOSVersion { get; set; }
    string TotalPhysicalMemory { get; set; }
    string TotalVirtualmemory { get; set; }
    string OSName { get; set; }
}

public interface IDeviceEssentialsProvider
{
    DeviceEssentials DeviceEssentials { get; }
}

public class DeviceA implements IDeviceEssentialsProvider
{
    DeviceEssentials DeviceEssentials { get; }
}
1
jhyot