- iOS/Objective-C 元类和类别
- objective-c - -1001 错误,当 NSURLSession 通过 httpproxy 和/etc/hosts
- java - 使用网络类获取 url 地址
- ios - 推送通知中不播放声音
我正在尝试编写一个客户端库,它将使用 HttpClient
向网络服务器发送信息和从网络服务器接收信息。这个库的核心是一个 WebClient
类:
public class WebClient {
public String send(String apiUrl) {
// Use HttpClient to send a message to 'apiUrl', such as:
// http://example.com?a=1&response=xml
//
// Wait for a response, and extract the HTTP response's body out
// as raw text string. Return the body as a string.
}
}
现在,这是我正在编写的库 (mylib.jar
)。一个由各种不同的服务组成的库,其中每个服务都有 1 种以上的方法,API 开发人员可以使用这些方法向服务器读取/写入数据。所以像这样的服务:
WidgetService
getAllWidgets
getMostExpensiveWidget
getLastWidgetUpdated
etc...
FizzService
getFizzById
getFizziestFizz
etc...
BuzzService
etc...
...
每个服务方法将采用 1+ 个 Java 原语或 1+ 个模型实例(实体、值对象等)。 每个服务方法都会返回一个这样的模型对象。例如:
public class FizzService {
public Fizz getFizzById(Long fizzId) {
// ...
}
public Fizz getFizzByBuzz(Buzz buzz) {
// ...
}
}
这一点很重要,因为这意味着 WebClient#send()
接收到的 HTTP 响应主体最终需要映射回 Java 对象。
从 API 开发人员的角度来看,我只希望开发人员实例化每个服务实例,将 WebClient
传递给它以在后台使用,然后让服务完成参数和 api url 之间的所有映射,以及 HTTP 响应主体和模型/实体之间。
例如:
public class FizzService {
private WebClient webClient;
private FizzApiBuilder fizzApiBuilder;
// Getters & setters for all properties...
public Fizz getFizzByBuzz(Buzz buzz) {
// apiCall == "http://example.com/fizz-service/getFizzByBuzz?buzz_id=93ud94i49&response=json"
String apiCall = fizzApiBuilder.build(buzz).toString();
// We asked API to send back a Fizz as JSON, so responseBody is a JSON String representing
// the correct Fizz.
String responseBody = webClient.send(apiCall);
if(apiCall.contains("json"))
return JsonFizzMapper.toFizz(reponseBody);
else
return XmlFizzMapper.toFizz(responseBody);
}
}
// What the API developer writes:
WebClient webClient = WebClientFactory.newWebClient();
FizzService fizzSvc = new FizzService(webClient);
Buzz b = getBuzz();
Fizz f = fizzSvc.getFizzByBuzz(b);
到目前为止,我喜欢这个设置。但是,对于所有服务方法,我需要相同的“样板”代码:
String apiCall = someBuilder.build(...)
String responseBody = webClient.send(apiCall)
if(apiCall.contains("json"))
return JsonMapper.toSomething(responseBody)
else
return XmlMapper.toSomething(responseBody)
这听起来像是抽象的主要用例。理想情况下,我希望将所有这些样板代码都放在 AbstractService
中,并让每个服务都扩展该抽象类。只有一个问题:
public abstract class AbstractService {
private WebClient webClient;
private ServiceApiBuilder apiBuilder;
// Getters & setters for all properties...
public Object invoke(Object... params) {
// apiCall == "http://example.com/fizz-service/getFizzByBuzz?buzz_id=93ud94i49&response=json"
// The injected apiBuilder knows how to validate params and use them to build the api url.
String apiCall = apiBuilder.build(params).toString();
String responseBody = webClient.send(apiCall);
if(apiCall.contains("json"))
return jsonMapper.to???(reponseBody);
else
return xmlMapper.to???(responseBody);
}
}
public class FizzService extends AbstractService { ... }
问题是,将功能抽象到 AbstractService
会使构建 API 调用变得尴尬(但并非不可能),但更糟糕的是,我有完全不知道如何创建和注入(inject)一组 JSON/XML 映射器,这些映射器将知道将 responseBody
映射回哪个模型/实体。
这一切都开始发臭了。当我的代码很糟糕时,我会寻找解决方案,如果找不到,我就会来这里。所以我问:我是否采用了根本错误的方法,如果是这样,我的方法应该是什么?如果我很接近,那么我如何注入(inject)正确的映射器并让这段代码闻起来更好?请记住我的代码片段显示我希望 API 开发人员如何实际使用模型/服务 - 这是最终的目标在这里。提前致谢。
最佳答案
正如评论中提到的那样,我们在编译时就知道我们想要发出的请求类型和响应类型,所以我不会让代码在运行时简单地解决这些问题,这样它就可以被提取到一个基类中。您必须为其编写额外的代码来做出这些决定,这只会为细微的错误留下空间。此外,让 API 构建器仅根据参数来计算要进行的调用将使您陷入困境;例如getMostExpensiveWidget
和 getLastWidgetUpdated
两者听起来都像是无参数方法,那么 API 构建器类如何知道要做什么?
因此,假设我们将为 API 构建器类提供用于不同 API 调用的单独方法。还假设映射器类上的方法不是静态的。然后 getFizzByBuzz
方法可能如下所示:
public Fizz getFizzByBuzz(Buzz buzz) {
String apiCall = fizzApiBuilder.buildForBuzz(buzz).toString();
String responseBody = webClient.send(apiCall);
return jsonFizzMapper.toFizz(reponseBody);
}
同样,getFizzById
方法可能如下所示:
public Fizz getFizzById(Long fizzId) {
String apiCall = fizzApiBuilder.buildForId(fizzId).toString();
String responseBody = webClient.send(apiCall);
return xmlFizzMapper.toFizz(reponseBody);
}
这两种方法都遵循相同的模式,并且它们的区别在于特定的 API 构建器方法(例如 buildForBuzz
与 buildForId
)和特定的响应映射器(jsonFizzMapper
与 xmlFizzMapper
),因为我们不想在运行时做出这些决定。因此,唯一真正的冗余是对 Web 客户端的调用。假设JsonFizzMapper
和 XmlFizzMapper
实现一些 FizzMapper
接口(interface),我的下一步是在 FizzService
中创建以下私有(private)方法类:
private Fizz sendRequest(String apiCall, FizzMapper responseMapper) {
String responseBody = webClient.send(apiCall);
return responseMapper.toFizz(reponseBody);
}
getFizzByBuzz
和 getFizzById
现在看起来像这样:
public Fizz getFizzByBuzz(Buzz buzz) {
String apiCall = fizzApiBuilder.buildForBuzz(buzz).toString();
return sendRequest(apiCall, jsonFizzMapper);
}
public Fizz getFizzById(Long fizzId) {
String apiCall = fizzApiBuilder.buildForId(fizzId).toString();
return sendRequest(apiCall, xmlFizzMapper);
}
我认为这是纯粹性和实用性之间的一个很好的平衡。理想情况下,您只需将 sendRequest 方法传递给构建器方法(例如 buildForBuzz
),但您必须跳过接口(interface)和匿名类方面的障碍似乎不值得。
如果你想走得更远,你可以使 sendRequest 方法通用,然后将其放入抽象基类(或者最好使用 composition over inheritance 并将其放在一个完全独立的类中),然后你需要制作你的映射器接口(interface)通用。但是,由于该类只包含一个两行方法,所以我觉得这可能不值得。因此,我认为为每个服务类创建一个相应的私有(private)方法应该没问题,但如果您认为它可能会变得更复杂,请在其他地方提取功能。
关于java - 在父 Java 类中注入(inject)样板代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13924426/
我正在尝试测试依赖于其他服务 authService 的服务 documentViewer angular .module('someModule') .service('docu
如果我的网站上线(不要认为它会,目前它只是一个学习练习)。 我一直在使用 mysql_real_escape_string();来自 POST、SERVER 和 GET 的数据。另外,我一直在使用 i
我有以下代码,它容易受到 SQL 注入(inject)的攻击(我认为?): $IDquery = mysqli_query($connection, "SELECT `ID` FROM users W
我一直在自学如何创建扩展,以期将它们用于 CSS 注入(inject)(以及最终以 CSS 为载体的 SVG 注入(inject),但那是以后的问题)。 这是我当前的代码: list .json {
这个简单的代码应该通过 Java Spring 实现一个简单的工厂。然而结果是空指针,因为 Human 对象没有被注入(inject)对象(所以它保持空)。 我做错了什么? 谢谢 配置 @Config
我正在编写一个 ASP.NET MVC4 应用程序,它最终会动态构建一个 SQL SELECT 语句,以便稍后存储和执行。动态 SQL 的结构由用户配置以用户友好的方式确定,具有标准复选框、下拉列表和
首先让我说我是我为确保 SQL 注入(inject)攻击失败而采取的措施的知己。所有 SQL 查询值都是通过事件记录准备语句完成的,所有运算符(如果不是硬编码)都是通过数字白名单系统完成的。这意味着如
这是 SQL 映射声称可注入(inject)的负载: user=-5305' UNION ALL SELECT NULL,CONCAT(0x716b6b7071,0x4f5577454f76734
我正在使用 Kotlin 和 Android 架构组件(ViewModel、LiveData)构建一个新的 Android 应用程序的架构,并且我还使用 Koin 作为我的依赖注入(inject)提供
假设 RequestScope 处于 Activity 状态(使用 cdi-unit 的 @InRequestScope) 给定 package at.joma.stackoverflow.cdi;
我有一个搜索表单,可以在不同的提供商中搜索。 我从拥有一个基本 Controller 开始 public SearchController : Controller { protected r
SQLite 注入 如果您的站点允许用户通过网页输入,并将输入内容插入到 SQLite 数据库中,这个时候您就面临着一个被称为 SQL 注入的安全问题。本章节将向您讲解如何防止这种情况的发生,确保脚
我可以从什么 dll 中获得 Intercept 的扩展?我从 http://github.com/danielmarbach/ninject.extensions.interception 添加了
使用 NInject 解析具有多个构造函数的类似乎不起作用。 public class Class1 : IClass { public Class1(int param) {...} public
我有一个 MetaManager 类: @Injectable() export class MetaManager{ constructor(private handlers:Handler
我是 Angular 的新手,我不太清楚依赖注入(inject)是如何工作的。我的问题是我有依赖于服务 B 的服务 A,但是当我将服务 A 注入(inject)我的测试服务 B 时,服务 B 变得未定
我正在为我的项目使用 android 应用程序启动、刀柄和空间。我在尝试排队工作时遇到错误: com.test E/WM-WorkerFactory: Could not instantiate co
我不确定这是什么糖语法,但让我向您展示问题所在。 def factors num (1..num).select {|n| num % n == 0} end def mutual_factors
简单的问题,我已经看过这个了:Managing imports in Scalaz7 ,但我不知道如何最小化注入(inject) right和 left方法到我的对象中以构造 \/ 的实例. 我确实尝
在我的 Aurelia SPA 中,我有一些我想在不同模块中使用的功能。它依赖于调用时给出的参数和单例的参数。有没有办法创建一个导出函数,我可以将我的 Auth 单例注入(inject)其中,而不必在
我是一名优秀的程序员,十分优秀!