【问题标题】:How can I avoid DTOs duplication without inheritance?如何在没有继承的情况下避免 DTO 重复?
【发布时间】:2020-05-28 22:36:13
【问题描述】:

我正在连接许多社交网络以登录我的应用程序。 对于每个社交网络响应,我都有一个 DTO。

public class GoogleUserInfo {
    private String id;
    private String firstName;
    private String lastName;
    private String email;
    private AgeRange ageRange;
    // more specific fields
}

public class FacebookUserInfo {
    private String id;
    private String firstName;
    private String lastName;
    private String email;
    private String picture;
    // more specific fields
}

public class AppleUserInfo {
    private String id;
    private String firstName;
    private String lastName;
    private String email;
    private Boolean emailVerified;
    // more specific fields
}

在每个社交网络连接器中,我都会采取类似的步骤来获取信息,所以我认为我可以使用一些 DTO,如下所示:

public class SocialNetworkInfo {
    protected String id;
    protected String firstName;
    protected String lastName;
    protected String email;
}

社交网络 DTO 可以扩展此功能以获得公共字段。然后我可以使用这个通用 DTO 来实现一个抽象连接器,处理连接器之间的所有重复逻辑(发出请求、解析响应等......):

abstract class AbstractConnector {
    abstract SocialNetworkInfo fetchUserInfo(String networkId);
    ...
}

但我意识到,在上面,在我的服务层中,我需要那些特定的字段来进行一些更改和操作。

SocialNetworkInfo networkUserInfo = facebookConnector.fetchUserInfo(facebookId);
facebookService.updatePicture(networkUserInfo.getPicture()); // can't access this specific field

您认为在不强制转换和避免逻辑或 DTO 重复的情况下,最好的解决方法是什么?

很想听听你的想法。

谢谢!

【问题讨论】:

  • AbstractConnector<I extends SocialNetworkInfo>

标签: java inheritance dto code-duplication


【解决方案1】:

根据你的情况,所有社交网络模型的性质都是一样的,所以你可以把通用属性移到CommonSocialInfo这样的共享类中。然后我建议为连接器提供接口,例如:

interface SocialNetworkConnector<T extends SocialNetworkInfo> {
    T fetchUserInfo(String userId);
}

当然,对于通用功能(用于连接器),定义实现上述接口的通用抽象类(实现模板模式)是个好主意。我看到您正在分别使用FacebookService 和相关连接器。我认为在这种情况下使用组合并让 SocialNetworkService 依赖于它的连接器是个好主意。简而言之,FacebookService 依赖于 FacebookConnecter 等。只是一个简单的例子:

public class FacebookService implements SocialNetworkService {
    private final SocialNetworkConnector<FacebookSocialInfo> connector;
    ...
}  

如果你需要实现多个社交服务,你可以使用工厂模式来生产需要的服务,快速示例:

interface SocialNetworkServiceFactory {
    SocialNetworkService getFacebookService();
    ...
}

如果您需要更详细的帮助,或者您在理解该想法时遇到困难 - 请随时提出!

【讨论】:

  • Op 说没有继承,不知道为什么。我肯定会做这样的事情......
  • 我想这更像是多态而不是继承......虽然
  • 非常感谢您采用这种方法!它阐明了如何解决这个问题并使我对泛型更感兴趣(这对我来说现在几乎是未知的)。 @RobC:抱歉,如果我表达错误,我认为继承仅限于访问服务层中的子类属性,我正在寻找替代方案或实现这一点的最佳方法。
  • 对你有帮助!
【解决方案2】:

如果你不想使用继承,我建议考虑composition。代码如下:

public class SocialNetworkInfo {
    private String id;
    private String firstName;
    private String lastName;
    private String email;
}

public class GoogleUserInfo {
    private SocialNetworkInfo socialNetworkInfo;
    private AgeRange ageRange;
    // more specific fields
}

public class FacebookUserInfo {
    private SocialNetworkInfo socialNetworkInfo;
    private String picture;
    // more specific fields
}

public class AppleUserInfo {
    private SocialNetworkInfo socialNetworkInfo;
    private Boolean emailVerified;
    // more specific fields
}

【讨论】:

  • 感谢您的意见。我想我不能用这种方法直接映射 JSON 响应。如果事实上也有办法,请告诉我!
  • @LeandroSuarez:当然,您可以轻松将其映射到 JSON。如果您使用 JAX-RS 或 Spring Boot,您甚至不需要为此进行任何配置。 非常奇怪的是,您要求“没有继承”的解决方案并接受了使用继承的答案并且与您的要求相矛盾
  • 真的,对不起。我没有意识到我把那个条件放在了标题中。我更倾向于避免施放。尽管我没有实施您的解决方案,但我还是要感谢您,因为另一个更适合我的代码。
  • @LeandroSuarez:放松 :) 如果另一个答案对你来说是最好的,你应该接受它作为最好的。为避免误解,您可以相应调整您的问题,并更好地解释为什么您谈到了继承以及什么是真正的原因。那么您的问题可能不仅对您有帮助,而且对其他人也有帮助。
猜你喜欢
  • 2017-01-25
  • 1970-01-01
  • 1970-01-01
  • 2015-11-25
  • 1970-01-01
  • 1970-01-01
  • 2018-07-08
  • 2016-12-13
  • 2022-01-14
相关资源
最近更新 更多