【问题标题】:How to delegate to services by class type?如何按类类型委托给服务?
【发布时间】:2016-06-21 20:25:36
【问题描述】:

我有不同的类类型,并且根据某些条件,我想委托给可以处理这些类类型的适当service

示例: 我有几个类如下。

class Student;
class Prof;
...

每个类都有一个服务,实现:

interface IPersonService {
    void run();
}

我有一个mode,它是通过某些条件找到的:

enum PersonType {
    STUDENT, PROF;
}

当我委托时:

@Autowired
private StudentService studentService;

@Autowired
private ProfService profService;

//@param mode assume known
public void delegate(PersonType mode) {

    //assume there are several of those switch statements in my business code
    switch (mode) {
        case STUDENT: studentService.run(); break;
        case PROF: profService.run(); break;
        default: break;
    }
}

问题:当引入额外的类时,我必须同时修改PersonType并添加一个额外的枚举(这没问题),但我还必须扩展任何switch语句和添加对其他委托服务的调用。此外,我必须将这些服务显式自动连接到 switch 委托人。

问题:我如何优化这段代码,只为任何额外的类实现新的服务,而不必触及任何 switch 语句?

【问题讨论】:

  • 您可以在枚举本身中保留一个映射以将 PersonType 与服务相关联,并且在委托中只需根据 PersonType 获取服务并调用它。
  • 但是我不能将服务注入枚举,可以吗?
  • 问题的根源在于服务逻辑与其服务的数据是分离的。分离数据和逻辑称为Procedural Programming。在Object Oriented Programming 中,服务逻辑与其服务的数据相结合,因此无需“委托”。
  • @jaco0646 您将如何重写或改进给出的示例?
  • 我不会对重写代码感兴趣。我想指出,目前的答案是针对程序问题的程序解决方案,而根据我的经验,java 设计模式始终是面向对象的。

标签: java design-patterns


【解决方案1】:

IPersonService添加一个方法,这样方法的实现就可以告诉程序它处理的是什么类型的人:

interface IPersonService {
    PersonType supportedPersonType();
    void run();
}

在执行委托的服务中,注入一个List<IPersonService>,Spring 将填充它可以找到的所有IPersonService 的实现。然后实现delegate方法,通过列表查找第一个可以处理特定类型的IPersonService

@Autowired
private List<IPersonService> personServices;

public void delegate(PersonType mode) {
    for (IPersonService personService : personServices) {
        if (personService.supportedPersonType().equals(mode)) {
            personService.run();
            break;
        }
    }
}

这样,您可以添加 IPersonService 的新实现,而无需更改执行委托的服务。

为避免每次调用delegate 时都要经过循环,您可以预先构建一个Map,以便快速查找正确的IPersonService

class DelegatingService {
    @Autowired
    private List<IPersonService> personServices;

    private Map<PersonType, IPersonService> personServiceMap;

    @PostConstruct
    public void init() {
        personServiceMap = new HashMap<>();
        for (IPersonService personService : personServices) {
            personServiceMap.put(personService.supportedPersonType(), personService);
        }
    }

    public void delegate(PersonType mode) {
        personServiceMap.get(mode).run();
    }
}

(为简单起见省略了错误处理)。

【讨论】:

  • 这是一个很好的解决方案,可能是我的问题的正确解决方案!
【解决方案2】:

在我的应用程序中,我们通过将服务放入地图中解决了类似的问题。考虑将 Map&lt;PersonType,IPersonService&gt; serviceMap 定义为 bean 并注入到您的类中。 然后委托方法简单做

public void delegate(PersonType mode) {
    IPersonService service = serviceMap.get(mode);
    if (service!=null){
        service.run();
    }else{
        //do something if service is null
    }
}

【讨论】:

    【解决方案3】:

    您可以将服务 bean 名称(或类类型)存储在枚举中,并使用 getBean 按名称(或分别按类类型)从应用程序上下文中获取 bean。

    此外,所有服务都需要实现一个具有 run 方法的接口。

    interface ModeService {
        void run();
        }
    
        enum PersonType {
            STUDENT("studentService"), PROF("profService");
            private String serviceBean;
            public PersonType(String serviceBean) {
                  this.serviceBean = serviceBean);
            }
            public String getServiceBean() {
                   return serviceBean;
            }
        }
    

    在委托中可以使用以下内容。 ((ModeService)applicationContext.getBean(mode.getServiceBean()).run()

    这种方式只需要使用要使用的服务类型更新枚举,不需要更改委托方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-03-05
      • 1970-01-01
      • 1970-01-01
      • 2018-05-07
      • 2013-01-06
      • 1970-01-01
      • 2015-10-25
      • 2011-04-09
      相关资源
      最近更新 更多