如何将输入转换为程序可以理解的方法
How to convert an input into a method that the program will understand
我目前正在做关于客户端和服务器主题的作业:
我创建了一个名为 VehileRequest.java
的 class,它将从 Client.java
获取三个变量(年份、品牌、型号)并将它们传递给 Server.java
然后 Server
将得到来自 VehicleRespone.java
的信息并显示有关价格、里程 vv...
的信息
据我了解,程序无法识别传递给响应文件的请求。
我无法将请求传递给响应,以便响应能够理解。请任何帮助。谢谢
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
public class VehicleResponse {
private VehicleRequest request;
private int milesOnVehicle;
private int price;
private int numberOfSeats;
private int numberOfDoors;
private String[] options;
@JsonIgnore
private static final ObjectMapper objectMapper = new ObjectMapper();
public static String toJSON(VehicleResponse vehicle) throws Exception {
return objectMapper.writeValueAsString(vehicle);
}
public static VehicleResponse fromJSON(String input) throws Exception{
return objectMapper.readValue(input, VehicleResponse.class);
}
protected VehicleResponse() {}
public VehicleResponse(VehicleRequest request, int milesOnVehicle,int price, int numberOfSeats, int numberOfDoors,String[] options) {
this.request=request;
this.milesOnVehicle=milesOnVehicle;
this.price=price;
this.numberOfSeats=numberOfSeats;
this.numberOfDoors=numberOfDoors;
this.options=options;
}
@Override
public String toString() {
return String.format(
"Vehicle request:[miles=%d, price=%d, number of seats=%d,number of doors=%d, option='%s']",
milesOnVehicle,price,numberOfSeats,numberOfDoors,options);
}
public VehicleRequest getRequest() {return request;}
public int getMilesOnVehicle(){return milesOnVehicle;};
public int getPrice(){return price;}
public int getNumberOfDoors() {return numberOfDoors;}
public int getNumberOfSeats() {return numberOfSeats;}
public String[] getOptions() {return options;}
public void setRequest(VehicleRequest request) {
this.request = request;
}
public void setMilesOnVehicle(int milesOnVehicle) {
this.milesOnVehicle = milesOnVehicle;
}
public void setPrice(int price) {
this.price = price;
}
public void setNumberOfSeats(int numberOfSeats) {
this.numberOfSeats = numberOfSeats;
}
public void setNumberOfDoors(int numberOfDoors) {
this.numberOfDoors = numberOfDoors;
}
public void setOptions(String[] options) {
this.options = options;
}
}
这是 VehicleRequest 文件
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
public class VehicleRequest {
private int year;
private String make;
private String model;
@JsonIgnore
private static final ObjectMapper objectMapper = new ObjectMapper();
public static String toJSON(VehicleRequest vehicle) throws Exception {
return objectMapper.writeValueAsString(vehicle);
}
public static VehicleRequest fromJSON(String input) throws Exception{
return objectMapper.readValue(input, VehicleRequest.class);
}
protected VehicleRequest() {}
public VehicleRequest(int year, String make, String model) {
this.year = year;
this.make =make;
this.model=model;
}
@Override
public String toString() {
return String.format(
"Vehicle: [year=%d, make='%s', model='%s']",
year,make,model);
}
public int getYear() {
return year;
}
public String getMake(){return make;}
public String getModel(){return model;}
public void setYear(int year) {
this.year = year;
}
public void setMake(String make){
this.make=make;
}
public void setModel(String model){
this.model=model;
}
}
这是服务器
public class Server {
private ServerSocket serverSocket;
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public void start(int port) throws Exception {
serverSocket = new ServerSocket(port);
clientSocket = serverSocket.accept();
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
VehicleRequest request = VehicleRequest.fromJSON(inputLine);
VehicleResponse response = new VehicleResponse(request,10000,12000,5,4, null);
out.println(VehicleResponse.toJSON(response));
}
}
public void stop() throws IOException {
in.close();
out.close();
clientSocket.close();
serverSocket.close();
}
public static void main(String[] args) {
Server server = new Server();
try {
server.start(4444);
server.stop();
} catch(Exception e) {
e.printStackTrace();
}
}
}
这是客户
public class Client {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public void startConnection(String ip, int port) throws IOException {
clientSocket = new Socket(ip, port);
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
}
public VehicleRequest sendRequest() throws Exception {
out.println(VehicleRequest.toJSON(new VehicleRequest(2008,"Honda","Civic")));
return VehicleRequest.fromJSON(in.readLine());
}
public void stopConnection() throws IOException {
in.close();
out.close();
clientSocket.close();
}
public static void main(String[] args) {
Client client = new Client();
try {
client.startConnection("127.0.0.1", 4444);
System.out.println(client.sendRequest().toString());
client.stopConnection();
} catch(Exception e) {
e.printStackTrace();
}
}
}
我得到的结果是
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "request" (class edu.sdccd.cisc191.template.VehicleRequest), not marked as ignorable (3 known properties: "model", "year", "make"])
at [Source: (String)"{"request":{"year":2008,"make":"Honda","model":"Civic"},"milesOnVehicle":10000,"price":12000,"numberOfSeats":5,"numberOfDoors":4,"options":null}"; line: 1, column: 13] (through reference chain: edu.sdccd.cisc191.template.VehicleRequest["request"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:855)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1212)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1604)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1582)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:299)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:156)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3434)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3402)
at edu.sdccd.cisc191.template.VehicleRequest.fromJSON(VehicleRequest.java:17)
at edu.sdccd.cisc191.template.Client.sendRequest(Client.java:32)
at edu.sdccd.cisc191.template.Client.main(Client.java:44)
}
在行 VehicleRequest.fromJSON(in.readLine());
中,您试图解析输入的内容,似乎是:
{
"request":{
"year":2008,
"make":"Honda",
"model":"Civic"
},
"milesOnVehicle":10000,
"price":12000,
"numberOfSeats":5,
"numberOfDoors":4,
"options":null
}
但是,您希望它可以解析为 VehicleRequest
,这是不可能的,因为它只包含 3 个参数,而不是全部。您要么将其解析为 VehicleResponse
,如下所示:
VehicleResponse.fromJSON(in.readLine());
或者您将输入更改为可以解析为 VehicleRequest
:
的内容
{
"year":2008,
"make":"Honda",
"model":"Civic"
}
如果我正确理解了您的代码,那么您正试图在 Client
和 Server
之间进行通信。在这种情况下,您需要更改 Client
代码:
public class Client {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public void startConnection(String ip, int port) throws IOException {
clientSocket = new Socket(ip, port);
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
}
public VehicleResponse sendRequest() throws Exception {
out.println(VehicleRequest.toJSON(new VehicleRequest(2008,"Honda","Civic")));
return VehicleResponse.fromJSON(in.readLine());
}
public void stopConnection() throws IOException {
in.close();
out.close();
clientSocket.close();
}
public static void main(String[] args) {
Client client = new Client();
try {
client.startConnection("127.0.0.1", 4444);
System.out.println(client.sendRequest().toString());
client.stopConnection();
} catch(Exception e) {
e.printStackTrace();
}
}
}
虽然 , I would like to share some practical advice on JSON-conversion especially when implementing web-API facades 喜欢你的 Client
/ Server
classes:
- 带参数的纯函数使代码更易于测试:
send(request)
- content-conversion(from/into 格式,如 JSON)是一个(交叉)单独关注点:注入一个可重用的组件,如
ObjectMapper
到客户端和服务器(表示层),使您的 request/response classes 格式独立
生成的代码可能对您有吸引力,因为
- 更具可读性
- 更容易理解和调试
- 少耦合
- 更易于维护和扩展
请求响应:send
作为纯函数
通常一个客户端的职责是发送多个不同的请求。所以参数化你的 send
方法。
设计问题
之前:没有参数,只有静态硬编码请求。因此不可测试。
public VehicleRequest sendRequest() throws Exception {
out.println(VehicleRequest.toJSON(new VehicleRequest(2008,"Honda","Civic")));
return VehicleRequest.fromJSON(in.readLine());
}
注意:方法签名...Request sendRequest()
可以读作“output = produceInput()”。这种设计方法或函数的风格被称为 pure function:它通常将输入转换为新的输出 - 它会产生一些东西,比如响应。
设计问题:
这里不能输入。返回的产品或答案可能总是相同的。您不想提出不同的问题或发送不同的请求吗?这样预期的答案取决于给定的请求。
改善
之后:添加了参数以使用不同的 request
s 进行测试。简化的方法名称。
public VehicleResponse send(VehicleRequest request) throws Exception {
out.println(VehicleRequest.toJSON(request));
return VehicleResponse.fromJSON(in.readLine());
}
这可以在您的 main
或 response = client.send(new VehicleRequest(2008,"Honda","Civic"))
.
等测试中调用
对象表示:JSON-转换为单独关注
有一些基本的表示格式,如 String
与每个 class 紧密耦合。在 Java 中,这个 关注点 继承自 Object
class 的方法 toString()
并且对于一些包装器-classes像 Integer.valueOf(String s)
.
但是更复杂的表示,尤其是与 REST 或 HTTP 相关的 JSON、XML
、HTML
,还有二进制格式通常由称为 Separation of Concerns 的设计原则处理。特别是在 Web 应用程序中(比如简化的客户端-服务器架构),这会导致分离层(最佳实践):
- 表示层(关注 UI、Web、通信).. 例如您的
Client
/ Server
classes 和 Jackson 的 ObjectMapper
处理 JSON-conversion
- 业务层(关注逻辑).. 例如你的
Vehicle..
classes
- 持久层(关注存储、数据库等)
将这些单独的关注点显示为层,例如在维基百科中 Multi-tier Architecture or in Robert C. Martin's Clean Architecture.
设计问题
外部依赖 Jackson 是您的 DTO-class 的重复部分,例如 ..Request
和 ..Response
。所以两者都取决于 Jackson 和 JSON-格式。
之前:表示层的关注点混合到DTO-classes中,这可能会随着业务层的变化而变化。现在他们需要改变每种格式,客户端也可以支持(强耦合和 2 个改变的原因)。
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
// ..
@JsonIgnore
private static final ObjectMapper objectMapper = new ObjectMapper();
// duplicated code, implemented similarly for both classes
public static String toJSON(VehicleRe.. vehicle) throws Exception {
return objectMapper.writeValueAsString(vehicle);
}
public static VehicleRe.. fromJSON(String input) throws Exception{
return objectMapper.readValue(input, VehicleRe.. .class);
}
注意:您可以将此横切关注点(许多 classes 使用)移至客户端。这样,您的(实体)classes 将独立于 format/mapping-framework,并且客户端可以在将来轻松扩展以支持其他格式(如 XML)。
改善
之后:在 REST 中,格式是表示层的关注点。客户端负责内容协商和转换。
class Client {
private final ObjectMapper mapper;
// Mapper can be injected as dependency, configurable from ouside
public Client(ObjectMapper mapper) {
this.mapper = mapper;
// optional remaining initialization, could add ip, port, etc.
}
public VehicleResponse send(VehicleRequest vehicle) throws Exception {
String request = objectMapper.writeValueAsString(vehicle); // to <textual-format>
out.println(request); // request as String (now JSON, future XML)
String response = in.readLine(); // request as String (now JSON, future XML)
return objectMapper.readValue(response); // from <textual-format>
}
}
这样你就可以在你的主方法中简单地创建一个特殊的 XML-capable 客户端通过配置像
Client xmlClient = new Client(new XmlMapper())
或按原样使用(使用 JSON 作为内容交换格式):Client client = new Client(new ObjectMapper())
.
对于你的 CS 课程和家庭作业,这可能是太多和过度工程化了。
然而,在“真实代码”中,如 Spring
等专业网络框架中所见,这是好的或最佳实践,有助于开发可扩展和可维护的软件,请参见 SOLID。
我目前正在做关于客户端和服务器主题的作业:
我创建了一个名为 VehileRequest.java
的 class,它将从 Client.java
获取三个变量(年份、品牌、型号)并将它们传递给 Server.java
然后 Server
将得到来自 VehicleRespone.java
的信息并显示有关价格、里程 vv...
据我了解,程序无法识别传递给响应文件的请求。
我无法将请求传递给响应,以便响应能够理解。请任何帮助。谢谢
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
public class VehicleResponse {
private VehicleRequest request;
private int milesOnVehicle;
private int price;
private int numberOfSeats;
private int numberOfDoors;
private String[] options;
@JsonIgnore
private static final ObjectMapper objectMapper = new ObjectMapper();
public static String toJSON(VehicleResponse vehicle) throws Exception {
return objectMapper.writeValueAsString(vehicle);
}
public static VehicleResponse fromJSON(String input) throws Exception{
return objectMapper.readValue(input, VehicleResponse.class);
}
protected VehicleResponse() {}
public VehicleResponse(VehicleRequest request, int milesOnVehicle,int price, int numberOfSeats, int numberOfDoors,String[] options) {
this.request=request;
this.milesOnVehicle=milesOnVehicle;
this.price=price;
this.numberOfSeats=numberOfSeats;
this.numberOfDoors=numberOfDoors;
this.options=options;
}
@Override
public String toString() {
return String.format(
"Vehicle request:[miles=%d, price=%d, number of seats=%d,number of doors=%d, option='%s']",
milesOnVehicle,price,numberOfSeats,numberOfDoors,options);
}
public VehicleRequest getRequest() {return request;}
public int getMilesOnVehicle(){return milesOnVehicle;};
public int getPrice(){return price;}
public int getNumberOfDoors() {return numberOfDoors;}
public int getNumberOfSeats() {return numberOfSeats;}
public String[] getOptions() {return options;}
public void setRequest(VehicleRequest request) {
this.request = request;
}
public void setMilesOnVehicle(int milesOnVehicle) {
this.milesOnVehicle = milesOnVehicle;
}
public void setPrice(int price) {
this.price = price;
}
public void setNumberOfSeats(int numberOfSeats) {
this.numberOfSeats = numberOfSeats;
}
public void setNumberOfDoors(int numberOfDoors) {
this.numberOfDoors = numberOfDoors;
}
public void setOptions(String[] options) {
this.options = options;
}
}
这是 VehicleRequest 文件
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
public class VehicleRequest {
private int year;
private String make;
private String model;
@JsonIgnore
private static final ObjectMapper objectMapper = new ObjectMapper();
public static String toJSON(VehicleRequest vehicle) throws Exception {
return objectMapper.writeValueAsString(vehicle);
}
public static VehicleRequest fromJSON(String input) throws Exception{
return objectMapper.readValue(input, VehicleRequest.class);
}
protected VehicleRequest() {}
public VehicleRequest(int year, String make, String model) {
this.year = year;
this.make =make;
this.model=model;
}
@Override
public String toString() {
return String.format(
"Vehicle: [year=%d, make='%s', model='%s']",
year,make,model);
}
public int getYear() {
return year;
}
public String getMake(){return make;}
public String getModel(){return model;}
public void setYear(int year) {
this.year = year;
}
public void setMake(String make){
this.make=make;
}
public void setModel(String model){
this.model=model;
}
}
这是服务器
public class Server {
private ServerSocket serverSocket;
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public void start(int port) throws Exception {
serverSocket = new ServerSocket(port);
clientSocket = serverSocket.accept();
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String inputLine;
while ((inputLine = in.readLine()) != null) {
VehicleRequest request = VehicleRequest.fromJSON(inputLine);
VehicleResponse response = new VehicleResponse(request,10000,12000,5,4, null);
out.println(VehicleResponse.toJSON(response));
}
}
public void stop() throws IOException {
in.close();
out.close();
clientSocket.close();
serverSocket.close();
}
public static void main(String[] args) {
Server server = new Server();
try {
server.start(4444);
server.stop();
} catch(Exception e) {
e.printStackTrace();
}
}
}
这是客户
public class Client {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public void startConnection(String ip, int port) throws IOException {
clientSocket = new Socket(ip, port);
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
}
public VehicleRequest sendRequest() throws Exception {
out.println(VehicleRequest.toJSON(new VehicleRequest(2008,"Honda","Civic")));
return VehicleRequest.fromJSON(in.readLine());
}
public void stopConnection() throws IOException {
in.close();
out.close();
clientSocket.close();
}
public static void main(String[] args) {
Client client = new Client();
try {
client.startConnection("127.0.0.1", 4444);
System.out.println(client.sendRequest().toString());
client.stopConnection();
} catch(Exception e) {
e.printStackTrace();
}
}
}
我得到的结果是
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "request" (class edu.sdccd.cisc191.template.VehicleRequest), not marked as ignorable (3 known properties: "model", "year", "make"])
at [Source: (String)"{"request":{"year":2008,"make":"Honda","model":"Civic"},"milesOnVehicle":10000,"price":12000,"numberOfSeats":5,"numberOfDoors":4,"options":null}"; line: 1, column: 13] (through reference chain: edu.sdccd.cisc191.template.VehicleRequest["request"])
at com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException.from(UnrecognizedPropertyException.java:61)
at com.fasterxml.jackson.databind.DeserializationContext.handleUnknownProperty(DeserializationContext.java:855)
at com.fasterxml.jackson.databind.deser.std.StdDeserializer.handleUnknownProperty(StdDeserializer.java:1212)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownProperty(BeanDeserializerBase.java:1604)
at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.handleUnknownVanilla(BeanDeserializerBase.java:1582)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:299)
at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:156)
at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4482)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3434)
at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3402)
at edu.sdccd.cisc191.template.VehicleRequest.fromJSON(VehicleRequest.java:17)
at edu.sdccd.cisc191.template.Client.sendRequest(Client.java:32)
at edu.sdccd.cisc191.template.Client.main(Client.java:44)
}
在行 VehicleRequest.fromJSON(in.readLine());
中,您试图解析输入的内容,似乎是:
{
"request":{
"year":2008,
"make":"Honda",
"model":"Civic"
},
"milesOnVehicle":10000,
"price":12000,
"numberOfSeats":5,
"numberOfDoors":4,
"options":null
}
但是,您希望它可以解析为 VehicleRequest
,这是不可能的,因为它只包含 3 个参数,而不是全部。您要么将其解析为 VehicleResponse
,如下所示:
VehicleResponse.fromJSON(in.readLine());
或者您将输入更改为可以解析为 VehicleRequest
:
{
"year":2008,
"make":"Honda",
"model":"Civic"
}
如果我正确理解了您的代码,那么您正试图在 Client
和 Server
之间进行通信。在这种情况下,您需要更改 Client
代码:
public class Client {
private Socket clientSocket;
private PrintWriter out;
private BufferedReader in;
public void startConnection(String ip, int port) throws IOException {
clientSocket = new Socket(ip, port);
out = new PrintWriter(clientSocket.getOutputStream(), true);
in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
}
public VehicleResponse sendRequest() throws Exception {
out.println(VehicleRequest.toJSON(new VehicleRequest(2008,"Honda","Civic")));
return VehicleResponse.fromJSON(in.readLine());
}
public void stopConnection() throws IOException {
in.close();
out.close();
clientSocket.close();
}
public static void main(String[] args) {
Client client = new Client();
try {
client.startConnection("127.0.0.1", 4444);
System.out.println(client.sendRequest().toString());
client.stopConnection();
} catch(Exception e) {
e.printStackTrace();
}
}
}
虽然 Client
/ Server
classes:
- 带参数的纯函数使代码更易于测试:
send(request)
- content-conversion(from/into 格式,如 JSON)是一个(交叉)单独关注点:注入一个可重用的组件,如
ObjectMapper
到客户端和服务器(表示层),使您的 request/response classes 格式独立
生成的代码可能对您有吸引力,因为
- 更具可读性
- 更容易理解和调试
- 少耦合
- 更易于维护和扩展
请求响应:send
作为纯函数
通常一个客户端的职责是发送多个不同的请求。所以参数化你的 send
方法。
设计问题
之前:没有参数,只有静态硬编码请求。因此不可测试。
public VehicleRequest sendRequest() throws Exception {
out.println(VehicleRequest.toJSON(new VehicleRequest(2008,"Honda","Civic")));
return VehicleRequest.fromJSON(in.readLine());
}
注意:方法签名...Request sendRequest()
可以读作“output = produceInput()”。这种设计方法或函数的风格被称为 pure function:它通常将输入转换为新的输出 - 它会产生一些东西,比如响应。
设计问题: 这里不能输入。返回的产品或答案可能总是相同的。您不想提出不同的问题或发送不同的请求吗?这样预期的答案取决于给定的请求。
改善
之后:添加了参数以使用不同的 request
s 进行测试。简化的方法名称。
public VehicleResponse send(VehicleRequest request) throws Exception {
out.println(VehicleRequest.toJSON(request));
return VehicleResponse.fromJSON(in.readLine());
}
这可以在您的 main
或 response = client.send(new VehicleRequest(2008,"Honda","Civic"))
.
对象表示:JSON-转换为单独关注
有一些基本的表示格式,如 String
与每个 class 紧密耦合。在 Java 中,这个 关注点 继承自 Object
class 的方法 toString()
并且对于一些包装器-classes像 Integer.valueOf(String s)
.
但是更复杂的表示,尤其是与 REST 或 HTTP 相关的 JSON、XML
、HTML
,还有二进制格式通常由称为 Separation of Concerns 的设计原则处理。特别是在 Web 应用程序中(比如简化的客户端-服务器架构),这会导致分离层(最佳实践):
- 表示层(关注 UI、Web、通信).. 例如您的
Client
/Server
classes 和 Jackson 的ObjectMapper
处理 JSON-conversion - 业务层(关注逻辑).. 例如你的
Vehicle..
classes - 持久层(关注存储、数据库等)
将这些单独的关注点显示为层,例如在维基百科中 Multi-tier Architecture or in Robert C. Martin's Clean Architecture.
设计问题
外部依赖 Jackson 是您的 DTO-class 的重复部分,例如 ..Request
和 ..Response
。所以两者都取决于 Jackson 和 JSON-格式。
之前:表示层的关注点混合到DTO-classes中,这可能会随着业务层的变化而变化。现在他们需要改变每种格式,客户端也可以支持(强耦合和 2 个改变的原因)。
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
// ..
@JsonIgnore
private static final ObjectMapper objectMapper = new ObjectMapper();
// duplicated code, implemented similarly for both classes
public static String toJSON(VehicleRe.. vehicle) throws Exception {
return objectMapper.writeValueAsString(vehicle);
}
public static VehicleRe.. fromJSON(String input) throws Exception{
return objectMapper.readValue(input, VehicleRe.. .class);
}
注意:您可以将此横切关注点(许多 classes 使用)移至客户端。这样,您的(实体)classes 将独立于 format/mapping-framework,并且客户端可以在将来轻松扩展以支持其他格式(如 XML)。
改善
之后:在 REST 中,格式是表示层的关注点。客户端负责内容协商和转换。
class Client {
private final ObjectMapper mapper;
// Mapper can be injected as dependency, configurable from ouside
public Client(ObjectMapper mapper) {
this.mapper = mapper;
// optional remaining initialization, could add ip, port, etc.
}
public VehicleResponse send(VehicleRequest vehicle) throws Exception {
String request = objectMapper.writeValueAsString(vehicle); // to <textual-format>
out.println(request); // request as String (now JSON, future XML)
String response = in.readLine(); // request as String (now JSON, future XML)
return objectMapper.readValue(response); // from <textual-format>
}
}
这样你就可以在你的主方法中简单地创建一个特殊的 XML-capable 客户端通过配置像
Client xmlClient = new Client(new XmlMapper())
或按原样使用(使用 JSON 作为内容交换格式):Client client = new Client(new ObjectMapper())
.
对于你的 CS 课程和家庭作业,这可能是太多和过度工程化了。
然而,在“真实代码”中,如 Spring
等专业网络框架中所见,这是好的或最佳实践,有助于开发可扩展和可维护的软件,请参见 SOLID。