我可以用合成替换此继承吗?

Bri*_*128 1 java oop dependency-injection

因此,让我们从一些背景[经过修改以使其更加具体[。我意识到我可以替换以下内容:

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) {}
}
Run Code Online (Sandbox Code Playgroud)

下一段代码:

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.
  }
}
Run Code Online (Sandbox Code Playgroud)

也就是说,我可以将抽象方法替换为注入的接口,以简化测试。现在让我们说该设计规定人们可以选择覆盖方法:

class FakeMessageHandler extends MessageHandler {
  protected void validateMessage(Message m) {}
  protected void processMessage(Message m) {}
}
Run Code Online (Sandbox Code Playgroud)

现在不能使用注入的接口,因为MessageHandler中只有1个抽象方法。但是,我不能强迫注入的接口包含方法,validateMessage(Message message)因为使用抽象类的原始目的是定义此方法的默认实现。

是否存在某种优雅的模式可以将其转换为合成,以便进行依赖注入和更轻松的测试?

esa*_*saj 6

这是我的看法:

除了扩展MessageHandler之外,我还有一个MessageHandler类,它由IMessageProcessor和IMessageValidator组成: 类图

希望我的UML图正确,已经有一段时间了...

无论如何,让我们看一下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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

MessageHandler具有两个私有成员,即IMessageProcessor和IMessageValidator(由处理器和验证程序组成)。可以不设置验证器,在这种情况下,处理消息时将使用默认验证逻辑。

在此示例中,如果传入的处理器还实现IMessageValidator-interface,它将用作验证器。这可能就是您想要的,因为您可以使用同一构造函数使用默认的验证或自定义验证逻辑来​​构建MessageHandler,具体取决于传入的对象仅实现IMessageProcessor还是同时实现IMessageProcessor和IMessageValidator(为方便起见,我扩展了第三个接口,这些接口中的IValidatingMessageProcessor)。如果验证器逻辑是单独实现的(仅实现IMessageValidator),则可以使用setValidator-method对其进行设置。

无需扩展MessageHandler,因为您可以在处理程序外部实现处理和验证-logic,既可以单独实现,也可以在实现处理和验证的单个类中实现。

这是我使用过的类,希望对您有所帮助:

MediaFire中的压缩包

文字形式:

Message.java:


public class Message 
{
    private int number;

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

    public String toString()
    {
        return "Msg " + number;
    }
}
Run Code Online (Sandbox Code Playgroud)

IMessageProcessor.java:


interface IMessageProcessor 
{
  public void processMessage(Message m);
}
Run Code Online (Sandbox Code Playgroud)

IMessageValidator.java:


public interface IMessageValidator 
{
    public void validateMessage(Message m);
}
Run Code Online (Sandbox Code Playgroud)

IValidatingMessageProcessor.java:


public interface IValidatingMessageProcessor extends IMessageProcessor, IMessageValidator
{
}
Run Code Online (Sandbox Code Playgroud)

FakeMessageProcessor.java:


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

FakeMessageValidator.java:


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

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);       
    }
}
Run Code Online (Sandbox Code Playgroud)

上述类的简单测试主要工具(仅输出内容):


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));
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:


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
Run Code Online (Sandbox Code Playgroud)