2

我编写了一个小的 Web API,它应该可以轻松创建 URI。每个资源类都应包含一个createURI采用所需参数的方法。此方法应在后台使用辅助方法 ,populateUriTemplate来创建 URI 字符串。populateUriTemplate需要键值对来填充 URI 模板。在另一种语言(如 Scala 或 Python)中,我会使用命名参数,但 Java 不支持它们。那么问题来了:如何在Java中模拟命名参数?

直接的解决方案是创建一个地图:

public String createUri(int id, String name){
    Map<String, Object> params = new HashMap<String, Object>();
    params.put("id", id);
    params.put("name", name);
    return populateUriTemplate(params);
}

但我不喜欢先创建地图,然后将每个参数都放入其中。

另一个想法是使用静态方法 ,param来创建键值对:

public String createUri(int id, String name){
    return populateUriTemplate(param("id", id), param("name", name));
}

在我看来好多了!

即使需要更多字符,也可以对其进行一些改进以使其更加不言自明:

public String createUri(int id, String name){
    return populateUriTemplate(key("id").value(id), key("name").value(name));
}

我也想到了构建器模式,但这会迫使我的 API 的用户为每个资源类创建一个显式的构建器,这会很乏味而没有好处。只要toString存在适当的实现方法,参数的类型并不重要。

我最喜欢的是上面的静态方法(param(key, value)key(k).value(v))的两种方法之一。您知道在这种情况下模拟命名参数的更好方法吗?

4

6 回答 6

2

有关构建器模式的一些想法,您可以查看Stephan Schmidt 的这篇博文。

您还刚刚给了我使用流畅接口、aCallable和静态方法执行以下操作的想法:

createUri().id(5).name("dennetik").call();

这需要使用静态方法创建一个Callable类 ( ):CreateUri

public static final CreateUriFluentInterface createUri() {
    return FluentInterface.of(new CreateUri(), CreateUriFluentInterface.class);
}

还有一个流畅的界面,像这样:

public interface CreateUriFluentInterface {
    public CreateUriFluentInterface id(Integer id);
    public CreateUriFluentInterface name(String name);
}

这不是那么多样板代码,是吗?

(好吧,如果你把那个可怕的名字调低CreateUriFluentInterface一点,它不是。)

(您可能有CreateUriFluentInterfaceextend Callable<String>,以便能够将呼叫重新路由到Callable#call()

于 2010-06-20T11:33:53.407 回答
1
populateUriTemplate("id",id, "name",name);

void populateUriTemplate(Object... nvs){
    for(int i=0; i<nvs.length/2; i++)
        ....
}
于 2010-06-20T15:48:14.803 回答
0

也许你喜欢这种方法:

class Params {
    private HashMap<String, Object> allParams = new HashMap<String,Object>();

    public Params(ParamEntry...params) {
        for( ParamEntry p : params ) {
            allParams.put(p.name, p.value);
        }
    }

    public getParam(String name) {
           return allParams.get(name);
    }

    class ParamEntry {
        public String name;
        public Object value;
    }
}

public String createUri(Params.ParamsEntry ... params){
    return populateUriTemplate(new Params(params));
}

调用它使用

createUri(new Param.ParamEntry("name", valueObject) );

在 populateUriTemplate 里面......只是use params.get("name");

于 2010-06-20T11:09:23.597 回答
0

Spring MVC正是这样做的。除了能够将请求绑定到控制器类中的特定方法之外,您还可以将请求参数绑定到方法参数。你可以看看它是如何工作的,但基本上它会选择一种策略来将正确的请求参数映射到正确的方法参数。

你基本上会得到类似的东西:

public String createUri(@RequestParam int id, @RequestParam String name){
    return populateUriTemplate(id, name);
}
于 2010-06-20T11:10:11.460 回答
0

这几乎是愚蠢的,而且有点偏离主题,但是使用 Lombok 的@Builder注释可以更接近预期的结果。

此外,如果将构建器、构建器方法和构建方法名称更改为_它们几乎会消失:

import static foo.Template._;

class Resource {
    String createURI(String id, String name) {
        return populateURITemplate(_.id(id).name(name)._());
    }
    String populateURITemplate(Template t ){
        return t.id+"="+t.name;
    }
}
@Builder(builderClassName = "_", builderMethodName = "_", buildMethodName = "_" )
class Template {
    static _ _ = _();
    String id;
    String name;
}
于 2015-09-30T21:05:54.277 回答
0

命名参数不是办法:

在这种情况下,命名参数不会使您的代码更清晰。我认为它们使 Java 中的事情变得更加复杂和容易出错,因为您失去了类型安全性并且您失去了关于不存在标识符的编译器警告。

TypeSafe 不可变 Fluent 构建器:

UrlBuilder我在今年早些时候写了一篇关于实现的文章,它展示了一个类型安全的流式接口,该接口强制强制输入的构造顺序,并允许可选部分具有合理的默认值。

现在我将第一个承认我使用的方法相当冗长,但是一旦支付了初始价格,它就会非常高效。它与依赖注入一起工作,易于单元测试,最重要的是可组合用于专业化。

    final URL url1 = new UrlBuilder().scheme("http").host("www.google.com").build();
    System.out.println("url1 = " + url1);
    final URL url2 = new UrlBuilder().scheme("https").userInfo("xkcd", "correcthorsebatterystaple").host("admin.xkcd.com").build();
    System.out.println("url2 = " + url2);

产生:

url1 = http://www.google.com
url2 = https://xkcd:correcthorsebatterystaple@admin.xkcd.com

我正在用我正在试验的另一种方法解决接口的匿名内部类实现的冗长性;来自使用动态代理的接口的值对象的类型安全实现

这将取消样板值对象并将它们替换为Map<String,?>但在它们周围放置一个动态生成的类型安全的不可变Interface包装器。

我鼓励您阅读这两个方面的内容,看看将它们结合起来如何为您提供比命名属性更好的解决方案。

当我有时间UrlBuilder用动态代理重构我的时,我也会发布另一篇关于它的博客文章。

通过 Guice 命名参数

如果您对命名参数一无所知,那么我建议您查看Guice @Named bindings。你仍然失去了编译类型检查和安全性,但至少你从 Guice 得到了一些验证。

公共类 RealBillingService 实现 BillingService {

@Inject
public RealBillingService(@Named("Checkout") CreditCardProcessor processor,
    TransactionLog transactionLog) {
...
}
于 2015-09-30T21:25:21.603 回答