问题描述
假设,我有以下课程:
interface ReportInterface {
void execute();
}
class Report implements ReportInterface {
private final Repository rep;
Report(Repository ref){
this.rep = ref;
}
public void execute(){
//do some logic
}
}
class ReportWithSetter implements ReportInterface {
private final Repository rep;
private String release;
ReportWithSetter(Repository ref){
rep = ref;
}
public void execute(){
if (release == null) throw IlligalArgumentException("release is not specified");
//do some logic
}
public void setRelease(String release){
this.release=release;
}
}
第二个报告需要一个额外的参数 release 才能正常工作,但是我的界面没有为execute
方法定义参数,因此我使用setter方法来解决它,所以它将看起来像:
ReportWithSetter rep2 = new ReportWithSetter (rep);
rep.setRelease("R1.1");
rep.execute();
所以我不喜欢这个额外的rep.setRelease
。我看起来很奇怪,很人为-此类的用户可能会感到困惑,例如,如果我在Spring中将该类设为单例bean,那么如果第二次请求该类并且有人忘记了,则它是潜在错误的来源再次触发rep.setRelease
。除了将其放入构造函数(我想使其成为Spring bean)之外,处理这种情况的最佳实践是什么?
解决方法
假设您可以更改界面,以下是我可以想到的一些解决方案:
解决方案#1
ID
或
void execute(Optional<String> release);
,然后将它们作为void execute(@Nullable String release);
或Report
用于execute(Optional.empty())
类。
解决方案2
execute(null)
,然后将其用于void execute(String... release);
类的Report
和execute()
类的ReportWithSetter
类。
解决方案3
在界面中同时定义execute("R1.1")
和void execute();
。然后在实施时,将void execute(String release);
放入不需要的方法中。例如,在UnsupportedOperationException
类中,您可以这样做:
Report
您还可以在界面中将这两种方法都设置为 public void execute(){
//do some logic
}
public void execute(String release){
throw new UnsupportedOperationException("Use the overloaded method");
}
,因此您的实现类不必担心实现不受支持的方法。
使用对您而言最易读和可维护的方式。
,解决方案1:弹簧依赖注入-字段注入:
Spring的依赖注入与反射一起使用,因此不需要Setter方法。
因此,如果将Report类设置为Spring Bean并使用@Autowired注入另一个Bean,则不需要Setter方法。
看起来像这样:
@Component
class ReportWithRelease implements ReportInterface {
@Autowired private final Repository rep;
@Autowired private Release release;
public void execute(){
if (release == null) throw IlligalArgumentException("release is not specified");
//do some logic
}
}
我将“ String release”更改为“ Release release”,因为制作“ String” bean也很奇怪。因此,“发行”类将必须包含您的“字符串发行版”。
如果“字符串发布”仅包含一些配置的值,则该值在运行时不会更改。然后,您可以使用@Value从属性文件中读取其String值。
解决方案2:Spring构造函数注入:
构造函数注入是另一种选择,甚至更推荐使用。 然后,您的Report Bean将如下所示:
@Component
class ReportWithRelease implements ReportInterface {
private Repository rep;
private Release release;
@Autowired
public ReportWithRelease(Repository rep,Release release) {
this.rep = rep;
this.release = release;
}
public void execute(){
if (release == null) throw IlligalArgumentException("release is not specified");
//do some logic
}
}
,
如果要创建同一接口的不同类的实例,则工厂方法模式是很好的选择。
class MyFactory {
ReportInterface createInstance(Class clazz,String... args) {
if (Report.class.equals(clazz)) {
return new Report();
}
if (ReportWithSetter.class.equals(clazz)) {
return new ReportWithSetter(args[0]);
}
throw new IllegalArgumentException(clazz.getName());
}
}
,
Spring当然提供了自动装配,但是引入@AutoWire应该是出于系统目的。
您可以在这里执行两阶段执行,即工厂。
class ReportFactory /*ReportWithSetter*/ {
private final Repository rep;
private final String release;
private final ReportInterface report = ...;
ReportFactory (Repository rep,String release) {
this.rep = rep;
this.release = release;
}
public ReportInterface report() {
return report;
}
}
new ReportFactory(rep,release).execute();