Java:生成对象;“适合”;具有基类不实现接口且can';不能继承

Java:生成对象;“适合”;具有基类不实现接口且can';不能继承,java,oop,testing,inheritance,twilio,Java,Oop,Testing,Inheritance,Twilio,我正在使用Twilio库,特别是在测试时,我正在尝试注入TwilioRestClient的模拟 它没有实现任何接口。我无法添加一个 我不能继承它,它在构造函数中做一些我不喜欢的事情 我需要在mock中有几个方法来覆盖或替换TwilioRestClient的某些行为 我该怎么做 我尝试过内部匿名类,但没有效果。我尝试过子类化,但显然不起作用。有Java大师吗 编辑,twilio sdk版本 public class TwilioRestClient { /** The Constant V

我正在使用Twilio库,特别是在测试时,我正在尝试注入TwilioRestClient的模拟

它没有实现任何接口。我无法添加一个

我不能继承它,它在构造函数中做一些我不喜欢的事情

我需要在mock中有几个方法来覆盖或替换TwilioRestClient的某些行为

我该怎么做

我尝试过内部匿名类,但没有效果。我尝试过子类化,但显然不起作用。有Java大师吗

编辑,twilio sdk版本

public class TwilioRestClient {
    /** The Constant VERSION. */
    private static final String VERSION = "3.3.15";
建造商:

    /**
 * Explcitly construct a TwilioRestClient with the given API credentials.
 *
 * @param accountSid
 *            the 34 character Account identifier (starting with 'AC'). This
 *            can be found on your Twilio dashboard page.
 * @param authToken
 *            the 32 character AuthToken. This can be found on your Twilio
 *            dashboard page.
 *
 */
public TwilioRestClient(String accountSid, String authToken) {
    this(accountSid, authToken, null);
}

/**
 * Explcitly construct a TwilioRestClient with the given API credentials and
 * endpoint.
 *
 * @param accountSid
 *            the 34 character Account identifier (starting with 'AC'). This
 *            can be found on your Twilio dashboard page.
 * @param authToken
 *            the 32 character AuthToken. This can be found on your Twilio
 *            dashboard page.
 * @param endpoint
 *            the url of API endpoint you wish to use. (e.g. -
 *            'https://api.twilio.com')
 */
public TwilioRestClient(String accountSid, String authToken, String endpoint) {

    validateAccountSid(accountSid);
    validateAuthToken(authToken);

    this.accountSid = accountSid;
    this.authToken = authToken;

    if ((endpoint != null) && (!endpoint.equals(""))) {
        this.endpoint = endpoint;
    }

    //Grab the proper connection manager, based on runtime environment
    ClientConnectionManager mgr = null;
    try {
        Class.forName("com.google.appengine.api.urlfetch.HTTPRequest");
        mgr = new AppEngineClientConnectionManager();
    } catch (ClassNotFoundException e) {
        //Not GAE
        mgr = new ThreadSafeClientConnManager();
        ((ThreadSafeClientConnManager) mgr).setDefaultMaxPerRoute(10);
    }

    setHttpclient(new DefaultHttpClient(mgr));
    httpclient.getParams().setParameter("http.protocol.version",
            HttpVersion.HTTP_1_1);
    httpclient.getParams().setParameter("http.socket.timeout",
            new Integer(CONNECTION_TIMEOUT));
    httpclient.getParams().setParameter("http.connection.timeout",
            new Integer(CONNECTION_TIMEOUT));
    httpclient.getParams().setParameter("http.protocol.content-charset",
            "UTF-8");

    this.authAccount = new Account(this);
    this.authAccount.setSid(this.accountSid);
    this.authAccount.setAuthToken(this.authToken);

}

据我所知,TwilioRestClient不是一个最终类,因此扩展它并重写每个非私有方法没有问题

其次,这里是我找到的
TwilioRestClient
的源代码:

我们可以看到,它只是将三个字符串字段初始化到构造函数中。因此,我认为您可以发送任何您想要的参数,甚至不必扩展类。但是,我不确定我找到的源代码是否属于您正在使用的同一版本。您尚未指定版本


这里唯一的限制是不能将
内点
初始化为听起来不错的空字符串。但如果您想做类似的事情,可以在创建实例后使用反射来完成

您希望避免使用模拟框架,并且不希望扩展/覆盖TwilioRestClient

因此,一种“纯java”的方法可能是让使用TwilioRestClient的类使用您定义的接口。这一点的难度取决于您实际使用的Twiliorest客户端接口的数量

如果只使用几种方法,请定义自己的接口,例如:

public interface RestClient {
    String get(String uri);
}
当您想要使用TwilioRestClient时,您可以:

final TwilioRestClient trc = createTwilioRestClient();
myService.setRestClient(new RestClient() {
    public String get(String uri) {
        return trc.get(uri);
    }
});
如果要进行单元测试,请使用:

myService.setRestClient(new RestClient() {
    public String get(String uri) {
        return someMockData();
    }
});

但是,如果您使用了大量的接口,这将变得很难操作;在这种情况下,模拟库确实是您的最佳选择,这就是它们的用途。

我最终编写了一个抽象层来隐藏所有特定于twilio的逻辑,并使代码更易于测试。没有其他方法可以实现我想要的。

根据定义,如果没有继承,您将无法重写任何方法。您是对的。我的目的是确保当我测试的是调用这些方法时,我在测试中决定这些方法将返回什么。基本上只是注入行为。尝试jmockit来重新定义方法。我使用mockito,但在其他类中模拟这个类时会出现奇怪的行为。我想在不使用测试/模拟框架的情况下解决这个问题。谢谢,我将用这个版本更新我的问题。看起来不一样。那么,你需要对构造函数的行为进行什么样的更改呢?基本上我可以覆盖构造函数中调用的所有方法,以达到我想要的效果。然而,最好不要这样做,因为在某些情况下,不是特别是这一次,这可能会非常乏味。我可能对Java的思考还不够,因为我更习惯于用Ruby等无类型语言动态替换行为。不要试图在运行中替换功能。你可以做到,但这是通往地狱的路。创建子类,重写所需内容并使用子类。如果愿意,您始终可以从覆盖的
foo()
版本调用
super.foo()
。您也可以有条件地这样做,因此在某些情况下,您的子类的行为将与其基类类似。如果您仍然需要一些“动态”替换,您可以查看字节码修改和代理,但同样,只有在其他方法不起作用时才这样做。有趣的方法,我将研究此选项。谢谢
myService.setRestClient(new RestClient() {
    public String get(String uri) {
        return someMockData();
    }
});