英文:
Java - Getter/Setter, behavior and Interfaces
问题
以下是翻译好的部分:
我有一个问题,有点理论性:
假设我有以下的类:
interface ReportInterface {
void execute();
}
class Report implements ReportInterface {
private final Repository rep;
Report(Repository ref) {
this.rep = ref;
}
public void execute() {
//进行一些逻辑
}
}
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未指定");
//进行一些逻辑
}
public void setRelease(String release) {
this.release = release;
}
}
第二个报告需要额外的参数 release 才能正常工作,但是我的接口在 execute
方法中没有参数,所以我通过一个设置器方法来解决这个问题,代码如下:
ReportWithSetter rep2 = new ReportWithSetter(rep);
rep.setRelease("R1.1");
rep.execute();
所以我不喜欢这个额外的 rep.setRelease
。它看起来奇怪而且不自然 - 这个类的用户可能会感到困惑,例如,如果我在Spring中将这个类定义为单例bean,那么当第二次请求这个bean时,如果有人忘记为第二次请求触发 rep.setRelease
,就会产生潜在的错误。除了将其放入构造函数中(我希望将其作为Spring bean),在处理这种情况时有什么最佳实践呢?
英文:
I have a question, a little bit theoretical:
Assume, I have the following 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;
}
}
The second report needs an additional parameter release to work properly, but my interface is defined without parameters for execute
method, so I work around it with a setter method, so it would look like:
ReportWithSetter rep2 = new ReportWithSetter (rep);
rep.setRelease("R1.1");
rep.execute();
So I don't like this additional rep.setRelease
. I looks weird and artificial - a user of this class may be confused, and for example, if I make the class as a singleton bean in Spring, it is a source of potential error, if it is requested for the second time and somebody forgets to trigger rep.setRelease
for the second time. Besides putting it into constructor (I want to make it a spring bean), what would be the best practice to handling this situation?
答案1
得分: 5
以下是翻译好的部分:
解决方案 #1
void execute(Optional<String> release);
或者
void execute(@Nullable String release);
然后在 Report
类中使用它们,如 execute(Optional.empty())
或 execute(null)
。
解决方案 #2
void execute(String... release);
然后在 Report
类中使用它,如 execute()
,在 ReportWithSetter
类中使用它,如 execute("R1.1")
。
解决方案 #3
在接口中定义两个方法:void execute();
和 void execute(String release);
。然后,在实现时,在不需要的方法中抛出 UnsupportedOperationException
。例如,在 Report
类中,你可以这样做:
public void execute(){
//执行一些逻辑
}
public void execute(String release){
throw new UnsupportedOperationException("请使用重载的方法");
}
你也可以将这两个方法都设置为接口的默认方法,这样你的实现类就不必担心实现不支持的方法。
请根据你认为最易读和易维护的方式选择其中之一。
英文:
Assuming you are allowed to change the interface, here are a few solutions I can think of:
Solution #1
void execute(Optional<String> release);
or
void execute(@Nullable String release);
and then use them for Report
class as execute(Optional.empty())
or execute(null)
.
Solution #2
void execute(String... release);
and then use it for Report
class as execute()
and for ReportWithSetter
class as execute("R1.1")
.
Solution #3
Define both void execute();
and void execute(String release);
in the interface. Then while implementing, throw UnsupportedOperationException
in the method you don't need. For example, in Report
class, you would do:
public void execute(){
//do some logic
}
public void execute(String release){
throw new UnsupportedOperationException("Use the overloaded method");
}
You can also make both these methods as default
in the interface, so your implementation classes don't have to worry about implementing the unsupported method.
<hr/>
Use whichever is most readable and maintainable for you.
答案2
得分: 2
**解决方案1:Spring 依赖注入 - 字段注入:**
Spring 的依赖注入使用反射,因此不需要 Setter 方法。<br>
因此,如果将 Report 类设置为 Spring Bean,并使用 @Autowired 注解来注入另一个 Bean,那么就不需要 Setter 方法。<br>
代码如下:
@Component
class ReportWithRelease implements ReportInterface {
@Autowired private final Repository rep;
@Autowired private Release release;
public void execute(){
if (release == null) throw IlligalArgumentException("未指定 release");
// 执行一些逻辑
}
}
我将 "String release" 更改为 "Release release",因为创建一个 "String" 类型的 Bean 也会很奇怪。所以 "Release" 类必须包含您的 "String release" 内容。
如果 "String release" 只包含一些在运行时不会更改的配置值。那么您可以使用 @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");
// 执行一些逻辑
}
}
英文:
Solution 1: Spring Dependency Injection - Field Injection:
Spring's Dependency Injection works with reflection, so Setter methods are not required.<br>
So if you make your Report class a Spring Bean and use @Autowired to inject another bean, then the Setter method is not required.<br>
It would look like this:
@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
}
}
I changed "String release" to "Release release", because making a bean of "String" would be also strange. So the "Release" class would have to contain your "String release".
If "String release" contains only some configured value, which does not change at runtime. Then you can use @Value to read its String value from a properties file.
Solution 2: Spring Constructor Injection:
Constructor injection is another option, which is even more recommended.
Then your Report bean would look like this:
@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
}
}
答案3
得分: 0
工厂方法模式在您希望创建同一接口的不同类实例时非常有用。
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());
}
}
英文:
Factory method patterns are good if you want to create instances of different classes of same interface.
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());
}
}
答案4
得分: 0
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();
请注意,我只为您翻译了代码部分,其他内容均未翻译。
英文:
Spring of course offers autowiring, but introducing @AutoWire should be done for systematic purposes.
Here you can do with a two-stage execute, a factory.
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();
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论