Будет ли это анти-шаблон, если из уровня Presenter я открою Activity
?
Если да, должен ли я управлять навигацией приложения из уровня просмотра?
Будет ли это анти-шаблон, если из уровня Presenter я открою Activity
?
Если да, должен ли я управлять навигацией приложения из уровня просмотра?
Да, это анти-mvp-шаблон. Основанный на пассивном виде в MVP, вы потеряли свою тестируемость, потому что вам не нужно иметь дело с каркасом Android в своем презентаторе.
Так что лучше управлять навигацией приложения с уровня просмотра.
class MyPresenter {
MyPresenter.View view;
void backButtonClicked() {
view.navigateToHomeScreen();
}
public interface View {
void navigateToHomeScreen();
}
}
class MyActivity extends Activity implements MyPresenter.View {
@Override
void navigateToHomeScreen() {
startActivity(...)
}
@OnClick(R.id.my_button)
void onClick() {
presenter.backButtonClicked();
}
}
Другим преимуществом этого способа является то, что будет легко заменить активность фрагментом или представлением.
Изменить 1:
Morgwai сказал, что этот способ нарушит разделение беспокойства и единоличной ответственности, но вы не можете иметь единой ответственности везде. Когда-нибудь вам нужно будет его нарушить. Вот пример из Google для MVP:
TaskDetailPresenter
вызывает ShowEditTask
, который ответственный за открытие нового Activity
внутри TaskDetailFragment
.
Но вы также можете использовать CommandPattern, который является лучшим подходом
interface NavigationCommand {
void navigate();
}
Итак, Presenter будет использовать его, когда это необходимо.
Как я писал в своем комментарии к принятому отвечу, я считаю, что управление навигацией с уровня представления является явным нарушением правила разделения интересов: представления должны содержать ТОЛЬКО методы обновления текущего экрана пользовательского интерфейса.
Проблема возникает из конструкции платформы Android, поскольку классы Activity
и Fragment
содержат оба метода для работы на экране пользовательского интерфейса и для отправки объектов намерения, которые запускают другие действия, такие как startActivity
.
Чистым способом решения этой проблемы было бы создать некоторый интерфейс Navigator
, который будет содержать методы, связанные с навигацией, активировать его и внедрить в презентаторов. Этот способ, по крайней мере, с точки зрения навигации инструкторов и манипулирования пользовательским интерфейсом будет разделен. Однако он может показаться странным с точки зрения деятельности: теперь они часто реализуют оба интерфейса (Navigator и View) и передают ссылку 2 раз ведущему. Если по этой причине вы решите управлять навигацией со своего уровня представления, по крайней мере, сохраните методы навигации отдельно от тех, которые используются для управления пользовательским интерфейсом: никогда не выполняйте навигацию и манипулирование пользовательским интерфейсом одним и тем же методом.
По-моему, было бы лучше, если бы вы открыли активность из уровня просмотра. Я предпочитаю, чтобы ведущий знал о деятельности как можно меньше.
Если есть какое-то условие того, какая деятельность должна быть запущена, вы можете использовать что-то вроде этого:
public class Presenter {
private ViewsPresentation mViewsPresentation;
public void someButtonClicked() {
if (/*some condition*/) {
mViewsPresentation.startFirstActivity();
} else {
mViewsPresentation.startSecondActivity();
}
}
public interface ViewsPresentation {
void startFirstActivity();
void startSecondActivity();
}
}
Я сделал это решение (в Котлине):
Я создал интерфейс под названием ViewNavigator
interface ViewNavigator {
fun navigateTo(target: Class<*>)
}
Затем я заставил интерфейс представления реализовать его.
interface View : ViewNavigator {
//...
}
Тогда фактический вид (действие) может переопределить функцию navigateTo
override fun navigateTo(target: Class<*>) {
startActivity(Intent(this, target))
}
Поэтому, когда я захочу перейти к какому-либо занятию, я могу просто написать это в классе докладчика. Например:
override fun onAnimationFinished() {
view.navigateTo(HomeActivity::class.java)
}