Java - Getter/Setter,行为和接口
Java - Getter/Setter, behavior and Interfaces
我有一个问题,有点理论化:
假设,我有以下 classes :
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
。我看起来很奇怪而且做作 - 这个 class 的用户可能会感到困惑,例如,如果我将 class 作为 Spring 中的单例 bean,它是潜在错误的来源, 如果第二次请求并且有人忘记第二次触发 rep.setRelease
。除了将其放入构造函数(我想将其设为 spring bean)之外,处理这种情况的最佳做法是什么?
解决方案 1:Spring 依赖注入 - 字段注入:
Spring 的依赖注入与反射一起工作,因此不需要 Setter 方法。
因此,如果您将 Report class 设为 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也会很奇怪。所以“发布”class 必须包含您的“字符串发布”。
如果“String release”只包含一些配置值,在运行时不会改变。然后您可以使用@Value 从属性文件中读取它的字符串值。
解决方案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
}
}
假设你可以更改界面,这里有一些我能想到的解决方案:
解决方案 #1
void execute(Optional<String> release);
或
void execute(@Nullable String release);
然后将它们用于 Report
class 作为 execute(Optional.empty())
或 execute(null)
.
解决方案 #2
void execute(String... release);
然后将它用于 Report
class 作为 execute()
和用于 ReportWithSetter
class 作为 execute("R1.1")
.
解决方案#3
在界面中同时定义void execute();
和void execute(String release);
。然后在实现的时候,在不需要的方法中抛出UnsupportedOperationException
。例如,在 Report
class 中,您可以这样做:
public void execute(){
//do some logic
}
public void execute(String release){
throw new UnsupportedOperationException("Use the overloaded method");
}
您也可以在界面中将这两种方法都设为 default
,这样您的实现 class 就不必担心实现不受支持的方法。
使用最适合您的可读性和可维护性。
如果您想创建同一接口的不同 类 实例,工厂方法模式很好。
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 应该是出于系统目的。
在这里你可以用一个two-stage执行,一个工厂。
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();
我有一个问题,有点理论化:
假设,我有以下 classes :
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
。我看起来很奇怪而且做作 - 这个 class 的用户可能会感到困惑,例如,如果我将 class 作为 Spring 中的单例 bean,它是潜在错误的来源, 如果第二次请求并且有人忘记第二次触发 rep.setRelease
。除了将其放入构造函数(我想将其设为 spring bean)之外,处理这种情况的最佳做法是什么?
解决方案 1:Spring 依赖注入 - 字段注入:
Spring 的依赖注入与反射一起工作,因此不需要 Setter 方法。
因此,如果您将 Report class 设为 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也会很奇怪。所以“发布”class 必须包含您的“字符串发布”。
如果“String release”只包含一些配置值,在运行时不会改变。然后您可以使用@Value 从属性文件中读取它的字符串值。
解决方案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
}
}
假设你可以更改界面,这里有一些我能想到的解决方案:
解决方案 #1
void execute(Optional<String> release);
或
void execute(@Nullable String release);
然后将它们用于 Report
class 作为 execute(Optional.empty())
或 execute(null)
.
解决方案 #2
void execute(String... release);
然后将它用于 Report
class 作为 execute()
和用于 ReportWithSetter
class 作为 execute("R1.1")
.
解决方案#3
在界面中同时定义void execute();
和void execute(String release);
。然后在实现的时候,在不需要的方法中抛出UnsupportedOperationException
。例如,在 Report
class 中,您可以这样做:
public void execute(){
//do some logic
}
public void execute(String release){
throw new UnsupportedOperationException("Use the overloaded method");
}
您也可以在界面中将这两种方法都设为 default
,这样您的实现 class 就不必担心实现不受支持的方法。
使用最适合您的可读性和可维护性。
如果您想创建同一接口的不同 类 实例,工厂方法模式很好。
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 应该是出于系统目的。
在这里你可以用一个two-stage执行,一个工厂。
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();