Мне нравится шаблон компоновщика (пример https://stackoverflow.com/a/1953567) по разным причинам, например, из-за того, что я могу работать с неизменяемыми объектами и что я могу контролировать создание объектов так, как нельзя создавать недопустимые объекты.
Однако я стараюсь следовать парадигме «программа для интерфейсов, а не для реализаций» (пример https://stackoverflow.com/a/2697810).
. Я понял, что эти два руководства не очень хорошо сочетаются друг с другом.
Если у меня есть интерфейс Person
, класс PersonImpl
и сборщик PersonImplBuilder
, который создает PersonImpl
. Теперь я могу заверить, что каждый экземпляр PersonImpl
является действительным и неизменным. Но каждое возвращаемое значение и особенно каждыйПараметр метода в моем API использует интерфейс. Поэтому я не могу зависеть от действительного объекта.
Я что-то упустил, соответственно, есть ли другой способ объединить эти два очень полезных руководства?
РЕДАКТИРОВАТЬ
Некоторый код для пояснения. В этом примере построитель бесполезен с точки зрения обеспечения достоверности и / или неизменности объекта в моей точке доступаЯ. only гарантирует, что любой объект 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();
}
});
}