2012-01-13 24 views
0

だから、いくつかの背景から始めましょう。この継承をコンポジションに置き換えることはできますか?

abstract class MessageHandler { 
    public void handleMessage(Message m) { 
    validateMessage(m); 
    processMessage(m); 
    } 

    protected void validateMessage(Message m) { 
    // Default validation logic 
    } 

    protected abstract void processMessage(Message m); 
} 

class FakeMessageHandler extends MessageHandler { 
    proteced void processMessage(Message m) {} 
} 

コードの次のブロックで:私は容易にテストを可能にする注入界面で抽象メソッドを置き換えることができ、ある

interface IMessageProcessor { 
    public void processMessage(Message m); 
} 

class FakeMessageProcessor implements IMessageProcessor { 
    public void processMessage(Message m) {} 
} 

class MessageHandler { 
    private IMessageProcessor processor; 
    public MessageHandler(IMessageProcessor processor) { 
    this.processor = processor; 
    } 

    public void handleMessage(Message message) { 
    validateMessage(message); 
    processor.processMessage(message); 
    } 

    protected void validateMessage(Message message) { 
    // Default validation logic. 
    } 
} 

私は次のように置き換えることができ実現しています。今

class FakeMessageHandler extends MessageHandler { 
    protected void validateMessage(Message m) {} 
    protected void processMessage(Message m) {} 
} 

注入されたインタフェースをMessageHandlerの唯一の1抽象メソッドがあるとして使用することはできません。今のデザインは、人々は、必要に応じてメソッドをオーバーライドすることを規定しているとしましょう。しかし、注入されたインタフェースにメソッドvalidateMessage(Message message)が含まれているように強制することはできません。抽象クラスを使用する元の点は、このメソッドのデフォルトの実装を定義することでした。

これは、依存性注入と簡単なテストのためにこれを合成に変換するためのエレガントなパターンですか?

+0

第2の例で'B'は' bar'ではなく 'baz'を実装してはいけませんか?私は十分に質問を理解しているか分からない。それをもっと具体的にしようとすることは可能でしょうか?私は、 'C'は(基本クラスに渡すために)' I'を取るコンストラクタを含んでいなければならないので、解決策がすでにあると思います。 –

+0

私はあなたの質問を理解していません。 Aの2番目の実装では、抽象メソッドはまったくありません。質問の最後の部分は1ではありません。あなたは正確に何をしたいですか? –

+0

コードを読みやすくするためにコードをリファクタリングしました。たとえば、インタフェースIをIMessageProcessorに変更しました。何らかのエラーがないかどうか確認してください。しかし、うまくいけばコードは簡単になり、質問は簡単に答えることができます。 – Steven

答えて

4

ここでは、この上の私の感想です:私は、UMLダイアグラムを得た Class diagram

希望:

代わりにMessageHandlerのを拡張する、私は単一MessageHandlerのクラスを持って、それはIMessageProcessorとIMessageValidatorの組成物であり、右、それはしばらくしている...とにかく

は、のはMessageHandlerのを見てみましょう:


class MessageHandler 
{ 
    private IMessageProcessor processor; 
    private IMessageValidator validator; 

    public MessageHandler(IMessageProcessor processor) 
    { 
     this.processor = processor; 

     //Use the given processor as validator, if it implements the IMessageValidator-interface 
     if(IMessageValidator.class.isAssignableFrom(processor.getClass())) 
     { 
      this.validator = (IMessageValidator)processor; 
     } 
    } 

    public void setMessageValidator(IMessageValidator validator) 
    { 
     this.validator = validator; 
    } 

    public void handleMessage(Message message) 
    { 
     validateMessage(message); 
     processor.processMessage(message); 
     System.out.println("Message " + message + " handled by MessageHandler"); 
    } 

    protected void validateMessage(Message message) 
    { 
     if(validator != null) 
     { 
      validator.validateMessage(message); 
     } 
     else 
     { 
      System.out.println("No IMessageValidator-implementation set, using default validation for message " + message); 
     } 
    } 
} 

MessageHandlerには、IMessageProcessorとIMessageValidatorという2つのプライベートメンバーがあります(これはプロセッサとバリデータの構成です)。バリデーターは未設定のままにすることができます。この場合、メッセージを処理するときにデフォルト検証ロジックが起動します。

この例では、渡されたプロセッサもIMessageValidatorインターフェイスを実装している場合、それはバリデータとして使用されます。これはおそらくあなたが望んだのは、同じコンストラクタを使って、渡されたオブジェクトがIMessageProcessorだけを実装するか、IMessageProcessorとIMessageValidatorの両方を実装するかによって、デフォルトの検証またはカスタム検証ロジックを使用してMessageHandlerを構築できるからです(便宜上、これらのインタフェースからのIValidatingMessageProcessor)。バリデーターロジックが個別に実装されている場合(IMessageValidatorのみ実装)、setValidatorメソッドを使用して設定できます。

ハンドラの外側で処理と検証の両方のロジックを処理することも、処理と検証の両方を実装する単一のクラスで実装することもできるため、MessageHandlerを拡張する必要はありません。

はここで、私が使用したクラスであるこのことができます願っています:

Zip-package in MediaFire

テキスト形式:

Message.java:


public class Message 
{ 
    private int number; 

    public Message(int number) 
    { 
     this.number = number; 
    } 

    public String toString() 
    { 
     return "Msg " + number; 
    } 
} 

IMessageProcessor。Javaの:


interface IMessageProcessor 
{ 
    public void processMessage(Message m); 
} 

IMessageValidator.java:


public interface IMessageValidator 
{ 
    public void validateMessage(Message m); 
} 

IValidatingMessageProcessor.java:


public interface IValidatingMessageProcessor extends IMessageProcessor, IMessageValidator 
{ 
} 

FakeMessageProcessor.java:


public class FakeMessageProcessor implements IMessageProcessor 
{ 
    public void processMessage(Message m) 
    { 
     System.out.println("Using FakeMessageProcessor to process message " + m); 
    } 
} 

FakeMessageValidator.java:


public class FakeMessageValidator implements IMessageValidator 
{ 
    public void validateMessage(Message m) 
    { 
     System.out.println("Using FakeMessageValidator to validate message " + m);  
    } 
} 

FakeMessageProcessorAndValidator.java:上記クラスの


public class FakeMessageProcessorAndValidator implements IValidatingMessageProcessor 
{ 
    public void validateMessage(Message m) 
    { 
     System.out.println("Using FakeMessageProcessorAndValidator for validating message " + m); 
    } 

    public void processMessage(Message m) 
    { 
     System.out.println("Using FakeMessageProcessorAndValidator for processing message " + m);  
    } 
} 

簡易試験メイン(単に出力するもの):


public class MessageTest 
{ 
    public static void main(String[] args) 
    { 
     //Using processor implementing only IMessageProcessor, MessageHandler will use default validation 
     IMessageProcessor processor = new FakeMessageProcessor(); 
     MessageHandler handler = new MessageHandler(processor); 

     handler.handleMessage(new Message(1)); 

     //Setting separate validator to existing MessageHandler-instance 
     handler.setMessageValidator(new FakeMessageValidator()); 

     handler.handleMessage(new Message(2)); 

     //Using processor implementing both IMessageProcessor and IMessageValidator 
     processor = new FakeMessageProcessorAndValidator(); 
     handler = new MessageHandler(processor); 

     handler.handleMessage(new Message(3)); 
    } 
} 

出力:


No IMessageValidator-implementation set, using default validation for message Msg 1 
Using FakeMessageProcessor to process message Msg 1 
Message Msg 1 handled by MessageHandler 
Using FakeMessageValidator to validate message Msg 2 
Using FakeMessageProcessor to process message Msg 2 
Message Msg 2 handled by MessageHandler 
Using FakeMessageProcessorAndValidator for validating message Msg 3 
Using FakeMessageProcessorAndValidator for processing message Msg 3 
Message Msg 3 handled by MessageHandler 
+0

+1の「サイズ問題」の回答 – Bohemian

+0

私はあなたの考え方が気に入っています。残念ながら、私はそれがうまくスケールされないと思います - オプションでオーバーライドできる3つのメソッドがある場合はどうなりますか?私は一般的な解決策を望んでいたと思うが、私はあなたのものを、おそらく私が得ようとしている最も近いものとしてマークするだろう。ありがとう! – Bringer128

関連する問題