2

異なるシグネチャを持つメソッドがいくつかあるとします。いくつかの外部コードに基づいて、このすべてのメソッドが呼び出される可能性があります。あなたはそれらをいくつかのイベントハンドラーと考えるかもしれません。

今、私はそのように2つの実装を持つ必要があります:

  • 各実装は、実際にはすべての可能なイベントの一部のみを処理します。
  • 各実装は、処理したくない/処理できないすべてのイベントに対して何もしません。

もちろん、考えられるすべてのハンドラーのインターフェースを宣言することもできますが、その場合は、各実装で空のメソッド(ハンドラー)を作成する必要があります。それらのイベントでさえ、私は処理したくない/処理できません。

私は次のようなことを考えています:

abstract class Base
{
    public virtual void First(int i, double d) { /* no implementation */ }
    public virtual void Second(double d) { /* no implementation */ }
    public virtual void Third(string s, int i) { /* no implementation */ }
}

class Child : Base
{
    public override void First(int i, double d) { /* implementation */ }
    public override void Second(double d) { /* implementation */ }
}

class AnotherChild : Base
{
    public override void Second(double d) { /* implementation */ }
    public override void Third(string s, int i) { /* implementation */ }
}

このアプローチでは、基本抽象クラスで可能なすべてのハンドラーに対して空の実装を作成する必要があります。

もっと良いものをお勧めしますか?多数の空のメソッドを生成する必要がないアプローチですか?

私はC#2.0を使用していますが、このタスクに新しいバージョンの言語を使用できません。

4

2 に答える 2

4

私は@usrに同意します-空の関数に問題はありません。関数を呼び出す場合は、存在する必要があります。場合によっては何もしない場合、その関数は空でなければなりません。同じ空の関数の実装を何度も必要とするインターフェイスに対して、空の関数を持つ基本クラスは非常に良い考えのようです。

別の方法を探している場合は、Chain of Responsibility設計パターンを検討できます。特定の関数を呼び出すのではなく、一般的な関数を呼び出して、目的の動作をパラメーター化することができます。次に、オブジェクトをチェーン (さまざまな状況でのさまざまなチェーン) にして、すべてのオブジェクトに動作を処理する機会を与えることができます。誰もそれを処理しない場合、何も起こりません。

これはいくつかのシナリオでは非常にうまく機能しますが、非常にシンプルで洗練された基本クラスのアプローチよりも実装が複雑です。オーバーエンジニアリングしないように注意してください。


これは、質問で示した例に基づいて、一連のコマンドを実装する例です。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication2 {

    interface ICommand {
        bool Execute( string action, params object[] parameters );
    }

    class Program {
        static void Main( string[] args ) {

            CommandChain l_chain1 = new CommandChain( new FirstCommand(), new SecondCommand() );
            CommandChain l_chain2 = new CommandChain( new SecondCommand(), new ThirdCommand() );

            // Chain 1

            if ( l_chain1.Execute( "first", (int) 1, (double) 1.1 ) )
                Console.WriteLine( "Chain 1 executed First" );
            else
                Console.WriteLine( "Chain 1 did not execute First" );

            if ( l_chain1.Execute( "second", (double) 1.2 ) )
                Console.WriteLine( "Chain 1 executed Second" );
            else
                Console.WriteLine( "Chain 1 did not execute Second" );

            if ( l_chain1.Execute( "third", "4", (int) 3 ) )
                Console.WriteLine( "Chain 1 executed Third" );
            else
                Console.WriteLine( "Chain 1 did not execute Third" );

            // Chain 2

            if ( l_chain2.Execute( "first", (int) 1, (double) 1.1 ) )
                Console.WriteLine( "Chain 2 executed First" );
            else
                Console.WriteLine( "Chain 2 did not execute First" );

            if ( l_chain2.Execute( "second", (double) 1.2 ) )
                Console.WriteLine( "Chain 2 executed Second" );
            else
                Console.WriteLine( "Chain 2 did not execute Second" );

            if ( l_chain2.Execute( "third", "4", (int) 3 ) )
                Console.WriteLine( "Chain 2 executed Third" );
            else
                Console.WriteLine( "Chain 2 did not execute Third" );

            Console.ReadKey( true );

        }
    }

    class CommandChain {

        private ICommand[] _commands;

        public CommandChain( params ICommand[] commands ) {
            _commands = commands;
        }

        public bool Execute( string action, params object[] parameters ) {
            foreach ( ICommand l_command in _commands ) {
                if ( l_command.Execute( action, parameters ) )
                    return true;
            }
            return false;
        }

    }

    class FirstCommand : ICommand {
        public bool Execute( string action, params object[] parameters ) {
            if ( action == "first" &&
                parameters.Length == 2 &&
                parameters[0].GetType() == typeof( int ) &&
                parameters[1].GetType() == typeof( double ) ) {

                int i = (int) parameters[0];
                double d = (double) parameters[1];

                // do something

                return true;
            } else
                return false;
        }
    }

    class SecondCommand : ICommand {
        public bool Execute( string action, params object[] parameters ) {
            if ( action == "second" &&
                parameters.Length == 1 &&
                parameters[0].GetType() == typeof( double ) ) {

                double d = (double) parameters[0];

                // do something

                return true;
            } else
                return false;
        }
    }

    class ThirdCommand : ICommand {
        public bool Execute( string action, params object[] parameters ) {
            if ( action == "third" &&
                parameters.Length == 2 &&
                parameters[0].GetType() == typeof( string ) &&
                parameters[1].GetType() == typeof( int ) ) {

                string s = (string) parameters[0];
                int i = (int) parameters[1];

                // do something

                return true;
            } else
                return false;
        }
    }

}

(この例は、すべてのプログラミングのベスト プラクティスに従っているわけではないことに注意してください。このコードをそのまま実装することはお勧めしません。たとえば、アクション パラメーターは文字列よりも列挙型の方が適切であり、ブール値ではなくある種の CommandResult を返します。 . インスピレーションのみに使用してください。)

于 2012-09-18T17:20:08.010 に答える
1

補足: これがテスト目的の場合は、インターフェイスをモックして、ほとんどコードを使用せずに何もしない実装をすぐに作成できます。同様に、インターフェイスを部分的にのみ実装するクラスで不足しているすべてのメソッドを具体化することもできます。テスト用ではありません-本番環境でも使用できますが、..本番環境でモックを使用するのは少し臭いがあり、「ハンドラーっぽい」コントラクトのボイラープレートベースの空の実装を持つことは本当に悪いことではなく、実際には良いことです. Cyborgx37 が既に述べたように、さらなるコードのベースとして持つこと。

于 2012-09-18T18:09:50.960 に答える