Design patterns 对于使用多个提供者的客户端应用程序,要使用什么设计/模式?
这是一个与设计相关的问题。 假设我们有一个名为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) 此模型的最佳设计/模式是什么?还要记住,提供商的数量将增长——我们将增加更多的提供商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的方法签名/实现,需要
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
重新访问
我们可以尝试通过在以前的设计中重新引入enumAuthType
并使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
创建新的子类型外,新的提供者还需要在enumUserData
中添加新条目AuthType
- 我们仍然有大量的
子类型,但是现在这些子类型的可重用性增加了UserData
我今天的灵感不是很好,所以如果我想到其他东西,我会不断更新这篇文章。根据您的描述,当客户调用CrateAccount()API时,他还不知道将使用哪个提供程序。因此,如果您想要一个简单的解决方案,您的CreateAccount()API必须需要它最终可能需要的所有信息 添加需要新参数的新提供程序将始终破坏API:
- 如果向函数添加新参数,它将在编译时中断(这是检测问题的最简单方法)
- 如果您使用字典/地图,它将在运行时中断,因为您将丢失所需的信息
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);