Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/design-patterns/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Design patterns 对于使用多个提供者的客户端应用程序,要使用什么设计/模式?_Design Patterns_Architecture_Adapter_Strategy Pattern - Fatal编程技术网

Design patterns 对于使用多个提供者的客户端应用程序,要使用什么设计/模式?

Design patterns 对于使用多个提供者的客户端应用程序,要使用什么设计/模式?,design-patterns,architecture,adapter,strategy-pattern,Design Patterns,Architecture,Adapter,Strategy Pattern,这是一个与设计相关的问题。 假设我们有一个名为ClientAPI的公共API,其中包含一些web方法,如CreateAccount、GetAccount。根据客户的不同,我们使用许多不同的供应商来满足这些要求 假设我们有ProviderA、ProviderB和ProviderC ProviderA有一个CreateCount的方法签名/实现,只需要(Firstname、Lastname),并使用ProviderA创建一个帐户 ProviderB具有CreateAccount的方法签名/实现,需要

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

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

假设我们有ProviderA、ProviderB和ProviderC

ProviderA有一个CreateCount的方法签名/实现,只需要(Firstname、Lastname),并使用ProviderA创建一个帐户

ProviderB具有CreateAccount的方法签名/实现,需要(Firstname、Lastname、Email、DOB)并使用ProviderB创建帐户

ProviderC具有CreateCount的方法签名/实现,需要(昵称、公司密钥、电子邮件)并使用ProviderC创建帐户

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

我这里有两个问题。

1) 此模型的最佳设计/模式是什么?还要记住,提供商的数量将增长——我们将增加更多的提供商


2) 关于传递参数–目前ClientAPI CreateCount方法签名是一大行变量,如果新的提供者需要一个新值,则该方法签名会添加另一个变量,这显然打破了旧的实现等。将方法签名中的参数数组/列表/字典传递给下面的提供者是一种好的做法,还是有更好的方法?

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

  • ClientAPI
  • 每个提供程序所需的变量数量和类型
  • 当我设计服务或新功能时,我喜欢通过尽量减少为支持新功能所需的更改次数来对设计进行推理。在您的情况下,将添加新的身份验证提供程序。我现在想到了至少三种不同的实现方法。在我看来,没有完美的解决方案。您必须根据权衡选择其中之一。下面,我尝试介绍几个解决上述两个难点的选项,以及它们的优缺点

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

    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('john.doe@gmail.com'));
    
    AuthProvider authProvider = new AuthProviderFactory().get(userData.getType());
    authProvider.createAccount(userData);
    
    优势
    • 包含强类型属性的
      UserData
      的特殊形式
    • 只需创建新的
      UserData
      类型并添加新条目
      AuthProviderFactory
      即可支持新的提供程序
    • 每个
      AuthProvider
      都确切地知道它需要什么来执行公开的操作(
      createAccount()
      ,等等)。逻辑和复杂性都得到了很好的封装
    缺点
    • AuthProviderFactory
      使用
      instanceof
      选择正确的
      AuthProvider
    • UserData
      子类型的爆炸和潜在的代码重复

    键入
    UserData
    重新访问 我们可以尝试通过在以前的设计中重新引入enum
    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('john.doe@gmail.com'));
    
    AuthProvider authProvider = new AuthProviderFactory().get(userData.getType());
    authProvider.createAccount(userData);
    
    优势
    • 包含强类型属性的
      UserData
      的特殊形式
    • 每个
      AuthProvider
      都确切地知道它需要什么来执行公开的操作(
      createAccount()
      ,等等)。逻辑和复杂性都得到了很好的封装
    缺点
    • 除了向
      AuthProviderFactory
      添加新条目和为
      UserData
      创建新的子类型外,新的提供者还需要在enum
      AuthType
      中添加新条目
    • 我们仍然有大量的
      UserData
      子类型,但是现在这些子类型的可重用性增加了
    总结 我很确定这个问题还有其他几种解决办法。正如我上面提到的,也没有完美的解决方案。你可能必须根据他们的权衡和你想要实现的目标来选择一个


    我今天的灵感不是很好,所以如果我想到其他东西,我会不断更新这篇文章。

    根据您的描述,当客户调用CrateAccount()API时,他还不知道将使用哪个提供程序。因此,如果您想要一个简单的解决方案,您的CreateAccount()API必须需要它最终可能需要的所有信息

    添加需要新参数的新提供程序将始终破坏API:

    • 如果向函数添加新参数,它将在编译时中断(这是检测问题的最简单方法)
    • 如果您使用字典/地图,它将在运行时中断,因为您将丢失所需的信息
    但是,如果您处于面向对象的环境中,则可以使用设计模式:

  • CreateAccount()函数将委托作为单个参数
  • 一旦CreateAccount()知道将使用哪个提供程序,就会调用委托来收集所需的
    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('john.doe@gmail.com'));
    
    AuthProvider authProvider = new AuthProviderFactory().get(userData.getType());
    authProvider.createAccount(userData);