【问题标题】:Adding functionality to implementation classes without changing the implemented Interface在不更改实现的接口的情况下向实现类添加功能
【发布时间】:2016-06-05 08:31:06
【问题描述】:

我有一个接口,它定义了一些用于数据检索的服务方法:

public interface DataReceiver {
    public Data getData();
}

然后我有一个实现此接口并通过连接加载数据的类。我使用构造函数注入提供此连接:

public class ConnectionDataReceiver implements DataReceiver {
    private Connection connection;

    public ConnectionDataReceiver(Connection connection) {
        this.connection = connection;
    }

    public Data getData() {
        return connection.query("blabla");
    }
}

这很好用。我可以使用构造函数实例化我的ConnectionDataReceiver 对象,或者我可以添加一个工厂方法/类,通过提供一个选项来选择用于连接设置的配置文件来扩展可用性。然后我通过我的接口使用我的新类,所以我可以轻松地换出实现(比如从文件而不是连接加载数据)。

但是如果我想在运行时更改我的连接,而不实例化一个新的ConnectionDataReceiver,该怎么办?我将不得不为我的班级添加 getter 和 setter。但是由于它们不是我的公共服务定义的一部分,所以我不能将它们放在我的界面中。我可以在我的代码中使用实现对象来设置一个新的连接,但是仅仅为了改变连接对象而挂在对原始对象的引用上感觉很尴尬:

ConnectionDataReceiver conDataRec = new ConnectionDataReceiver(myConnection);
DataReceiver dataRec = conDataRec;
// use dataRec

conDataRec.setConnection(myNewConnection);
// use dataRec again

在这个例子中,最简单的方法是实例化一个新的ConnectionDataReceiver 并重新分配dataRec,但是如果我的对象的实例化真的很昂贵怎么办?我如何为我的实现类提供额外的功能,同时仍然能够使用我的旧服务接口?或者当接口没有定义该功能时,它通常会在运行时更改数据时皱眉吗?

【问题讨论】:

  • 我认为您需要将问题的解决方案移至另一层,即使用DataReceiver 接口实现的层。在那里,您可以在运行时“注入”另一个实现。你没有展示DataReceiver是如何使用的,所以很难更详细
  • 我会在我的业务逻辑代码中的某处使用 DataReceiver(类似于 1. 获取数据 2. 处理 3. 存储在数据库中)。我获取 DataReceiver 的方式并不重要,DI、Service Locator 等等,只要我可以轻松地在业务逻辑代码中切换我的实现即可。

标签: java interface


【解决方案1】:

你可以做的是在你的界面中添加以下两个简单的方法:

public void setProperty(String name, Object value);

public Object getProperty(String name);

现在借助这两个简单的方法,您可以在实现类中配置任意数量的附加功能,而无需在super 类型中为(您的实现类的)新功能添加新方法。

此模式用于以下界面:

com.ibm.msg.client.jms.JmsQueueConnectionFactory

接口有setCharPropertysetDoublePropertysetFloatProperty等,因此当他们发布新的实现时,他们不必修改接口。

【讨论】:

  • 来自问题:我如何为我的实现类提供额外的功能,同时仍然能够使用我的旧服务接口
  • 如果您的Service Interface 有一个setProperty(String name, Object value) 方法,您可以在您的实现类中多次调用它,使用不同的name-value 对并将它们放在java.util.Map 中。
  • 我的意思是,那个 OP 已经有了接口,他不想改变
  • 该问题没有任何这样的短语。让OP澄清一下。他关心的是How do i give my implementation classes additional functionality while still being able to use my old service interface?。我已经回答了这个问题。从问题来看,尚不清楚 OP 是否使用了他无法更改的库中的 old service interface
  • 我真的不喜欢对象转换和将字符串解析为具体类型,但这个解决方案相当干净且易于扩展。
【解决方案2】:

我的版本:

界面

public interface DataReceiver 
{
    public Data getData();
}

实施

public class ConnectionDataReceiver implements DataReceiver 
{
    private Connection connection;

    public ConnectionDataReceiver(Connection connection) 
    {
        this.connection = connection;
    }

    public Data getData() 
    {
        return connection.query("blabla");
    }
}

接口在业务层使用,这里方法setReceiver会在运行时分配接口的新实现。

public class SomeBusinessLogic
{
    private DataReceiver receiver;

    public SomeBusinessLogic(DataReceiver receiver) 
    {
        this.receiver = receiver;
    }

    public void setReceiver(DataReceiver receiver) 
    {
        this.receiver = receiver;
    }
}    

通过这种方法,您可以在运行时更改 DataReceiver 的实现

【讨论】:

  • 这就是我现在的做法。在 99% 的情况下,这可能是要走的路,因为实例化 DataReceiver 的新实现对象已经足够快了。我只是想知道如何解决这个问题,如果实例化不快,但是切换实现对象本身的组件很快。基本上我想使用 DataReceiver 接口保留我的旧代码,但对实现对象进行特定调用。我希望有一个策略来做到这一点。比如扩展接口之类的。
  • 你的业务逻辑不关心接口是如何实现的接口的用途。但是在您的情况下,您的业务逻辑需要决定将使用哪个实现。原始解决方案,但可能业务对象将具有包含所有类型的“接收器”的类型属性。
  • 也许我需要重新表述:将我的实现放入我的业务代码很容易。我想知道,我应该如何处理特定于实现的调用,或者这是否是我不应该做的事情。
  • 您误解了接口的用途。接口将所有特定实现“包装”在一个通用“模式”下。但是您想从业务逻辑中显式调用特定方法。
  • 我只是想知道是否有一种好方法可以做到这一点。我可能会采用您回答中的方法,或者尝试扩展接口并使用它(您将拥有额外的功能,并且仍然可以在必要时注入它,因为它是从基础接口派生的接口)。
猜你喜欢
  • 2013-01-21
  • 1970-01-01
  • 2014-11-14
  • 2011-09-18
  • 1970-01-01
  • 2010-12-18
  • 1970-01-01
  • 1970-01-01
  • 2010-11-04
相关资源
最近更新 更多