1

更新:ここにいるほとんどの人が、クラスの設計方法を最初からやり直す必要があると言っています(ちなみに、素晴らしい回答をありがとう!)。ヒントを得て、私は戦略パターンを読み始めました。1 つまたは複数の抽象基本クラスから継承する動作クラス (または戦略クラス) を作成したいと考えています。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 の mixin について読んだことがありますが、これは私がやりたいことに似ているようです。また、単一の「is a」関係を維持しながら、クラスが複数の動作を継承できる動作タイプのアイデアについて説明している興味深いブログ投稿も読みました。http://www.deftflux.net/blog/post/A-good-design-for-multiple-implementation-inheritance.aspx . これは私が望んでいることを伝えているようです。優れた設計手法を使用してこれを達成する方法について、誰かが私に指示を与えることができますか?

4

5 に答える 5

5

不変のデータ値クラス。さまざまな Candidate サブクラスのプロパティが 何らかの意味のあるデータ値を表している場合は、必要な動作を備えた不変クラスを作成します。個別の Candidate サブクラスはそれぞれデータ型を使用できますが、コードはデータ クラスにカプセル化されたままです。

拡張方法。 これらは、任意のクラスで動作するようにオーバーロードできます。

私はデコレータ パターンを避け、コンパイル済み/反映可能な機能に固執します。

構成。 Candidate クラスに独自の動作を記述し、後で関連するクラスで使用するためにそれらの機能を引き出しようとするのではなく、個別のクラスで独自の動作をすぐに開発し、それらの周りに Candidate クラスを構築します。

クラスの使用方法に応じて、関連する型への明示的および暗黙的な変換演算子を実装して使用することもできるため、(回避したかった) インターフェイスを再実装する代わりに、実際にオブジェクトを型/実装にキャストできます。どんな目的でも必要です。

最後の段落に関連して、私がちょうど考えたもう 1 つのことは、リース システムを用意することです。このシステムでは、クラスが生成され、適切な型のオブジェクトが操作可能になり、後でそれを消費して更新された情報を吸収します。

于 2009-04-29T23:45:06.007 に答える
4

これは、私がかなり興味深いと思う主題に関する学術論文です ( PDF リンク)。

しかし、一般化にビジネスロジックを押し付けようとしていると思います。あなたはたまたま、InternalCandidate が自分の GPA を調べられることは決してないことを知っています。しかし、InternalCandidate には確かに GPA があります。それで、InternalEntryLevelCandidate と呼ばれるこの奇妙な男をクラックしたのは、たまたまこの男の GPA を見たいと思っているからです。アーキテクチャ的に、EntryLevelCandidate は正しくないと思います。候補者に「レベル」の概念を追加し、GPA を与えます。GPA を参照するかどうかを決定するのは、ビジネス ロジック次第です。

編集: また、Scott Meyersは著書の中でこの問題を分析する素晴らしい仕事をしています。

于 2009-04-29T23:31:39.470 に答える
2

免責事項

私の経験では、多重継承が必要なのは規則ではなく例外であり、クラス階層を注意深く設計することで、通常、この機能の必要性を回避できます。私は、この要件をサンプルで回避できるというJPに同意します。

質問に戻ると、明確な解決策はありませんが、いくつかの選択肢があります。

  • 拡張メソッドを使用すると、右クリックで[解決]が機能しないという欠点があります。また、これらの子犬が本当に嫌いな人もいます。

  • 合成する各クラスのインスタンスを保持する集約オブジェクトを作成し、委任するスタブメソッドを再実装します。

  • this is IInterface各ビヘイビアーのインターフェースを定義し、ビヘイビアーを実行する前に、ベースのメソッドにチェックを依頼します。(動作定義をベースにプルすることができます)

ほぼ重複: C#での多重継承

于 2009-04-29T23:28:13.723 に答える
1

私は、継承がここでは正しいことではないように思われることに同意します。完璧な答えがわからないのですが、おそらくデコレータパターンが適切です。

もう1つの、より難解なアイデアは、アスペクト指向プログラミングについて考えることです。あなたはアスペクトでかなり驚くべきことをすることができますが、それは私がまだ習得していない非常に高度なトピックです。リカード・オーバーグと彼のQi4Jコホートのような人々がいます。

于 2009-04-29T23:16:01.380 に答える
0

Delegation patternを使用するだけです。最終的には、機能の個別の部分ごとにインターフェイスを使用し、各インターフェイスのデリゲートとして具象クラスを使用します。その後、最終的なクラスは必要なデリゲートを使用するだけで、複数のインターフェイスから継承できます。

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 }
    }
}
于 2009-04-29T23:46:20.480 に答える