【问题标题】:Design generic interface for data object used throughout a service为整个服务中使用的数据对象设计通用接口
【发布时间】:2018-08-10 04:39:32
【问题描述】:

一直在迁移一些遗留代码,我遇到了这个。

@Getter
@Setter
public class CollectedData
{
    SkillResponse skills;
    TrendingResponse storyMatch;
    Map<String, String> slotData;
    Map<String, String> slotDataSecondSource;
    Boolean hasSlots;
    Boolean hasSlotsSecondSource;
    KnowledgeRequest request;
}

由于我一直在使用 java 8 并且习惯了流,我开始将这个响应类重构为 ..

@Getter
@Setter
public class CollectedData
{
    List<DataSupplierResponse> dataSupplierResponses;
    Metadata metadata;
}

DataSupplierResponse 应该是这样定义的接口。

public interface DataSupplierResponse<T>
{
    DataSupplierType getDataSupplierType();

    T getSupplierResponse();
}

实现示例:

public class DataSupplierResponseImpl implements DataSupplierResponse<TrendingResponse>
{
    private TrendingResponse mTrendingResponse;

    public DataSupplierResponseImpl(
        TrendingResponse trendingResponse)
    {
        mTrendingResponse = trendingResponse;
    }

    @Override
    public DataSupplierType getDataSupplierType()
    {
        return DataSupplierType.TRENDING_STORY;
    }

    @Override
    public TrendingResponse getSupplierResponse()
    {
        return mTrendingResponse;
    } 
}

目标是根据CollectedData 运行某些谓词。

Optional<DataSupplierResponse> first = data.getDataSupplierResponses().stream()
                .filter(res -> res.getDataSupplierType().equals(DataSupplierType.TRENDING_STORY))
                .findFirst();

这需要强制转换才能获得正确的对象。它返回对象

TrendingResponse match = first.get().getSupplierResponse();

因此,当我开始重构时,我假设通过创建返回不同数据的通用接口来解决数据可用的问题。为了使这段代码正常工作,我必须强制转换 getSupplierResponse 的返回对象,这违背了使用泛型的目的。为了我自己,我需要使这个 Data Carrier 对象尽可能的干净和美观。任何想法我应该如何构建这些类,和/或如何使用泛型来解决这个问题。

编辑:我知道 StackOverflow 社区喜欢强制执行客观、具体的答案,但还有其他地方可以解决设计问题吗?

【问题讨论】:

  • 你能编辑像SkillResponse这样的类吗?

标签: java generics interface architecture java-8


【解决方案1】:

您还必须在 CollectedData 中指定 List 并使用泛型。例如:

List<DataSupplierResponse> dataSupplierResponse;

实际上应该是:

List<DataSupplierResponse<YourType>> dataSupplierResponse;

其中YourType 对应于响应的类型。这是因为当使用RawType(一个没有实际指定泛型的泛型类)时,该类的所有泛型信息都会被消除。这就是为什么它返回 Objects 并且您必须手动转换它。

【讨论】:

  • 1+,OP 在代码的一部分中添加了泛型,但在另一部分中没有添加...
  • @Eugene 完全正确。虽然我不知道,如果列表中的所有元素都具有相同的返回类型。如果他们不这样做,那么答案将会完全不同,而且会更加困难
  • 老实说,我没有过多地讨论问题的细节,所以我在这里相信你
【解决方案2】:

除非在其他地方使用,否则我会去掉 enumeration 类型 DataSupplierType,因为 classes TrendingResponse(和其他)已经提供了区分标准。

(还要记住enums 已满classes

对此的完美响应是让您为响应实现一个基本类型,例如:

interface Response {
    int getScore(); // implement in subclasses
    String getName(); // implement in subclasses
}

class TrendingResponse implements Response {}
class SkillResponse implements Response {}
class OtherResponse implements Response {}

但这并不是绝对必要的。

此时,只需一个通用包装器 class 就足够了(不需要有一个基础 interface 并为每种类型的响应扩展它):

class DataSupplierResponse<T extends Response>  {
    private T t;

    public DataSupplierResponse(final T t) {
        this.t = t;
    }
    public T getSupplierResponse() {
        return this.t;
    }
}

这将允许您调用:

Optional<DataSupplierResponse<?>> first = data.responses
            .stream()
            .filter(response -> TrendingResponse.class.isAssignableFrom(response.getClass()))
            .findFirst();

first.ifPresent( o -> o.getSupplierResponse().getScore() );

或者干脆

Optional<?> first = data.responses
            .stream()
            .filter(response -> TrendingResponse.class.isAssignableFrom(response.getClass()))
            .map(r -> r::getSupplierResponse)
            .findFirst();

即使没有基本接口Response(仅用于定义响应中的常见行为),您的class DataSupplierResponse&lt;T&gt; 也不需要enumeration 类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-22
    • 1970-01-01
    • 2011-02-08
    • 1970-01-01
    相关资源
    最近更新 更多