C#でクラス間(コースの複数の継承なし)で共有動作を実装する方法
-
03-07-2019 - |
質問
更新:
だから、ここのほとんどの人は私にクラスの設計方法をもう一度やり直す必要があることを教えてくれました(皆さん、素晴らしい回答をありがとう!)ヒントをとって、私は戦略パターンについて広範囲にわたる読書を始めました。 1つまたは複数の抽象基本クラスから継承する動作クラス(または戦略クラス)を作成したい。 Candidateクラスには、動作または戦略の Type
として、さまざまな抽象基本クラスのクラスを持つプロパティがあります。多分このようなものです:
public abstract class SalaryStrategy {
public abstract decimal Salary { get; set; }
public abstract decimal Min { get; set; }
public abstract decimal Mid { get; set; }
public decimal CompaRatio {
get {
if (this.Mid == 0) { return 0; }
else { return this.Salary / this.Mid; }
}
}
}
public class InternalCurrentSalaryStrategy {
public override decimal Salary { get; set; }
public override decimal Min {
get { return this.Salary * .25m; }
set { }
}
public override decimal Mid { get; set; }
}
public class Candidate {
public int Id { get; set; }
public string Name { get; set; }
public SalaryStrategy CurrentSalaryStrategy { get; set; }
}
public static void Main(string[] args) {
var internal = new Candidate();
internal.CurrentSalaryStrategy = new InternalCurrentSalaryStrategy();
var internalElp = new Candidate();
internalElp.CurrentSalaryStrategy = new InternalCurrentSalaryStrategy();
var elp = new Candidate();
// elp.CurrentSalaryStrategy can stay null cause it's not used for elps
}
コメントや提案はありますか
元の質問:
私は、設計パターンと原則を習得し、より熟達しようとしています。私は現在、私を困惑させたいくつかのクラスの設計に取り組んでいます。コードの非常に要約されたバージョンを次に示します。
public class Candidate {
public int Id { get; set; }
public string Comments { get; set; }
// lots more properties and behaviors...
}
public class InternalCandidate : Candidate {
public decimal CurrentMid { get; set; }
public decimal CurrentMax {
get { return this.CurrentMin * 1.3m;
}
// lots more properties and behaviors...
}
public class EntryLevelCandidate : Candidate {
public string Gpa { get; set; }
// lots more properties and behaviors...
}
public class InternalEntryLevelCandidate /* what do I inherit here??? */ {
// needs all of the properties and behaviors of
// EntryLevelCandidate but also needs the CurrentMin and
// CurrentMax (and possibly more) in InternalCandidate
}
InternalEntryLevelCandidateクラスは、主にEntryLevelCandidateですが、InternalCandidateのいくつかの実装を共有する必要があります。実装を異なる、または繰り返したくないので、実装と言います。そうでなければ、共通のコントラクトにインターフェイスを使用し、各クラスに具体的な実装を持ちます。 InternalCandidateプロパティおよび動作の実装の一部は、共通または共有する必要があります。 C ++とRubyのミックスインについて読んだことがありますが、これは私がやりたいことと似ているようです。また、この興味深いブログ記事を読んで、クラスが複数のビヘイビアを継承しながら、単一の「is a」を維持できるビヘイビアタイプのアイデアについて説明します。関係: http:// www。 deftflux.net/blog/post/A-good-design-for-multiple-implementation-inheritance.aspx 。これは私が望んでいることを伝えているようです。優れた設計手法を使用してこれを達成する方法について、誰かが私に指示を与えることができますか?
解決
不変データ値クラス。さまざまな候補サブクラスのプロパティが何らかの意味のあるデータ値を表す場合、必要な動作を備えた不変クラスを作成します。 。個別のCandidateサブクラスはそれぞれデータ型を使用できますが、コードは引き続きデータクラスにカプセル化されます。
拡張メソッド。これらはオーバーロードされ、任意のクラスで動作します。
デコレータパターンを避け、コンパイル/リフレクタブル機能を使用します。
構成。候補クラスに独自の動作を記述し、関連する用途で機能を引き出すのではなく、個別のクラスで独自の動作をすぐに開発し、それらの周りに候補クラスを構築する後でクラス。
クラスの使用方法に応じて、関連する型への明示的および暗黙的な変換演算子を実装して使用することもできるため、インターフェイスを再実装するのではなく(回避したい)、実際にオブジェクトをどんな目的にも必要なタイプ/実装。
最後の段落に関連して考えたもう1つのことは、リースシステムを使用することです。このシステムでは、適切なタイプのクラスが生成され、オブジェクトが操作され、更新された情報を吸収するために後で消費されます。
他のヒント
これは、私がかなり興味深いと思うテーマに関する学術論文です( PDFリンク)。
しかし、あなたはあなたの一般化にビジネスロジックを課そうとしていると思います。あなたは、InternalCandidateがGPAを見ることは決してないことを知っています。しかし、InternalCandidateには確かにGPAがあります。このため、InternalEntryLevelCandidateと呼ばれるこの奇妙な男を解任しました。これは、この男のGPAを確認したいことを知っているからです。建築的には、EntryLevelCandidateは間違っていると思います。 「レベル」を追加します候補者に概念と彼にGPAを与えます。 GPAを検討するかどうかを決定するのは、ビジネスロジック次第です。
編集:また、 Scott Meyers は、この問題を詳しく分析しています。彼の本。
免責事項:
私の経験では、ルールではなく多重継承が必要ですが、クラス階層を慎重に設計すると、通常、この機能が不要になります。この要件をサンプルで回避できることに、JPに同意します。
質問に戻って、明確な解決策はありませんが、いくつかのオプションがあります:
-
拡張メソッドを使用すると、右クリックで「解決」が機能しないという欠点があります。また、これらの子犬が嫌いな人もいます。
-
合成する各クラスのインスタンスを保持する集合オブジェクトを作成し、委任するスタブメソッドを再実装します。
-
各動作のインターフェースを定義し、動作を実行する前にベースのメソッドに
this is IInterface
をチェックさせます。 (動作定義をベースにプルできます)
ほぼ重複: C#の多重継承
委任パターンを使用するだけです。最終的には、機能の個別の部分ごとにインターフェイスを使用し、各インターフェイスのデリゲートとして具体的なクラスを用意します。その後、最終クラスは必要なデリゲートを使用するだけで、複数のインターフェイスから継承できます。
public class InternalEntryLevelCandidate : EntryLevelCandidate {
private InternalCandidate internalCandidateDelegate
= new InternalCandidate();
public decimal CurrentMid {
get { return internalCandidateDelegate.CurrentMid; }
set { internalCandidateDelegate.CurrentMid = value; }
}
public decimal CurrentMax {
get { return internalCandidateDelegate.CurrentMax }
}
}