2016-10-17 8 views
3

メッセージをあるフォーマットから別のフォーマットに変換するプロジェクトがあります。 メッセージはIDで識別されます。私は1つのフォーマットのIDと他のフォーマットの対応するIDを持つMappingTableを持っています。例えば多くのマジックナンバー

:このメソッドは、マッピングリストにマッピングエントリを追加します

this.AddMappingEntry(2400, 2005, true, false); 

: 2000 = 153

IDは整数

私は経由でこれらすべてのentrysを追加します。私は値が2400のメッセージを受け取った場合、正しいマッピングを見つけるためにlinqでリストをフィルタリングできます。

プロジェクトが開始されて以来、多くのリファクタリングと多くのIDにつながるかなり成長しています特別な行動。私は、メッセージが特別なタイプのものであるかどうかを制御しなければならない。

if(message.id == 153) 
{ 
    //special behavior 
} 

これはどのように処理すればよいですか? メッセージの種類を表す定数を使用するか、もう少し上手くいく方法がありますか?

EDIT:

私は質問を言い替えます。 私はヘッドレコードとサブレコードを持っています。しかし、これらの記録のIDはコード全体で使用されています。約200の異なるIDがあるので、私はそれらの魔法の数字で何をすべきか考えていました。私が書いているツールはコンバータです。次のように

構造である

は、コンバータクラスはおおよそこの

+------------+ 
    |BaseClass | 
    +-----+------+ 
     ^
      | 
    +-----+------+ 
    |BaseRecord +^-------------+----------------------+ 
    +------+-----+    |      | 
     ^     |      | 
      |     |      | 
    +------+-----+  +-------+--------+  +-------+--------+ 
    | HeadRecord |  | RecordType1 |  | RecordType2 | 
    +------------+  +----------------+  +----------------+ 

ように見えるBaseRecordは基底クラスを拡張し、他のすべてのクラスはBaseRecordを拡張します。全体では、私はrecordId 1-5で5つのレコードタイプを持っています。 このレコードの中には、いくつかのsubrecordId(〜50)があります。これらは、書き込みプロセスの後で、それぞれライターの背後にあるレコードを識別するためにのみ使用されます。

問題は、いくつかのレコードが異なるフィールドからいくつかの値を読み取ってから、レコードを識別する必要のある特殊なケースにつながるレコードがあることです。

これは問題につながる: 私は私のクラスとコンバータでそれらを使用する場合、私は、誰も彼らが何であるかを知らない多くのマジックナンバーを持っています。どのようにそれらの魔法の数字を避けるのですか?私はそれらを使用する場合、私のクラスは50 + constの値でいっぱいであると私は残念です。マジックナンバーや大量のconst値を避ける方法はありますか?これに対して正しいリファクタリングは何ですか?

+0

あなたのケースでは、 '2000 = 0153'は0153ですvarcharまたは整数.. Ifステートメントを使用する代わりに、ケースステートメントについて考えましたか?また、もしあなたがそれを期待しているなら、私は個人的に先行ゼロを使用しません。 '153'は正しい整数表現を使用しません。これはvarcharではなくswitch文を使っても訂正可能です。 – MethodMan

+0

これはスイッチの場合とそうでない場合です。私は頭やサブレコードを扱ういくつかのクラスを持っています。 ifsはサブレコードなどで扱われます。 – Bongo

+0

あなたの質問を正しく理解すれば、あなたは 'enum'を使いたいと思うように思えます - それはあなたが魔法の数字の代わりに意味のある名前を使うことを可能にします。 –

答えて

1

一般に、これは修正することによって最もよく対処される問題です。これがオプションでない場合は、特別な動作を処理するためにswitch-statementを使用します。可能であれば、スイッチケースではできるだけ少なく、一般的なハンドラでは可能な限り操作してください。

0

私はApp.Configに設定し、Configuration Managerを使用してデータを取得します。

0

「タイプ」を示すプロパティを含む、使用しているMessageクラスに拡張メソッドを追加しないでください。 「型」があらかじめよく知られている場合、決定を行うときに列挙型を使用して比較することを検討してください。ただし、メッセージのタイプを確認するたびにIDのチェックを行わないように、メッセージの構築に列挙型を割り当てることができます(たとえば)。ただし、型が長いリストの場合は、文字列を使用して型(コードで読み込むと意味があります)を表示し、可能であれば、コードを文字列に変換します。

3

Factory design patternが便利な場所です。

シングル特別な行動に対処する方法を知っていますクラスのためのインタフェースを定義します。

public interface IMessageHandler 
{ 
    IMessage Transform(IMessage message); 
} 

その後、あなたはそれが異なる実装ですがあります。たとえば:

public class DefaultMessageHandler 
{ 

    IMessage Transform(IMessage message) 
    { 
     return message; 
    } 
} 

public class BehaviorXMessageHandler 
{ 

    IMessage Transform(IMessage message) 
    { 
     message.SomeProperty = "hello world"; 
     return message; 
    } 
} 

は、次に、あなたの工場を持っている:

public interface IMessageHandlerFactory 
{ 
    IMessageHandler GetHandler(int messageCode); 
} 

工場のための可能な実装は、スイッチケースを使用することになりますが、私はあなたがDependency Injectionを使うべきだと思う:

で次の実装では、すべての特別でない場合のマッピングとデフォルトの動作の両方を渡します。私は、これはあなたが必要なものだけをマッピングすると、工場出荷時は、あなたがそれを自分で初期化したり、そのような城としてそこに多くのIoC Containersのいずれかを使用することができますマッピングを受信することが

public class MappingMessageHandlerFactory : IMessageHandlerFactory 
{ 
    public MappingMessageHandlerFactory(Dictionary<int,IMessageHandler> mapping, IMessageHandler defaultBehavior) 
    { 
     Mapping = mapping; 
     DefaultBehavior = defaultBehavior; 
    } 

    public IMessageHandler GetHandler(int messageCode) 
    { 
     IMessageHandler output = DefaultBehavior; 
     Mapping.TryGetValue(messageCode, out output); 

     return output; 
    } 

    public Dictionary<int,IMessageHandler> Mapping {get; set;} 
    public IMessageHandler DefaultBehavior {get;set;} 
} 

をorganaized保つためにエレガントな方法だと思いますウィンザー、ニンジェット、ユニティなど

これらのコンテナの中には、工場の一般的な実装であっても、あなたが提供する必要があるのはインターフェイスだけです。コメントの後の更新

public IMessage ProcessMessage(IMessage message) 
{ 
    var handler = _messageHandlerFactory.GetHandler(message.Code); 
    return handler.Transform(message); 
} 

城ウィンザーでは、あなたのサービスのコードを上記の構造を経て、ちょうどのようになるはずです何でもあなたが選ぶ、TypedFactoryFacility

と呼ばれています

しかし、変換プロセスでは、他のいくつかの場所でマジックナンバーを使用する必要があります私はあなたが何ができるか、多くの異なる場所でのマジックナンバー

を持って使用することはCodeIMessageHandlerで別のプロパティを定義し、Codeを設定しなければならないことを強制する抽象基本クラスを持っています。そうすることで、実際には「MessageHandlerは、それが責任を負うメッセージを知っている」と言うことができます。

public interface IMessageHandler 
{ 
    IMessage Transform(IMessage message); 
    int Code {get; set;} 
} 

public abstract class MessageHandlerBase : IMessageHandler 
{ 
    public MessageHandlerBase(int code) 
    { 
     Code = code; 
    } 

    public abstract IMessage Transform(IMessage message); 
    public int Code {get; set;} 
} 

それはもはや辞書が、IEnumerable<IMessageHandler>を受信しません - 彼の数字がどこかで再び定義されていないになります。(あなたは、検索がまだo(n)になりますので、それは辞書にIEnumerableに変換それは実装の詳細であること、それを実装することができます。

例えばメッセージ2002(変更ユーザー)が送信システム内の1つのメッセージです。 (106)とログアウト(107)だけです。

このようなサービスは、上記の同じ種類の工場に依存する可能性がありますが、その特定の工場の初期化と、それぞれのHandlerCodeの初期化が異なる場合があります。

例としてCastle Windsorを使用している場合 - xmlを使用して依存関係を設定するオプションがあります。それはあなたが実際に持っていると思いますし、それらにどのCodeのマップビヘイビアそのマッピングを作成app.config年代にしたがって、各サービスは、この場所のようなセクションを持つことができます

+0

私はこのallreadyを使用しています:)。しかし、変換プロセスでは、私は多くの場所で魔法の数字を持っているので、悪いいくつかの場所でマジックナンバーを使用する必要があります。 – Bongo

+0

たとえば、メッセージ2002(変更ユーザー)は、送信システムの1つのメッセージです。受信側システムには変更ユーザーはなく、ログイン(106)とログアウト(107)のみがあります。私が2002年を受け取ると、ログアウト(107)を作成し、その後、新しいユーザーとログイン(106)します。私が2002年を受け取った場合、ログイン名は送信システムのフィールド "a"にあります。 2001年(送信システムでログイン)を受け取ったら、フィールド "b"を選択します。 – Bongo

+0

@Bongo - 最新の更新を参照してください –

5

あなたがアクションのコレクションとして特別な振る舞いを一般化することができた場合、

private static readonly IDictionary<int,Action> messageBehavior = 
    new Dictionary<int,Action> { 
     {153,() => { Console.WriteLine("Special action"); } }, 
     {154,() => { Console.WriteLine("Another special action"); } } 
    }; 

今、あなたは、メッセージIDによって辞書からアクションを取得し、それが利用可能な場合、それを実行することができます:

Action special; 
if (messageBehavior.TryGetValue(message.id, out special)) { 
    special(); 
} 

場合は、このようにそれを行うことができるはずアクションは、彼らが実行されるいくつかの特殊な状況、例えば、アクションをトリガしたメッセージは、あなたがAction<Message>を使用することができ、そしてそれにメッセージを渡すが必要です。

private static readonly IDictionary<int,Action<MyMessageType>> messageBehavior = 
    new Dictionary<int,Action> { 
     {153, (m) => { Console.WriteLine("Special action; message {0}", m); } }, 
     {154, (m) => { Console.WriteLine("Another special action ({0})", m); } } 
    }; 
... 
Action<MyMessageType> special; 
if (messageBehavior.TryGetValue(message.id, out special)) { 
    special(message); 
} 
+4

これは 'action'が小さい場合に最適なオプションです。特別なIDごとに5つ以上のLOCがある場合は、乱雑になる可能性があります。 –

0

私はSpecialMessageHandlerクラスとStandardMessageHandlerのセットを持っているでしょうクラス、次に何らかのルックアップ(Dictionaryなど)があります。 if/switchを使用して、辞書内のメッセージIDを検索するのではなく、標準のものを使用していない場合は、特殊クラスを使用します。辞書の内容をコードの外に指定することもできます(例:web/app.config)。

0

おそらく多くのdownvotesを得る余分なことを書くでしょう。

私は特別なコード(SpecialIds)で列挙型を作成します。 次に、数字が特別なものかどうかを調べるときには、Idを文字列に変換します。私は、この名前はclassになりますSpecial_153

のようになります。接頭辞「特別」を追加し、その文字列で

それから私は、すべてのSpecialクラスが唯一の方法Runを実装ISpecialから継承するすべての特別なクラス

Special_153.cs Special_154.cs

でフォルダを作成します。

メソッドRunは、コンストラクタ内で呼び出されます。 コンストラクタは完全なメッセージオブジェクトを受け取ります。

その後、私はこのようにそれを使用します。

if (Enum.GetNames(typeof(SpecialIds)).Contains(message.id)) 
{ 
    //special behavior 
    string id = message.Id.ToString(); 
    string prefix = "Special_"; 
    string className = prefix + id; 

    Type t = Type.GetType(className); 
    ConstructorInfo constructor = t.GetConstructor(new Type[] { message.GetType() }); 
    constructor.Invoke(message); 
} 

constructor.Invoke(message);は、引数messageでコンストラクタを呼び出します。 クラスメソッドRunの中で、オブジェクトmessageの中で必要なものをすべて変更することができます。

この方法では、特別なIDごとに1つのクラスを持つか、handlersと呼ぶことができます。 それらはそのフォルダ内にあり、200人のハンドラのうち1人を編集したい場合は、簡単にコードにアクセスできます。

編集1

ああ、および終了する、あなただけではそのconstructor呼び出しを行うと拡張子を書き、そしてちょうど呼び出すことができます。

message.CheckSpecial();

この拡張機能内には、チェックしますメッセージが特別なものである場合。この方法でこのコードを他の場所で使用することができます。

関連する問題