7

私はオープンクローズドの原則を調べていて、それは良さそうだったので、その教えを実践したかったのです。新たに発見した知識を既存のプロジェクトに適用することを検討しましたが、すぐに行き詰まりました。

新しい UserType が登場した場合 (その可能性は非常に高い)、これを変更する必要がありますが、まだ変更を受け付けていません。どうすればこれを回避できますか?

私が読んだことから、OCP を適用する代わりに、ここにファクトリを実装する必要があるように思えますか?

開閉原則を破る工場

 private void BuildUserTree(User user)
    {
        switch (user.UserType)
        {
            case UserType.FreeLoader:
                BuildFreeLoaderTree();
                break;
            case UserType.Premium:
                BuildPremiumTree();
                break;
            case UserType.Unlimited:
                BuildUnlimitedTree();
                break;
            default:
                throw new Exception("No UserType set");

        }
    }

ありがとう、コハン

4

4 に答える 4

8

他の「原則」と同様に、OCP は常に従わなければならない規則ではありません。

「継承よりも構成を優先する」ように言われていますが、デコレータやコンポジットなどのパターンは継承を公然と促進しています。

同様に、「実装ではなくインターフェイスにプログラムするように言われていますが、アプリケーションのある時点で、何らかの記述の具体的なオブジェクトをインスタンス化する必要があります.

あなたのソリューションは、古典的なファクトリ イディオムです (完全なファクトリ メソッドまたは抽象ファクトリ パターンではないにしても)。これが目的です。それにOCPを適用しようとしても意味がありません。

実際、このメソッドを作成することで、コードベースの他の部分で OCP を実際に促進することができます。アプリ内の他のクラスまたはクラスの一部は、作成を分離したので、OCP に従うことができます。

于 2011-08-22T15:21:49.443 に答える
5
internal class UserTreeBuilder
{
    [ImportMany(AllowRecomposition=true)]
    public IEnumerable<IBuilder> Builders{ get; set; }

    public UserTreeBuilder()
    {
        // Load all builders from a MEF CompositionContainer
    }

    public void BuildUserTree(User user)
    {
        var builder = Builders.FirstOrDefault(b => b.CanHandleUserType(user.UserType));

        if(builder == null)
        {
            throw new Exception("No UserType set");
        }else{
            builder.BuildTree();
        }
    }
}

利用可能なビルダーのリストは、 MEFを使用してビルドできます。

于 2011-08-22T15:10:26.693 に答える
2

タイプの切り替えをなくすには、責任をタイプ固有のアクションを必要とするタイプに戻す必要があります。このタイプ、あなたの場合は「ユーザー」は、自分自身に関するすべての情報を持っており、この知識に基づいて適切な操作を簡単に呼び出すことができます。そして、継承を利用する必要があります。

あなたの場合、単純な継承または構成を介してユーザータイプを反映する必要があります。あなたの「ユーザー」は、あなたの例のようにプロパティ「UserType」を持っていますが、「Enum」のようなタイプにする代わりに、「IUserType」インターフェイスを継承し、その特定の構築方法を知っている複雑なタイプになります依存関係 (「UserType」は「IUserType」を実装します)。「IUserType」は、プロパティを介して型固有の属性を公開できます (たとえば、「ITypeSpecificTree」を返す「IUserType.TypeSpecificTree」)。

したがって、あなたの例で「ユーザー」がプレミアムに昇格した場合、プロパティを具体的な「IUserType」実装(PremiumUserTypeなど)の新しいインスタンスに設定するだけで、プレミアムツリー(「ITypeSpecificTree」)の構築などの特定のアクションを実行します" 実装) を例から作成し、関連する型を構築します。

このように、構成と継承を使用することで、switch ステートメントが削除されます。複雑な「UserType」プロパティを別のクラスに変換し、タイプ固有の責任をタイプ自体に移しました。継承、特に依存関係の反転は、具体的な型を知らなくても、オブジェクトを操作するのに役立ちました (たとえば、 (User.IUserType.IUserSpecificTree") のようなユーザー型固有の情報を取得する)。型固有の動作をカプセル化して、コードを修正用に閉じます

タイプ固有のツリーの生成方法またはこのユーザー タイプの動作を変更する必要がある場合は、関連する「IUserType」実装のみを変更し、「User」には変更を加えません。新しいユーザー タイプが追加された場合 (拡張機能)、それらは基本インターフェイス "IUserType" を実装する必要があり、switch ステートメントなどの他のコードを変更して機能させる必要はなく、タイプ チェックは必要ありません。また、それを完成させて拡張性を高めるために、「User」クラスは、ユーザー タイプ (「IUser.IUserType」など) を公開する「IUser」などのインターフェイスも実装する必要があります。

于 2018-05-04T13:12:10.150 に答える