私はオブジェクト指向設計に慣れておらず、インターフェースと設計パターンについて学びます。この例では、自動車のクラスを作成しようとしています。私の質問:
基本クラスを使用してから継承することは良い習慣ですか?そして、さまざまな機能を実装するためにインターフェースを使用しますか?そうでない場合、これを実装する最良の方法は何でしょうか。
Base Class:
class BaseCar
{
public string color { get; set; }
public double price { get; set; }
public string carType { get; set; }
}
Interface:
interface ICarFunctions
{
void brakeSystem();
void drivingModes(string mode);
void entertainmentSystem();
}
今私は具体的なクラスを作成しようとしています
class BmwCar : BaseCar, ICarFunctions
{
public void brakeSystem()
{
Console.WriteLine("Brake in less than 0.02ms");
}
public void drivingModes(string mode)
{
switch(mode)
{
case "mountain":
{
Console.WriteLine("Switching to 4x4 mode");
break;
}
default:
{
Console.WriteLine("Normal Mode");
break;
}
}
}
public void entertainmentSystem()
{
Console.WriteLine("Music, Navigation");
}
}
今私の質問は:基本クラスを使用してから継承することは良い習慣ですか?そして、さまざまな機能を実装するためにインターフェースを使用しますか?
私の2番目の質問は、インターフェイスに1つの機能のみ(単一の責任の原則)があるべきか、それとも実装する必要がある複数の機能を持つことができるかということです。
基本クラスを使用してから継承することは良い習慣ですか?そして、さまざまな機能を実装するためにインターフェースを使用しますか?
メソッドはインターフェースを介して実装する必要があり、フィールド/プロパティはクラスで継承/定義する必要があることを示す固有の要件はありません。これは、クラスのフィールド/プロパティとそのメソッドの間に意味のある関係が存在することを本質的に妨げているため、ロジックを分離するための良い方法ではありません。
明らかな制限の1つは、インターフェイスが public メンバーのみを処理することです。手紙にアプローチを適用した場合、プライベートクラスメソッドを使用することは不可能です。
同様に、インターフェイスを介してパブリックプロパティを定義することはできません。これは、インターフェイスの使いやすさを妨げます。簡単な例として、 IList はCount
やIsReadOnly
などのパブリックプロパティを公開します。メソッド(GetCount()
およびIsReadOnly()
)を使用して技術的に処理できますか?もちろんですが、パブリックアクセスのメソッドのみを使用するように強制する意味は何ですか?パブリックプロパティが存在する理由は次のとおりです:必要なすべてのパブリックにアクセス可能なフィールドに対して記述する必要があるボイラープレートgetFoo()
/setFoo()
メソッドを大幅に削減するため。
もう1つの制限は、BaseCar
にbrakeSystem()
メソッドがあることをコードで定義しないことです。たとえば、必要なインターフェイスを実装しないBaseCar
を作成しても問題はありません。
_public class FlaterCar : BaseCar { }
_
コンパイラはこれで問題はありません。基本クラスとインターフェースの両方を互いに組み合わせて使用することを期待していると述べたことはありません。これは、anyBaseCar
を処理してブレーキをかけることができるメソッドを作成できないことを意味します。不可能だよ:
_public void MakeThisCarBrake(BaseCar car)
{
car.brakeSystem(); //compiler error, brakeSystem is not a known method of BaseCar
}
_
これは、多くの一般的なポリモーフィズムのユースケースで基本クラスを使用できなくなり、LSPおよびOCPに違反するif(car is ICarFuntions) car.brakeSystem();
を常に実行しなければならなくなることを意味します。
あなたが期待することは、実際の派生型が実装する可能性のあるインターフェースについて推測する必要なく、基本型を処理できることです。
_public void LetThisAnimalMakeASound(Animal animal)
{
animal.MakeSound();
}
_
ただし、これには、基本クラスと派生クラスの両方が同じメソッドシグネチャを共有する必要があります(派生クラスが基本クラスのメソッド本体をオーバーライドするかどうかに関係なく)。
_public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine(" ...silence... ");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof woof!");
}
}
public class Duck : Animal
{
public override void MakeSound()
{
Console.WriteLine("Quack!");
}
}
_
メソッドをインターフェイスに配置する場合、これが確実に機能する唯一の方法は、baseクラスにインターフェイスを実装させることです。
_public interface IMakeSound
{
void MakeSound();
}
public class Animal : IMakeSound
{
public virtual void MakeSound()
{
Console.WriteLine(" ...silence... ");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
Console.WriteLine("Woof woof!");
}
}
_
派生クラスはインターフェース自体を直接実装せず、基本クラスからインターフェース実装を継承するだけです(そして、それをオーバーライドすることを選択しますが、それは要件ではなく、基本メソッドをそのまま維持することを選択できます)。
しかし、その時点に到達すると、基本クラスで実装を定義する必要があるため、インターフェイスが不要になるインターフェイスが不要になります。つまり、インターフェイス might は無意味な2番目のレイヤーになる;このインターフェイスが多くの異なるクラスで使用され、基本クラスと比較して独自の(異なる)機能的な目的がない限り。
私の2番目の質問は、インターフェイスに1つの機能(単一の責任の原則)のみを含めるか、実装する複数の機能を含めることができるかです。
メソッドの数に関する規則はありません。 機能的独立性がすべてです。詳細については、SOLIDのIである インターフェース分離の原則 (ISP)を参照してください。 (注:1つのリンクを選択しただけで、他にも多くの参照が存在します)。
要するに、インターフェースを分割できるかどうかということになります。あなたのケースを取ってみましょう:
_public interface ICarFunctions
{
void brakeSystem();
void drivingModes(string mode);
void entertainmentSystem();
}
_
あなたが自問すべき最初の質問:これはパッケージ取引ですか?ブレーキシステムを備えたすべての車にエンターテイメントシステムがありますか?運転モードのあるすべての車にブレーキはありますか?
ここでの文脈上の答えはnoです。ブレーキがある場合、エンターテインメントシステムが必要であるという固有の要件はありません。そのため、このインターフェースを個別のインターフェースに分割できます。
ただし、これは、インターフェイスが持つことができるメソッドは1つだけであるということと同じではありません。インターフェースを分割できない場合があります。例:
_public interface IEntertainmentSystem
{
void IncreaseVolume();
void DecreaseVolume();
void Play();
void Pause();
void Previous();
void Next();
}
_
これらの標準的なメディアプレーヤー関数は、パッケージ取引と見なすことができます。これらの機能の some のみを持つことは意味がありません。したがって、これらの機能のサブセットのみを使用し、残りの機能を使用するのは意味がないため、このインターフェースをこれ以上分割しないでください。
基本クラスを使用してから継承することは良い習慣ですか?そして、さまざまな機能を実装するためにインターフェースを使用しますか?
記憶された状態を提供するための基本クラスと、メソッドを提供するためのインターフェースを示しています。次に具象クラスは「状態付きベース」を拡張し、いくつかの動作を提供するインターフェースも実装します。そして、あなたはこれが良い習慣かどうか尋ねています。答えは、これは状況によっては問題ないが、それが唯一の方法ではないということです。表示している例では、これは不必要な抽象化です。提供されている抽象化(図には示されていません)を十分に使用している可能性がありますが、提供されている例ではそれらの使用法はありません。これを単独で実行する理由はありません。何も問題はありませんが、ここで示しているより単純な例以上の動機が必要です。
ベースもデフォルトの実装を提供するインターフェースを実装する例を簡単に示すことができるので、BMWや他の同様のサブクラスは、それ以上実装する必要がまったくありません。 (上記の例を最後の努力として取った場合、基本クラスとインターフェースさえ必要とされなかったと言えるかもしれません。)
これらのことでよく起こるように、それは文脈に依存します。私の頭の中で一番重要なのは、これらが消費するプログラマーによってどのように使用されるかです。消費するクライアントプログラマーがこれらを作成して使用するのは非常に簡単です。次に、共通ベースから派生するサブクラスを実装するプログラマーがこれらを使用する方法です。 、ベースの定義方法によって3番目(および最後)。私はこれをピラミッドと見なします。必要な場合(通常はそうではありませんが)、ほとんどのユーザー(一番下)に、最も簡単に使用できる抽象化を提供する必要があります。 。
Noおよびno。
サブクラスのデータを保持する目的で基本クラスを使用することはできません。実際には、パブリックプロパティを持つことは、一般的にオブジェクト指向に反しています。オブジェクト指向はbehaviorを扱い、データ実装の詳細を考慮します。
インターフェースは1つのメソッドに限定されず、限られたメソッドのセットよりも良くも悪くも考えられていません。また、単一の責任!=単一のメソッド。 「単一の責任」の代わりに、より明確に定義された「結合」および「結合」の概念を使用することをお勧めします。