Имхо, всё это жуткий отстой.
View должно иметь методы только по показу данных. navigateToHome к ним не относится, это должен быть отдельный объект Router, которого нет в MVP.
View не должно иметь ссылку на Presenter и дергать из него какие-то методы.
Во View не должно быть бизнес-логики.
Presenter должен иметь ссылку на View.
Из View должны торчать наружу события(rx observable или коллбеки, например), на которые подписывается Presenter.
У Presenter'a должно быть два метода для связывания его с View: bind(View), unbind(View).
Fragment или Activity не должны быть ни View, ни Presenter'ом, ни Model. Они - клей, системные механизмы для связывания и поддержания стека экранов. Они каким-либо создают или получают через DI инстансы View и Presenter, и cвязывают их при помощи bind/unbind.
Таким образом, методов validate не будет ни в одном интерфейсе, и ни в одной реализации(если только это не приватные методы реализации, конечно).
Упрощенно как-то так
interface LoginView {
@NonNull Observable<String> names();
@NonNull Observable<String> passwords();
void showError(@NonNull String error);
}
interface LoginPresenter {
void bind(@NonNull LoginView view);
void unbind(@NonNull LoginView view);
}
interface Router {
void navigateToHome();
}
class LoginPresenterImpl implements LoginPresenter {
@Inject
LoginPresenterImpl(router: Router){...}
private CompositeDisposable disposables = new CompositeDisposable();
@Override
void bind(@NonNull LoginView view) {
disposable.add(
Observable.combineLatest(view.names(), view.passwords(), (name, password) -> validate(name, password))
.doOnNext(validated -> {if(!validated) view.showError("invalid login")}
.filter(it -> it)
.subscribe(it -> router.navigateToHome())
);
}
@Override
void unbind(@NonNull LoginView view) {
disposables.clear();
}
}
class Fragment {
@Injecte
LoginPresenter presenter;
@Inject
LoginView view;
onViewCreated() {
presenter.bind(view)
}
onDestroyView() {
presenter.unbind(view);
}
}