我想知道如何在应用程序内部将Abstract Factory Implementation类用作@Service。 我有想要的逻辑的多个提供程序,并通过构造函数将它们注入。问题是我不确定我的Factory是否应具有构造函数,如果不确定,则应将其构造为私有。
我已将此类注释为@Service,但想知道其中的任何一种是否很好,以及什么是最佳实践。 谢谢,抱歉,如果鞋帮很乱,这实际上是我的第一个问题和帖子。
这是我的代码段:
private final FirstClient first;
private final SecondClient second;
private SentimentFactoryImpl(FirstClient first, SecondCliend second) {
this.first = first;
this.second = second;
}
@Override
public SentimentClient get(String clientName) {
if ("First".equalsIgnoreCase(clientName)) {
return this.first;
} else if ("Second".equalsIgnoreCase(clientName)) {
return this.second;
}
throw new UnknownClientException("Service doesn't provide support for client: " + clientName);
}
答案 0 :(得分:2)
实际上,Spring框架的核心是一种超级灵活且功能强大的抽象工厂。如果您使用的是Spring,则应将所有类创建功能委托给Spring核心。在您的情况下,我更喜欢使用Spring java Configurations
进行类创建。相当于您春季工厂的样子。
@Configuration
public class ServiceConfig {
@Bean
public SentimentClient firstClient() {
return new FirstClient();
}
@Bean
public SentimentClient secondClient() {
return new SecondClient();
}
}
Spring核心识别出此类之后,每当其他Spring bean需要它时,它将用作工厂来获取服务的实例。您不需要像工厂中那样调用任何类型的get
方法。 Spring是自动完成的。
@Service
public class SomeService {
private final SentimentClient firstClient;
@Autowired
private SomeService(SentimentClient firstClient) {
this.firstClient = firstClient;
}
// Some business logic here
}
值得一提的是,Spring配置中的factory方法的名称是由此方法创建的bean(服务)的名称,并且具有类类型的名称用于查找自动装配所需的bean。
答案 1 :(得分:1)
您应考虑以下实践以应用最佳实践:
不要紧密耦合对象字段,而要注入它们。这将允许在需要时更改对象字段类。
要满足第一点,请务必按合同进行操作,即使用接口。
确定每次调用工厂时是单例还是新对象。
抽象工厂的可维护性。
在您的情况下,您已经遵守#2。休息一下
a)为FirstClient
和SecondClient
创建bean。
b)如果这些必须从final关键字看来是单例的,但在工厂类中不是显而易见的,则使两个客户端类都是单例的。
c)而不是使用字符串clientName,而是创建一个枚举,因为它总是可维护且安全,因为您始终在寻找定义明确的值。
d)摆脱if-else,而使用一个映射,该映射填充为工厂的后构造。这样可以加快获取豆子的速度。
e)您还可以想到延迟初始化:)。
class SentimentFactoryImpl{
private Map<Client, SentimentClient> factoryMap;
@Autowired
private final FirstClient first;
@Autowired
private final SecondClient second;
@Override
public SentimentClient get(String clientName) {
Client Client.getByName(clientName);
if(!factoryMap.contains(client)){
throw new UnknownClientException("Service doesn't provide support for client: "
+ clientName);
}
return factoryMap.get(Clients.getByName(clientName));
}
答案 2 :(得分:0)
由于您说此类被注释为@Service
,所以我假设您希望两个客户端都自动连接到该客户端,并且它们也都是bean。
在这种情况下,您可以在构造函数中对其进行自动装配(在这种情况下,构造函数应该是公共的)。另外,您也可以不使用构造函数,而直接(或通过setter方法)自动连接字段-不建议这样做,更好的做法是使用构造函数自动连接。
答案 3 :(得分:0)
我知道您已经接受了答案,但我只想发表自己的看法。第一件事是我不想使用Abstract Factory模式,似乎您不是在创建对象,而只是想根据它们的名称来定位客户端对象。这就是Locator的工作,我们称它为ClientLocator(由于缺少更好的名称)。
这个想法是创建一个名为ClientLocator
的类来实现ApplicationContextAware
。通过实现此接口,Spring将通过setter方法注入ApplicationContext
对象。使用ApplicationContext
对象,我们可以按其名称查找bean,然后检查bean的类型以确保获得正确的bean,否则返回错误。
这是代码的外观。
客户
// SentimentClient.java
public interface SentimentClient {
void call();
}
// FirstClient.java
@Service
public class FirstClient implements SentimentClient {
@Override
public void call() {
System.out.println("Hello from the first client");
}
}
// SecondClient.java
@Service
public class SecondClient implements SentimentClient {
@Override
public void call() {
System.out.println("Hello from the second client");
}
}
ClientLocator.java
@Component
public class ClientLocator implements ApplicationContextAware {
private ApplicationContext ctx;
public SentimentClient get(String name) {
return ctx.getBean(name + "Client", SentimentClient.class);
}
@Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {
this.ctx = ctx;
}
}
这是我们使用定位器的方式。
ClientLocatorDemo.java
@Component
public class ClientLocatorDemo implements CommandLineRunner {
@Autowired
private ClientLocator clientLocator;
@Override
public void run(String... args) {
SentimentClient firstClient = clientLocator.get("first");
SentimentClient secondClient = clientLocator.get("second");
firstClient.call();
secondClient.call();
// output:
// Hello from the first client
// Hello from the second client
}
}
通过这种方式,只要代码想要以其名称获取客户端,只需自动连接ClientLocator
并调用get(String name)
方法即可。
最重要的是,您可以根据需要创建任意数量的客户端实现,并按其名称获取它们(假定每个名称都有唯一的名称,并以“ Client”结尾),而不必一个接一个地自动接线。