我喜欢构建器模式(例如https://stackoverflow.com/a/1953567),原因有很多,例如: G。事实上,我可以使用不可变对象,并且我可以控制对象创建,而不是无法创建无效对象。
但是,我尝试遵循范式“程序到接口,而不是实现”(例如https://stackoverflow.com/a/2697810)。
我认为,这两个指导方针不能很好地协同作用。
如果我有一个界面Person
和一个类PersonImpl
以及一个构建PersonImplBuilder
的构建器PersonImpl
。我现在可以确保PersonImpl
的每个实例都是有效且不可变的。但是我的API中的每个返回值,尤其是每个方法参数都使用该接口。所以我不能依赖有效的对象。
我是否遗漏了某些东西是否存在将这两个非常有用的指南结合起来的另一种方式?
修改
一些代码要澄清。
在此示例中,构建器在确保API中对象的有效性和/或不可变性方面是无用的。它仅保证PersonImpl
的任何对象都是有效的(并且通过仅因为PersonImpl
被声明为 final 而有效的方式)。但我无法控制客户端是否实际使用我安全构造的PersonImpl
对象或Person
接口的任何其他实现。
public interface Person {
LocalDate getBirthday();
}
public final class PersonImpl implements Person {
private final LocalDate birthday;
private PersonImpl(PersonImplBuilder builder) {
this.birthday = builder.birthday;
}
@Override
public LocalDate getBirthday() {
return birthday;
}
}
public class PersonImplBuilder {
private LocalDate birthday;
public LocalDate getBirthday() {
return birthday;
}
public void setBirthday(LocalDate birthday) {
this.birthday = birthday;
}
public PersonImpl build() {
if(birthday.isAfter(LocalDate.now().minusYears(21).minusDays(1))) {
throw new IllegalStateException("Person must be 21 years or above");
}
return new PersonImpl(this);
}
}
// this is my API
public interface PersonService {
void doSomeAdultStuff(Person person);
}
public class PersonServiceImpl implements PersonService {
//...
}
public void maliciousMethod() {
PersonService service = new PersonServiceImpl();
service.doSomeAdultStuff(new Person() {
@Override
public LocalDate getBirthday() {
return LocalDate.now();
}
});
}
答案 0 :(得分:0)
您不能在代码中的任何位置使用构建器。如果您不希望代码使用Person类的不同实现,而只是使用PersonImpl,那么不要使用接口,而是使用具体的实现。这样,您将确保只有对象以您想要的方式构建。
你应该考虑一个人可以不到21岁,仍然是一个有效的"人(例如孩子)。您可以拥有成人构建器和子构建器(不同的实现),但您仍需要检查是否有正确的实现。因此,如果此人具有正确的年龄而非在构建对象期间,则可能应该检查服务。否则它应该被称为成人而不是人;)
答案 1 :(得分:0)
“程序到界面”概念和Builder模式的组合应该没有问题。原因在于您的以下代码:
render()
您编写了一个没有名称的新类,并实现了Person接口(匿名类)。此类与代码的PersonImpl类不同。在您的情况下,只需删除匿名类实现并使用新的PersonImpl(构建器)。
service.doSomeAdultStuff(new Person() {
@Override
public LocalDate getBirthday() {
return LocalDate.now();
}
});