[英]How can i implement Factory pattern without using if else or switch case in spring using annotations
[英]Implement a simple factory pattern with Spring 3 annotations
我想知道如何使用 Spring 3 注释实现简单的工厂模式。 我在文档中看到您可以创建调用工厂类并运行工厂方法的 bean。 我想知道这是否可能仅使用注释。
我有一个当前调用的控制器
MyService myService = myServiceFactory.getMyService(test);
result = myService.checkStatus();
MyService 是一个接口,其中一个方法称为 checkStatus()。
我的工厂类如下所示:
@Component
public class MyServiceFactory {
public static MyService getMyService(String service) {
MyService myService;
service = service.toLowerCase();
if (service.equals("one")) {
myService = new MyServiceOne();
} else if (service.equals("two")) {
myService = new MyServiceTwo();
} else if (service.equals("three")) {
myService = new MyServiceThree();
} else {
myService = new MyServiceDefault();
}
return myService;
}
}
MyServiceOne 类如下所示:
@Autowired
private LocationService locationService;
public boolean checkStatus() {
//do stuff
}
当我运行此代码时,locationService 变量始终为空。 我相信这是因为我自己在工厂内创建对象并且没有进行自动装配。 有没有办法添加注释以使其正常工作?
谢谢
以下对我有用:
该接口由您的逻辑方法和附加标识方法组成:
public interface MyService {
String getType();
void checkStatus();
}
一些实现:
@Component
public class MyServiceOne implements MyService {
@Override
public String getType() {
return "one";
}
@Override
public void checkStatus() {
// Your code
}
}
@Component
public class MyServiceTwo implements MyService {
@Override
public String getType() {
return "two";
}
@Override
public void checkStatus() {
// Your code
}
}
@Component
public class MyServiceThree implements MyService {
@Override
public String getType() {
return "three";
}
@Override
public void checkStatus() {
// Your code
}
}
工厂本身如下:
@Service
public class MyServiceFactory {
@Autowired
private List<MyService> services;
private static final Map<String, MyService> myServiceCache = new HashMap<>();
@PostConstruct
public void initMyServiceCache() {
for(MyService service : services) {
myServiceCache.put(service.getType(), service);
}
}
public static MyService getService(String type) {
MyService service = myServiceCache.get(type);
if(service == null) throw new RuntimeException("Unknown service type: " + type);
return service;
}
}
我发现这样的实现更容易、更简洁、更可扩展。 添加新的 MyService 就像创建另一个实现相同接口的 spring bean 一样简单,而无需在其他地方进行任何更改。
你是对的,通过手动创建对象,你不会让 Spring 执行自动装配。 考虑通过 Spring 管理您的服务:
@Component
public class MyServiceFactory {
@Autowired
private MyServiceOne myServiceOne;
@Autowired
private MyServiceTwo myServiceTwo;
@Autowired
private MyServiceThree myServiceThree;
@Autowired
private MyServiceDefault myServiceDefault;
public static MyService getMyService(String service) {
service = service.toLowerCase();
if (service.equals("one")) {
return myServiceOne;
} else if (service.equals("two")) {
return myServiceTwo;
} else if (service.equals("three")) {
return myServiceThree;
} else {
return myServiceDefault;
}
}
}
但我认为整体设计相当糟糕。 拥有一个通用的MyService
实现并将one
/ two
/ three
字符串作为额外参数传递给checkStatus()
不是更好吗? 你想达到什么目标?
@Component
public class MyServiceAdapter implements MyService {
@Autowired
private MyServiceOne myServiceOne;
@Autowired
private MyServiceTwo myServiceTwo;
@Autowired
private MyServiceThree myServiceThree;
@Autowired
private MyServiceDefault myServiceDefault;
public boolean checkStatus(String service) {
service = service.toLowerCase();
if (service.equals("one")) {
return myServiceOne.checkStatus();
} else if (service.equals("two")) {
return myServiceTwo.checkStatus();
} else if (service.equals("three")) {
return myServiceThree.checkStatus();
} else {
return myServiceDefault.checkStatus();
}
}
}
这仍然设计得很糟糕,因为添加新的MyService
实现也需要修改MyServiceAdapter
(违反 SRP)。 但这实际上是一个很好的起点(提示:地图和策略模式)。
跟随DruidKuma的回答
使用自动装配的构造函数对他的工厂进行小重构:
@Service
public class MyServiceFactory {
private static final Map<String, MyService> myServiceCache = new HashMap<>();
@Autowired
private MyServiceFactory(List<MyService> services) {
for(MyService service : services) {
myServiceCache.put(service.getType(), service);
}
}
public static MyService getService(String type) {
MyService service = myServiceCache.get(type);
if(service == null) throw new RuntimeException("Unknown service type: " + type);
return service;
}
}
您可以手动要求 Spring 自动装配它。
让您的工厂实现 ApplicationContextAware。 然后在您的工厂中提供以下实现:
@Override
public void setApplicationContext(final ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
然后在创建 bean 后执行以下操作:
YourBean bean = new YourBean();
applicationContext.getAutowireCapableBeanFactory().autowireBean(bean);
bean.init(); //If it has an init() method.
这将完美地自动连接您的 LocationService。
为什么不将接口 FactoryBean 添加到 MyServiceFactory (告诉 Spring 它是一个工厂),然后添加一个寄存器(字符串服务,MyService 实例),然后让每个服务调用:
@Autowired
MyServiceFactory serviceFactory;
@PostConstruct
public void postConstruct() {
serviceFactory.register(myName, this);
}
这样,您可以在必要时将每个服务提供者分成模块,Spring 将自动选择任何已部署和可用的服务提供者。
您还可以以声明方式定义一个ServiceLocatorFactoryBean类型的 bean,它将充当工厂类。 它受 Spring 3 支持。
一个 FactoryBean 实现,它采用一个接口,该接口必须具有一个或多个带有签名的方法(通常是 MyService getService() 或 MyService getService(String id)),并创建一个实现该接口的动态代理
基于 Pavel Černý的解决方案,我们可以对这种模式进行通用类型化实现。 为此,我们需要引入 NamedService 接口:
public interface NamedService {
String name();
}
并添加抽象类:
public abstract class AbstractFactory<T extends NamedService> {
private final Map<String, T> map;
protected AbstractFactory(List<T> list) {
this.map = list
.stream()
.collect(Collectors.toMap(NamedService::name, Function.identity()));
}
/**
* Factory method for getting an appropriate implementation of a service
* @param name name of service impl.
* @return concrete service impl.
*/
public T getInstance(@NonNull final String name) {
T t = map.get(name);
if(t == null)
throw new RuntimeException("Unknown service name: " + name);
return t;
}
}
然后我们创建一个具体对象的具体工厂,例如 MyService:
public interface MyService extends NamedService {
String name();
void doJob();
}
@Component
public class MyServiceFactory extends AbstractFactory<MyService> {
@Autowired
protected MyServiceFactory(List<MyService> list) {
super(list);
}
}
where 列出编译时 MyService 接口的实现列表。
如果您在应用程序中有多个按名称生成对象的类似工厂(如果按名称生成对象当然足以满足您的业务逻辑),则此方法可以正常工作。 这里的 map 可以很好地使用 String 作为键,并保存您服务的所有现有实现。
如果你有不同的逻辑来生成对象,这个额外的逻辑可以移动到另一个地方并与这些工厂(通过名称获取对象)结合使用。
您可以通过将所有服务类作为参数传递来实例化“AnnotationConfigApplicationContext”。
@Component
public class MyServiceFactory {
private ApplicationContext applicationContext;
public MyServiceFactory() {
applicationContext = new AnnotationConfigApplicationContext(
MyServiceOne.class,
MyServiceTwo.class,
MyServiceThree.class,
MyServiceDefault.class,
LocationService.class
);
/* I have added LocationService.class because this component is also autowired */
}
public MyService getMyService(String service) {
if ("one".equalsIgnoreCase(service)) {
return applicationContext.getBean(MyServiceOne.class);
}
if ("two".equalsIgnoreCase(service)) {
return applicationContext.getBean(MyServiceTwo.class);
}
if ("three".equalsIgnoreCase(service)) {
return applicationContext.getBean(MyServiceThree.class);
}
return applicationContext.getBean(MyServiceDefault.class);
}
}
我想你使用 org.springframework.beans.factory.config.ServiceLocatorFactoryBean。 这将大大简化您的代码。 除了 MyServiceAdapter 之外,您只能使用 MyService getMyService 方法和别名创建接口 MyServiceAdapter 来注册您的类
代码
bean id="printStrategyFactory" class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean">
<property name="YourInterface" value="factory.MyServiceAdapter" />
</bean>
<alias name="myServiceOne" alias="one" />
<alias name="myServiceTwo" alias="two" />
我最近处理过类似的需求,我想使用工厂模式,但我对 if else 逻辑不满意,这种逻辑在未来会继续增长,违反了单一责任原则。
第一步,创建一个接口并拥有一个 getType() 方法,在给定的上下文中它将返回“一”、“二”等,否则它可以是任何东西。 这是上面大多数人建议的常见解决方案。
public interface MyService {
String getType();
void checkStatus();
}
一些实现:
@Component
public class MyServiceOne implements MyService {
@Override
public String getType() {
return "one";
}
@Override
public void checkStatus() {
// Your code
}
}
@Component
public class MyServiceTwo implements MyService {
@Override
public String getType() {
return "two";
}
@Override
public void checkStatus() {
// Your code
}
}
@Component
public class MyServiceThree implements MyService {
@Override
public String getType() {
return "three";
}
@Override
public void checkStatus() {
// Your code
}
}
工厂本身如下:
@Service
public class MyServiceFactory {
@Autowired
private List<MyService> services;
public static MyService getService(final String type) {
return services
.stream().filter(service -> type.equals(service.getType()))
.findFirst()
.orElseThrow(throw new RuntimeException("Unknown service type: " + type));
}
}
此解决方案不需要额外的 Map 来针对类型存储实例的键值。 该解决方案无需任何进一步的代码更改即可扩展,因为工厂具有 List 自动布线,因此 MyService 的任何未来实现都将很容易工作。 因此,单一职责原则也得到了保证。
我在使用 Java 8 时使用了流()和谓词,因为早期版本简单的 for 循环就可以完成这项工作。
尝试这个:
public interface MyService {
//Code
}
@Component("One")
public class MyServiceOne implements MyService {
//Code
}
@Component("Two")
public class MyServiceTwo implements MyService {
//Code
}
我想知道在第三篇文章的续篇(DruidKuma发表)中,将从何处创建或注入“服务”对象? -是从MyServiceFactory创建或注入的地方?
这里需要创建/注入“服务”对象,并且需要将所有其他服务对象添加到列表中,这一步需要从哪里注入工厂对象?
@Service
public class MyServiceFactory {
@Autowired
private List<MyService> services;
遵循DruidKuma和jumping_monkey的回答
您还可以包含可选的,并使您的代码更漂亮和更清洁:
public static MyService getService(String type) {
return Optional.ofNullable(myServiceCache.get(type))
.orElseThrow(() -> new RuntimeException("Unknown service type: " + type));
}
这是创建新实例的上述答案的变体。
如果Service
仅依赖于Spring
托管 bean。
public interface MyService {
//Code
}
@Component("One")
@Scope("prototype")
public class MyServiceOne implements MyService {
//Code
public MyServiceOne(Dependency dep){
...
}
}
@Component("Two")
@Scope("prototype")
public class MyServiceTwo implements MyService {
//Code
}
public class Factory {
Map<String,MyService> services;
ApplicationContext context;
Dependency dep;
public Factory(Map<String, MyService> components, ApplicationContext context, Dependency dep) {
...
}
MyService service(String type){
return context.getBean(services.get(type).getClass());
}
}
@Configuration
public class Config {
@Bean
Factory languageFactory(Map<String,Service> map, ApplicationContext context, Dependency dep){
return new Factory(map,context,dep);
}
}
如果您想在工厂方法中包含并非全部由Spring
管理的自定义参数,您可以尝试以下草图之一
@Component("One")
@Scope("prototype")
public class MyServiceOne implements MyService {
//Code
public MyServiceOne(){
...
}
public MyServiceOne(Dependency dep){
...
}
public MyServiceOne(Dependency dep, Integer myFactoryValue){
...
}
}
\\ no longer available in autoscan
public class MyServiceOne implements MyService {
//Code
public MyServiceOne(){
...
}
public MyServiceOne(Dependency dep, Integer myFactoryValue){
...
}
}
@Configuration
public class Config {
@Bean("One")
@Scope("prototype")
Service serviceOne(){
// used only for dynamic discovery
return new ServiceOne();
}
...
@Bean
Factory languageFactory(Map<String,Service> map, ApplicationContext context, Dependency dep){
return new Factory(map,context,dep);
}
}
这两种解决方案都允许您像这样定义工厂方法
public class Factory {
....
MyService service(String type, Integer someParameter){
// you provide the parameters for the constructor
return context.getBean(services.get(type).getClass(),dep,someParameter);
}
}
如果没有明确的路径来确定它应该在构建时使用哪一个,Spring 不会自动装配 bean。 由于工厂没有改变,您可以在那里自动连接您的 LocationService 并将其传递给您的不同服务。 如果您的类有多个依赖项,例如服务、存储库等,这可能会有点麻烦。
如果您不打算对“MyService”类有很多依赖项,您可以这样做:
@Component
public class MyServiceFactory(){
@Autowired
LocationService locationService;
public static MyService getMyService(String service){
service = service.toLowerCase();
switch(service){
case "one":
return new MyServiceOne(locationService);
case "two":
return new MyServiceTwo(locationService);
case "three":
return new MyServiceThree(locationService);
default:
return new MyServiceDefault(locationService);
}
}
}
您的 MyServiceOne 课程:
@Service
public class MyServiceOne implements MyService{
public LocationService locationService;
public MyServiceOne(LocationService service){
locationService = service;
}
@Override
public checkStatus(){
// your code
}
}
我的服务接口
interface MyService{
boolean checkStatus();
}
public interface MyService {
public void save();
//Code
}
@Component("One")
public class MyServiceOne implements MyService {
//Code
public void save(){
System.out.println("one");
}
}
@Component("Two")
public class MyServiceTwo implements MyService {
//Code
public void save(){
System.out.println("two");
}
}
public class FatoryClass{
@Autowired
//@Qualifier("One") //This is your default child class use qualifier or just my sample
MyService One;
public MyService setMyservice(int typeId){
switch(typeId){
case 1:
One = new MyServiceTwo();
break;
default:
System.out.println("Default child instance");
}
return One;
}
}
@Service
public class serviceComponent{
@Autowired
FatoryClass facto;
public void setFactoryMethod(int typeId){
facto.setMyService(typeId);
facto.save();
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.