共通の機能のために基本クラスから継承させたいクラスが複数ある場合、クラスまたは抽象クラスを使用して基本クラスを実装する必要がありますか?
基本クラスをインスタンス化できないようにしたい場合は、それを抽象化してください。それ以外の場合は、通常のクラスのままにします。
基本クラスをインスタンス化する必要がない場合は、抽象クラスにします。基本クラスをインスタンス化する必要がある場合は、抽象クラスにしないでください。
この例では、基本クラスには具体的な意味がないため、基本クラスを抽象化することは理にかなっています。
abstract class WritingImplement
{
public abstract void Write();
}
class Pencil : WritingImplement
{
public override void Write() { }
}
ただし、この次の例では、基本クラスが具体的な意味を持っていることがわかります。
class Dog
{
public virtual void Bark() { }
}
class GoldenRetriever : Dog
{
public override void Bark() { }
}
それはすべてかなり主観的です-あなたはあなたの特定のドメインのニーズに基づいてかなり良い判断の電話をかけることができるはずです。
問題の基本クラスが派生せずにそれ自体で存在することは理にかなっていますか?答えが「はい」の場合は通常のクラスである必要があり、そうでない場合は抽象クラスである必要があります。
私は提案します:
抽象クラスではなく実際のクラスを好む理由は、抽象クラスをインスタンス化できないため、将来のオプションが制限されるためです不必要に。たとえば、後で基本クラスによって提供される状態とメソッドが必要になる場合がありますが、継承できず、インターフェイスを実装する必要はありません。基本クラスが抽象クラスの場合は運が悪いですが、基本クラスが通常のクラスの場合は、基本クラスのインスタンスを作成して他のクラスのコンポーネントとして保持し、インスタンスに委任して再利用できます。提供される状態/メソッド。
はい、これは頻繁には発生しませんが、重要なのは、基本クラスを抽象化することで、理由がない場合にこの種の再利用/ソリューションを防ぐことができるということです。
さて、基本クラスをインスタンス化することが何らかの形で危険である場合は、それを抽象化するか、可能であれば危険性を低くすることが望ましいです;-)
銀行口座のように考えてください。
「アカウント」と呼ばれる一般的な抽象ベースアカウントを作成できます。これは、顧客の詳細などの基本情報を保持します。
次に、「SavingAccount」または「DebitAccount」と呼ばれる2つの派生クラスを作成できます。これらのクラスは、基本クラスの動作の恩恵を受けながら、独自の特定の動作を持つことができます。
これは、顧客が普通預金口座またはデビット口座のいずれかを持っている必要がある状況です。一般的な「口座」は、説明のない口座だけを持つことは現実の世界ではあまり一般的ではないため、許可されていません。
ニーズに合わせて同様のシナリオを作成できる場合は、抽象化が最適です。
抽象クラスは、部分的に実装されたクラス用です。
抽象クラスのインスタンスを持つこと自体は意味がなく、派生する必要があります。基本クラスを作成できるようにしたい場合は、抽象にすることはできません。
抽象クラスは、すべてのサブクラスに共通であるため、いくつかのメンバーが事前定義されているインターフェイスと考えるのが好きです。
これを別の方法で考えてください
私の基本クラスはそれ自体で完全なオブジェクトですか?
答えが「いいえ」の場合は、抽象化します。はいの場合は、具体的なクラスにしたい可能性があります。
基本クラスを単独で呼び出す予定がない場合は、抽象クラスとして定義する必要があります。
は、基本クラスを単独で実装するかどうかによって異なります。
抽象クラスとして、それからオブジェクトを作成することはできません。
抽象クラスは、事前定義された機能に最適です。たとえば、クラスが公開する必要のある最小限の正確な動作はわかっているが、それを実行するために使用する必要のあるデータや正確な実装はわかっていない場合です。
abstract class ADataAccess
{
abstract public void Save();
}
通常の(非抽象)クラスは同様のことに最適ですが、それらを記述できるようにするには、実装の詳細を知っている必要があります。
public class DataAccess
{
public void Save()
{
if ( _is_new )
{
Insert();
}
else if ( _is_modified )
{
Update();
}
}
}
また、インターフェイス(個別に、または抽象かどうかに関係なくクラスで)を使用して、同じ種類のプロトタイプ定義を定義することもできます。
interface ISaveable
{
void Save();
void Insert();
void Update();
}
class UserAccount : ISavable
{
void ISavable.Save() { ... }
void ISavable.Insert() { ... }
void ISavable.Update() { ... }
}
さらに別のオプションはジェネリックを使用することかもしれません
class GenDataAccess<T>
{
public void Save()
{
...
}
}
これらすべてのメソッドを使用して、使用するクラスの特定のプロトタイプを定義できます。コードAがコードBと通信できることを確認する方法。もちろん、上記のすべてを好みに合わせて組み合わせることができます。明確な正しい方法はありませんが、インターフェイスと抽象クラスを定義してから、インターフェイスを参照するのが好きです。このようにして、最大限の柔軟性を維持しながら、より高いレベルのクラスで「配管」するための思考要件の一部を排除します。 (インターフェイスがあると、抽象基本クラスを使用する必要がなくなりますが、オプションとして残されます)。