使用多个提供程序的客户端应用程序应使用什么设计/模式?

2024-04-23

这是一个与设计相关的问题。

假设我们有一个名为 ClientAPI 的公共 API,其中包含一些 Web 方法,例如 CreateAccount、GetAccount。根据客户的不同,我们使用许多不同的提供商来满足这些请求。

假设我们有 ProviderS、ProviderS 和 ProviderS。

ProviderA 具有 CreateAccount 的方法签名/实现,仅需要(名字,姓氏)并使用 ProviderA 创建帐户。

ProviderB 具有 CreateAccount 的方法签名/实现,需要(名字、姓氏、电子邮件、出生日期)并使用 ProviderB 创建帐户。

ProviderC 具有 CreateAccount 的方法签名/实现,需要(昵称、CompanyKey、电子邮件)并使用 ProviderC 创建帐户。

客户不需要知道或关心他们是哪家提供商。当调用客户端 API 方法 CreateAccount 时,客户端 api 将计算出需要调用的提供程序并调用该提供程序方法。

我这里有两个问题。

1)该模型的最佳设计/模式是什么?另请记住,提供商的数量将会增加——我们将增加更多的提供商。

2)关于传递参数——目前ClientAPI CreateAccount方法签名是一大行变量,如果新的提供者需要新值,方法签名会添加另一个变量,这显然会破坏旧的实现等。将方法签名中的参数数组/列表/字典传递到下面的提供程序中是一种很好的做法,或者有更好的方法吗?


这确实是一个有趣的问题。我在从事的不同项目中很少遇到这样的问题。阅读您的问题后,我注意到您面临两个不同的挑战:

  1. 适当选择供应商ClientAPI
  2. 每个提供者所需的参数数量和类型不同。

当我设计服务或新功能时,我喜欢通过尽量减少支持新功能所需的更改数量来推理设计。就您而言,这将是添加新的身份验证提供程序。我现在至少想到了三种不同的实施方法。在我看来,没有完美的解决方案。您必须根据权衡选择其中之一。下面,我尝试提出一些解决上面列出的两个痛点的选项及其优点和缺点。

放松类型

无论我们做什么,无论我们使用多态性抽象复杂性有多好,总会有不同的类型或组件通过需要不同的信息集来将自己与其相似的事物区分开来。根据您想要在设计中投入多少精力来保持强类型以及多态抽象的不同程度,添加新功能时将需要更多更改。下面是一个实现示例,它不强制用户提供的各种信息的类型。

public class UserData {
    private AuthType type;
    private String firstname;
    private String lastname;
    private Map<String, String> metadata;
}

public enum AuthType {
    FACEBOOK, GPLUS, TWITTER;
}

public interface AuthProvider {
    void createAccount(UserData userData);
    void login(UserCredentials userCredentials);
}

public class AuthProviderFactory {
    public AuthProvider get(AuthType type) {
        switch(type) {
            case FACEBOOK:
                return new FacebookAuthProvider();
            case GPLUS:
                return new GPlusAuthProvider();
            case TWITTER:
                return new TwitterAuthProvider();
            default:
                throw new IllegalArgumentException(String.format('Invalid authentication type %s', type));
        }
    }
}

// example of usage
UserData userData = new UserData();
userData.setAuthType(AuthType.FACEBOOK);
userData.setFirstname('John');
userData.setLastname('Doe');
userData.putExtra('dateOfBirth', LocalDate.of(1997, 1, 1));
userData.putExtra('email', Email.fromString('[email protected] /cdn-cgi/l/email-protection'));

AuthProvider authProvider = new AuthProviderFactory().get(userData.getType());
authProvider.createAccount(userData);

优点

  • 只需添加新条目即可支持新的提供商AuthType and AuthProviderFactory.
  • Each AuthProvider确切地知道执行公开操作需要什么(createAccount(), ETC)。逻辑和复杂性都得到了很好的封装。

缺点

  • 中的几个参数UserData不会是强类型的。一些AuthProvider需要额外参数的必须查找它们,即metadata.get('email').

Typed UserData

我假设负责调用的组件AuthProviderFactory已经对它需要的提供商类型有所了解,因为它必须填写UserData以及成功所需的所有信息createAccount()称呼。那么,让这个组件创建正确的类型怎么样?UserData?

public class UserData {
    private String firstname;
    private String lastname;
}

public class FacebookUserData extends UserData {
    private LocalDate dateOfBirth;
    private Email email;
}

public class GplusUserData extends UserData {
    private Email email;
}

public class TwitterUserData extends UserData {
    private Nickname nickname;
}

public interface AuthProvider {
    void createAccount(UserData userData);
    void login(UserCredentials userCredentials);
}

public class AuthProviderFactory {
    public AuthProvider get(UserData userData) {
        if (userData instanceof FacebookUserData) {
            return new FacebookAuthProvider();
        } else if (userData instanceof GplusUserData) {
            return new GPlusAuthProvider();
        } else if (userData instanceof TwitterUserData) {
            return new TwitterAuthProvider();
        }
        throw new IllegalArgumentException(String.format('Invalid authentication type %s', userData.getClass()));
    }
}

// example of usage
FacebookUserData userData = new FacebookUserData();
userData.setFirstname('John');
userData.setLastname('Doe');
userData.setDateOfBirth(LocalDate.of(1997, 1, 1));
userData.setEmail(Email.fromString('[email protected] /cdn-cgi/l/email-protection'));

AuthProvider authProvider = new AuthProviderFactory().get(userData);
authProvider.createAccount(userData);

优点

  • 专门形式UserData包含强类型属性。
  • 只需创建新的即可支持新的提供商UserData类型并添加新条目AuthProviderFactory.
  • Each AuthProvider确切地知道执行公开操作需要什么(createAccount(), ETC)。逻辑和复杂性都得到了很好的封装。

缺点

  • AuthProviderFactory uses instanceof用于选择合适的AuthProvider.
  • 爆炸式UserData子类型和潜在的代码重复。

Typed UserData重新审视

我们可以尝试通过重新引入枚举来消除代码重复AuthType到我们之前的设计并制作我们的UserData子类更通用一些。

public interface UserData {
    AuthType getType();
}

public enum AuthType {
    FACEBOOK, GPLUS, TWITTER;
}

public class BasicUserData implements UserData {
    private AuthType type:
    private String firstname;
    private String lastname;

    public AuthType getType() { return type; }
}

public class FullUserData extends BasicUserData {
    private LocalDate dateOfBirth;
    private Email email;
}

public class EmailUserData extends BasicUserData {
    private Email email;
}

public class NicknameUserData extends BasicUserData {
    private Nickname nickname;
}

public interface AuthProvider {
    void createAccount(UserData userData);
    void login(UserCredentials userCredentials);
}

public class AuthProviderFactory {
    public AuthProvider get(AuthType type) {
        switch(type) {
            case FACEBOOK:
                return new FacebookAuthProvider();
            case GPLUS:
                return new GPlusAuthProvider();
            case TWITTER:
                return new TwitterAuthProvider();
            default:
                throw new IllegalArgumentException(String.format('Invalid authentication type %s', type));
        }
    }
}

// example of usage
FullUserData userData = new FullUserData();
userData.setAuthType(AuthType.FACEBOOK);
userData.setFirstname('John');
userData.setLastname('Doe');
userData.setDateOfBirth(LocalDate.of(1997, 1, 1));
userData.setEmail(Email.fromString('[email protected] /cdn-cgi/l/email-protection'));

AuthProvider authProvider = new AuthProviderFactory().get(userData.getType());
authProvider.createAccount(userData);

优点

  • 专门形式UserData包含强类型属性。
  • Each AuthProvider确切地知道执行公开操作需要什么(createAccount(), ETC)。逻辑和复杂性都得到了很好的封装。

缺点

  • 除了添加新条目之外AuthProviderFactory并创建新的子类型UserData,新的提供者将需要在枚举中添加一个新条目AuthType.
  • 我们仍然有爆炸UserData子类型,但现在这些子类型的可重用性已经增加。

Summary

我很确定这个问题还有其他几种解决方案。正如我上面提到的,也没有完美的解决方案。您可能必须根据他们的权衡和您想要实现的目标来选择一个。

我今天没有太多灵感,所以如果我想到其他事情,我会继续更新这篇文章。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用多个提供程序的客户端应用程序应使用什么设计/模式? 的相关文章

  • Meyers 的单例实现实际上是如何实现单例的

    我读了很多关于单例的内容 什么时候应该使用它们 什么时候不应该使用它们 以及如何安全地实现它们 我正在用 C 11 编写 并且遇到了 Meyer 的单例延迟初始化实现 如所示这个问题 https stackoverflow com ques
  • MouseAdapter:它使用哪种模式?

    我已经找到了很好的资源 这些资源告诉我 Java API 中的 MouseAdapter 没有使用适配器模式 问题是 MouseAdapter 是否实现了某种模式 我知道它的作用 它为 MouseListener 接口创建了一个具体的类 因
  • 在应用程序中搜索对象的设计模式

    需要一些有关设计模式的帮助 我正在创建一个应用程序 该应用程序在存储在单独表中的数据库中的对象上具有不同类型 例如 我有 5 种对象 A B C D E 我在数据库中有 5 个不同的表来存储每个对象 现在 我想在我的应用程序中实现搜索功能
  • 如何使用工厂来创建使用策略模式的对象?

    假设我们的在线商店有一个简单的支付功能 我们希望使用不同的交易处理器来管理不同的交易 交易可以是付款或退款 交易处理器可以是 Paypal 或 Payplug 所以我们有以下课程 class PaymentTransaction imple
  • 对于 Web 应用程序结构的最佳实践,您有哪些建议? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我在工作中做了很多定制应用程序 我正在尝试为新应用程序定义一些标准 有点像元素 CSS 你如何组织样式表 我是否应该为整个网站提供一个基本样式表
  • PHP 设计模式

    好的 我想首先让您注意到我一直在寻找 php mvc 设计模式 所以我还没有真正找到我想要的东西 现在 在写我的问题时 我找不到我要问的任何相关且具体的问题 但我知道我可能是错的 无论如何考虑这个问题和一个可能有用的问题包 Question
  • 是否有用于序列化和反序列化各种格式的对象层次结构的模式?

    给定一个复杂的对象层次结构 幸运的是它不包含循环引用 如何实现支持各种格式的序列化 我不是来讨论实际实施的 相反 我正在寻找可能派上用场的设计模式的提示 更准确地说 我正在使用 Ruby 我想解析 XML 和 JSON 数据以构建复杂的对象
  • “上下文模式”的这种实现看起来不错吗? [关闭]

    Closed 这个问题是无关 help closed questions 目前不接受答案 我有多个处理单元可能存在于一个数组中 每个处理单元都有自己的参数 我想使用以下方式传达每个处理单元的参数上下文模式在它被建议作为另一个问题的解答 ht
  • Android gridview 在片段适配器构造函数中丢失

    我正在尝试实现图像网格 我在一项活动中通过参考这个做到了这一点link http developer android com guide topics ui layout gridview html example现在 如果我尝试在扩展 F
  • 就MVC模式而言,Struts 2中使用的Action是什么?

    在Struts2中 控制器将请求分派给Action Action将其传递给后端逻辑 这可以看作是一个非常大的过程 model 处理请求 JSP代表视图 Struts2中如何定义Action 肯定是not一个看法 是控制器还是模型 Strut
  • 使用 Doctrine2 时的多重歧视级别

    我正在使用 Doctrine2 来管理我的模型 如下 有一个抽象概念Content与复合模式Gallery 也是一个抽象概念Media从中Video and Image继承 我的选择是添加鉴别器Content and Media表以便区分G
  • 确定方法调用顺序的接口设计模式

    我想创建一个具有多种方法的 Java 接口 但我希望界面的用户只能按照我定义的顺序或顺序调用方法 例如buyTicket 不应在此之前调用reserveTicket 问 有没有设计模式或任何关于如何做到这一点的提示 我考虑过 A 接口被包装
  • “TryParse / Parse like”模式:实现它的最佳方法是什么

    这个问题是来自的后续问题如何指示方法不成功 https stackoverflow com questions 161822 how to indicate that a method was unsuccessful xxx Tryxxx
  • 与竞争的消费者顺序处理消息

    Problem 我以特定顺序 FIFO 在队列上接收消息 比如订单 我的队列中有竞争的消费者 为了进一步增加复杂性 消费者可能只对订单的特定版本感兴趣 具体取决于其状态 例如版本 1 版本 2 和版本 5 订单版本号在订单上可用 但不能用于
  • 在 C++ 中创建观察者设计模式的好方法

    我正在尝试用 C 实现观察者设计模式 如下所示 include
  • 三层 Asp.Net 应用程序中的异常处理

    1 据我了解 在three tierAsp Net应用程序我们应该通过以下方式实现异常处理 a 我们应该把try catch围绕代码块 位于三层中的任何一层 我们希望页面能够从该代码块正常恢复 当此代码生成异常时 b 我们不应该放try c
  • C 中的 N 依赖注入 - 比链接器定义的数组更好的方法?

    Given a 库模块 在下文中称为Runner 它作为可重复使用的组件 无需重新编译 即静态链接库 中应用程序分区架构的 而不是主分区 请注意 它仅包含main 出于演示目的 Given a set 顺序无关 调用的其他模块 对象Call
  • 如何简化 ASP.NET MVC 中的全状态交错模式对话框

    我需要在多对多模式对话框中保留状态渐进增强 http en wikipedia org wiki Progressive enhancementASP NET MVC 项目中的方式 在我的代码中 当禁用 javascript 时 模式对话框
  • 定时任务应该放在哪一层?

    我正在尝试使用分层架构来实现 DDD 应用程序 我有 基础设施层 实现应用程序的技术特定部分的层 领域层 包含领域模型的层 应用层 包含与领域模型交互的干扰的层 接口层 从外部接收事件的层 经典的 3 层 基础设施 架构非常清晰 但我的应用
  • 工厂模式和策略模式

    我需要创建一个类来负责结果集处理 但可能会发生应该使用不同的算法来处理该结果集的情况 我知道以下选项 1 使用策略模式 伪代码如下 interface Strategy processResultSet ResultSet rs class

随机推荐

  • 将导航栏切换按钮向右对齐

    我正在尝试 Bootstrap 4 更具体地说是导航栏菜单 有没有办法让小导航栏切换按钮对齐到页面的右侧 而不是让它浮动到徽标旁边的左侧 这是我当前的代码 media min width 992px navbar nav li a line
  • GAE 中的拉取队列是否表现出一致的 FIFO 行为?

    App Engine 中的推送队列通常是先进先出队列 https developers google com appengine docs java taskqueue overview push The Rate of Task Exec
  • SQL 日期转换结果为“无效的数字格式模型参数”。

    我必须select一些数据来自Oracle 11g数据库 但我似乎不明白为什么以下select查询失败 SELECT INFO ID INFO DETAIL IMPORTANT FLG DELETE FLG CREATE TIME DISP
  • OpenCL:在内核中手动引发异常

    是否可以在 OpenCL 中手动引发异常 仅用于调试目的 我的代码中有一个非常奇怪的错误 当我计算两个双精度值并将它们相加时 主机报告 CL OUT OF RESOURCE 但是 如果我不添加这两个值 主机不会报告任何错误 OpenCL 不
  • 如何使用下面定义的类?

    class A public B b class B public A a 我不能在 A 类 B b 中写入 因为 B 类定义如下 有什么办法让它发挥作用吗 thanks 这不可能 您需要在其中一个类中使用指针或引用 class B for
  • Sql 异常:管道的另一端没有进程

    我无法从 C 代码访问我的 sql 服务器连接 我收到此错误 Sql 异常 管道的另一端没有进程 这是我的 app config 中的连接字符串
  • 如何在JavaFX中使用Node类的intersect()方法?

    JavaFX Node 类提供了两种相交方法 intersects Bounds localBounds and intersects double localX double localY double localWidth double
  • XSLT 和临时文档

    我正在尝试处理一个 xml 文件 该文件有几个不同的状态组 例如
  • 以角度实现 canActivate auth 防护

    我有一个带有此函数的服务 它会在令牌有效或无效时返回 true 或 false loggedIn return this http get http localhost 3000 users validateToken map res gt
  • C# 中的字符串加密和解密? [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 如何在 C 中加密和解密字符串 编辑 2013 年 10 月 虽然我随着时间的推移编辑了这个答案以解决缺点 但请参阅jbtule 的回
  • 使用自定义视图填充 ListFragments?

    以前 我可以将布局扩展为 ListView 的自定义视图层次结构 但我不知道如何对 listFragment 执行相同的操作 假设我有一个 item list 布局 其中有一个 ImageView 和 2 个文本视图 我想将其膨胀以在我的
  • Symfony 身份验证 - 无法通过生产中的登录页面

    我已经在本地开发服务器上设置了 Symfony 身份验证 它在生产和开发环境中都完美运行 今天我注册了一个域进行测试并将我的代码推送到 AWS EC2 服务器 我可以毫无问题地访问登录页面但一旦我尝试登录 我就会直接重定向回登录页面 没有任
  • 添加后收集所有非未定义值

    我对伊莎贝尔有以下补充 function proj add real real bit real real bit real real bit where proj add x1 y1 l x2 y2 j add x1 y1 x2 y2 l
  • 为什么 memo.Lines 使用 TStrings 而不是 TStringList?

    为什么Memo Lines使用抽象类TStrings 为什么不使用TStringList反而 我应该将其转换为TStringList在使用它之前 TMemo Lines TListBox Items TComboBox Items ETC
  • 复制 Ctrl-C 的快捷方式在 Android Studio 中不起作用

    我可以从其他程序复制文本并将其粘贴到 Android Studio 我可以右键单击并复制选定的文本 我可以使用快捷方式剪切文本并粘贴它 但是 在使用快捷键 Ctrl C 复制文本时 我经常无法复制 这是一个非常烦人的问题 这里有人知道如何解
  • 如何配置 apache 查看隐藏(`.`)文件?

    如何在 apache 中显示目录列表 隐藏文件 我都尝试过
  • 使用 Rack::SSLEnforcer 时从 Devise 确认链接中删除 HTTPS

    我在 Rails 应用程序中使用 Devise 和 Rack SSLEnforcer 当用户收到确认电子邮件时 它看起来像 http mysite com 443 users confirmation confirmation token
  • 单击开放图层版本 5 标记上的特征

    我正在使用开放层库版本 5 我需要标记上的 onClick 事件来执行一些业务逻辑 任何人都可以帮我解决这个问题 谢谢 我已经尝试了所有代码和片段 我正在使用这个库来响应js import Feature from ol Feature i
  • 通过css替换图片

    我正在 Stylish 一个 Firefox 插件 中编写代码来更改显示的图像 image 属性没有 div 标签 所以我必须使用它 img src s dschjungelplanet 因此 这将替换页面中任何位置的 img src 中的
  • 使用多个提供程序的客户端应用程序应使用什么设计/模式?

    这是一个与设计相关的问题 假设我们有一个名为 ClientAPI 的公共 API 其中包含一些 Web 方法 例如 CreateAccount GetAccount 根据客户的不同 我们使用许多不同的提供商来满足这些请求 假设我们有 Pro