RESS — Новая архитектура для мобильных приложений



Вопреки провокационному заголовку, это никакая не новая архитектура, а попытка перевода простых и проверенных временем практик на новояз, на котором говорит современное Android-комьюнити

Введение


В последнее время стало больно смотреть на то, что творится в мире разработки под мобильные платформы. Архитектурная астронавтика процветает, каждый хипстер считает своим долгом придумать новую архитектуру, а для решения простой задачи вместо двух строчек вставить несколько модных фреймворков.

Айтишные сайты заполонили туториалы по модным фреймворкам и переусложненным архитектурам, но при этом даже нет best practice для REST-клиентов под Android. Хотя это один из самых частых кейсов приложений. Хочется чтобы нормальный подход к разработке тоже пошел в массы. Поэтому и пишу эту статью

Чем плохи существующие решения


По большому счету проблема новомодных MVP, VIPER и им подобных — ровно одна, их авторы не умеют проектировать. А их последователи — тем более. И поэтому не понимают важных и очевидных вещей. И занимаются обычным оверинжинирингом.

1. Архитектура должна быть простой


Чем проще, тем лучше. Тем проще для понимания, надежнее и гибче. Переусложнить и наделать кучу абстракций может любой дурак, а чтобы сделать просто — нужно хорошенько подумать.

2. Оверинжиниринг это плохо


Добавлять новый уровень абстракции нужно только когда старый уже не решает проблем. После добавления нового уровня система должна стать проще для понимания, а кода меньше. Если, например, после этого у вас вместо одного файла, стало три, а система стала более запутанной, то вы сделали ошибку, а если по-простому — написали херню.

Фанаты MVP, например, сами в своих статьях пишут открытым текстом что MVP тупо приводит к значительному усложнению системы. И оправдывают это тем что так гибче и поддерживать проще. Но, как мы знаем из пункта номер 1, это взаимоисключающие вещи.

Теперь про VIPER, просто посмотрите, например, на схему из этой статьи.

Схема
image

И это для каждого экрана! Моим глазам больно. Особенно сочувствую тем, кому на работе с этим приходится сталкиваться не по своей воле. Тем же, кто это сам внедрил, сочувствую по немного другим причинам.

Новый подход


Эй, я тоже хочу модное название. Поэтому предлагаемая архитектура называется RESSRequest, Event, Screen, Storage. Буковки и названия подробраны так тупо для того чтобы получилось читаемое слово. Ну и чтобы не создавать путаницу с уже используемыми названиями. Ну и с REST созвучно.

Сразу оговорюсь, эта архитектура для REST-клиентов. Для других типов приложений она, вероятно, не подойдет.



1. Storage


Хранилище данных (в других терминах Model, Repository). Этот класс хранит данные и занимается их обработкой(сохраняет, загружает, складывает в БД и т.п.), а так же все данные от REST-сервиса сначала попадают сюда, парсятся и сохраняются здесь.

2. Screen


Экран приложения, в случае Android это ваше Activity. В других терминах это обычный ViewController как в MVC от Apple.

3. Request


Класс, который отвечает за посылку запросов к REST-сервису, а так же прием ответов и уведомление об ответе остальных компонентов системы.

4. Event


Связующее звено между остальными компонентами. Например, Request посылает эвент об ответе сервера, тем кто на него подписался. А Storage посылает эвент об изменении данных.

Далее пример упрощенной реализации. Код написан с допущениями и не проверялся, поэтому могут быть синтаксические ошибки и опечатки

Request
public class Request
{
	public interface RequestListener
	{
		default void onApiMethod1(Json answer) {}
		default void onApiMethod2(Json answer) {}
	}

	private static class RequestTask extends AsyncTask<Void, Void, String>
	{
		public RequestTask(String methodName)
		{
			this.methodName = methodName;
		}

		private String methodName;

		@Override
		protected String doInBackground(Void ... params)
		{
			URL url = new URL(Request.serverUrl + "/" + methodName);
			HttpURLConnection httpConnection = (HttpURLConnection)url.openConnection();

			// ...
			// Делаем запрос и читаем ответ
			// ...

			return result;
		}

		@Override
		protected void onPostExecute(String result)
		{
			// ...
			// Парсим JSON из result
			// ...

			Requestr.onHandleAnswer(methodName, json);
		}
	}

	private static String serverUrl = "myserver.com";
	private static List<OnCompleteListener> listeners = new ArrayList<>();

	private static void onHandleAnswer(String methodName, Json json)
	{
		for(RequestListener listener : listeners)
		{
			if(methodName.equals("api/method1")) listener.onApiMethod1(json);
			else if(methodName.equals("api/method2")) listener.onApiMethod2(json);
		}
	}

	private static void makeRequest(String methodName)
	{
		new RequestTask(methodName).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
	}

	public static void registerListener(RequestListener listener)
	{
		listeners.add(listener);
	}

	public static void unregisterListener(RequestListener listener)
	{
		listeners.remove(listener);
	}

	public static void apiMethod1()
	{
		makeRequest("api/method1");
	}

	public static void onApiMethod2()
	{
		makeRequest("api/method2");
	}
}


Storage
public class DataStorage
{
	public interface DataListener
	{
		default void onData1Changed() {}
		default void onData2Changed() {}
	}

	private static MyObject1 myObject1 = null;
	private static List<MyObject2> myObjects2 = new ArrayList<>();

	public static void registerListener(DataListener listener)
	{
		listeners.add(listener);
	}

	public static void unregisterListener(DataListener listener)
	{
		listeners.remove(listener);
	}

	public static User getMyObject1()
	{
		return myObject1;
	}

	public static List<MyObject2> getMyObjects2()
	{
		return myObjects2;
	}

	public static Request.RequestListener listener = new Request.RequestListener()
	{
		private T fromJson<T>(Json answer)
		{
			// ...
			// Парсим или десереализуем JSON
			// ...

			return objectT;
		}

		@Override
		public void onApiMethod1(Json answer)
		{
			myObject1 = fromJson(answer);

			for(RequestListener listener : listeners) listener.data1Changed();
		}

		@Override
		public void onApiMethod2(Json answer)
		{
			myObject2 = fromJson(myObjects2);

			for(RequestListener listener : listeners) listener.data2Changed();
		}
	};
}


Screen
public class MyActivity extends Activity implements DataStorage.DataListener
{
	private Button button1;
	private Button button2;

	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);

		button1.setOnClickListener((View) -> {
			Request.apiMethod1();
		});

		button2.setOnClickListener((View) -> {
			Request.apiMethod2();
		});

		updateViews();
	}

	@Override
	protected void onPause()
	{
		super.onPause();

		DataStorage.unregisterListener(this);
	}

	@Override
	protected void onResume()
	{
		super.onResume();

		DataStorage.registerListener(this);
		updateViews();
	}

	private void updateViews()
	{
		updateView1();
		updateView2();
	}

	private void updateView1()
	{
		Object1 data = DataStorage.getObject1();

		// ...
		// Тут обновляем нужные вьюшки
		// ...
	}

	private void updateView2()
	{
		List<Object2> data = DataStorage.getObjects2();

		// ...
		// Тут обновляем нужные вьюшки
		// ...
	}

	@Override
	public void onData1Changed()
	{
		updateView1();
	}

	@Override
	public void onData2Changed()
	{
		updateView2();
	}
}


App
public class MyApp extends Application
{
	@Override
	public void onCreate()
	{
		super.onCreate();
		
		Request.registerListener(DataStorage.listener);
	}
}


Та же схемка, но в терминах RESS, для понимания


Работает это так: При нажатии на кнопку, дергается нужный метод у Request, Request посылает запрос на сервер, обрабатывает ответ и уведомляет сначала DataStorage. DataStorage парсит ответ и кеширует данные у себя. Затем Request уведомляет текущий активный Screen, Screen берет данные из DataStorage и обновляет UI.

Screen подписывается и отписывается от умедомлений в onResume и onPause соотвественно. А так же обновляет UI дополнительно в onResume. Что это дает? Уведомления приходят только в текущую активную Activity, никаких проблем с обработкой запроса в фоне или поворотом Activity. Activity будет всегда в актуальном состоянии. До фоновой активити уведомление не дойдет, а при возвращении в активное состояние, данные возьмутся из DataStorage. В итоге никаких проблем при повороте экрана и пересоздании Activity.

И для всего этого хватает дефолтных апи из Android SDK.

Вопросы и ответы на будующую критику


1. Какой профит?


Реальная простота, гибкость, поддерживаемость, масштабируемость и минимум зависимостей. Вы всегда можете усложнить определенную часть системы, если вам необходимо. Очень много данных? Аккуратно разбиваете DataStorage на несколько. Огромное REST API у сервиса? Делаете несколько Request. Листенеры это слишком просто, некруто и немодно? Возьмите EventBus. Косо смотрят в барбершопе на HttpConnection? Ну возьмите Retrofit. Жирный Activity с кучей фрагментов? Просто считайте что каждый фрагмент это Screen, или разбейте на сабклассы.

2. AsyncTask это моветон, возьми хотя бы Retrofit!


Да? И какие проблемы он в данном коде вызывает? Утечки памяти? Нет, тут AsyncTask не хранит ссылки на активити, а только ссылку на статик метод. Ответ теряется? Нет, ответ всегда приходит в статик DataStorage, пока приложение не убито. Пытается обновить активити на паузе? Нет, уведомления приходят только в активную Activity.

Да и как тут поможет Retrofit? Просто смотрим сюда. Автор взял RxJava, Retrofit и все равно лепит костыли, чтобы решить проблему, которой в RESS попросту нет.

3. Screen это же ViewController! Нужно разделять логику и представление, arrr!


Бросьте уже эту мантру. Типичный клиент для REST-сервиса это одна большая вьюшка для серверной части. Вся ваша бизнес-логика это установить нужный стейт для кнопки или текстового поля. Что вы там собрались разделять? Говорите так будет проще поддерживать? Поддерживать 3 файла с 3 тоннами кода, вместо 1 файла с 1 тонной проще? Ок. А если у нас активити с 5 фрагментами? Это у нас уже 3 x (5 + 1) = 18 файлов.

Разделение на Controller и View в таких кейсах просто плодит кучу бессмысленного кода, пора бы уже это признать. Добавлять функционал в проект с MVP особенно весело: хочешь добавить обработчик кнопки? Ок, поправь Presenter, Activity и View-интерфейс. В RESS для этого я напишу пару строк кода в одном файле.

Но ведь в больших проектах ViewController ужасно разрастается? Так вы не видели больших проектов. Ваш REST-клиент для очередного сайта на 5тыс строк это мелкий проект, а 5тыс строк там только потому что на каждый экран по 5 классов. Реально большие проекты на RESS с 100+ экранов и несколькими командами по 10 человек прекрасно себя чувствуют. Просто делают несколько Request и Storage. А Screen для жирных экранов содержат внутри себя дополнительные Screen для крупных элементов UI, например, тех же фрагментов. Проект на MVP тех же масштабов просто захлебнется в куче презентеров, интерфейсов, активити, фрагментов и неочевидных связей. А переход на VIPER вообще заставит всю команду уволиться одним днем.

Заключение


Надеюсь эта статья сподвигнет многих разработчиков пересмотреть свои взгляды на архитектуру, не плодить абстракции и посмотреть на более простые и проверенные временем решения
Поделиться публикацией
Комментарии 43
    0
    Аббревиатура RESS в мобильном мире когда-то закрепилась за Responsive Design + Server Side (https://www.lukew.com/ff/entry.asp?1392).
      +3
      Хорошо было бы посмотреть на вашу архитектуру в действии. Может вам стоит написать какое-нибудь приложения для демонстрации? Честно говоря у меня возникает сомнение в целесообразности применении данной архитектуры даже в средних приложениях. Что-то подобное у нас делал Junior разработчик в небольшом проекте и от этого кода дыбом волосы становились, особенно, когда мне пришлось исправлять баги и дорабатывать экраны…
        0
        В статье есть пример небольшого приложения, опущены только детали реализации UI

        Честно говоря у меня возникает сомнение в целесообразности применении данной архитектуры даже в средних приложениях
        Так приведите аргументы почему?

        Отсутствие проблем с Activty lifecycle, поворотами экрана, упрощение архитектуры и как следствие увеличение понимания системы любым членом команды, уменьшение количества мусорного кода, упрощение поддержки, ускорение и удешевление стоимости разработки для вас нецелесообразно?

        делал Junior разработчик в небольшом проекте и от этого кода дыбом волосы становились
        А от статей по VIPER у вас волосы дыбом не встают? Вы точно уверены кто там у вас джуниор в команде? Ну и скорее всего ваш джун просто все коряво сделал
          0
          В статье есть пример небольшого приложения, опущены только детали реализации UI

          Я, наверное, не правильно выразился. Я имел ввиду законченное клиент-серверное приложение среднего масштаба. Например небольшой клиент для GitHub. Ваш пример очень простой и может не выявить определённых проблем в архитектуре, а они присутствуют в любой из них. Тем более я уверен, что Вы будете дальше развивать вашу архитектуру, и мне, как неуверенному джуниору, нужен будет пример реализации

          Отсутствие проблем с Activty lifecycle, поворотами экрана, упрощение архитектуры и как следствие увеличение понимания системы любым членом команды, уменьшение количества мусорного кода, упрощение поддержки, ускорение и удешевление стоимости разработки для вас нецелесообразно?

          Сильное заявление, проверять я его, конечно, не буду. Если сделаете рабочее приложение и выложите на GitHub, будет очень круто. Ну а пока я просто вижу несколько проблем, которые, конечно, можно решить, но надо посмотреть как это всё будет работать вместе и не усложнит ли всю архитектуру. Далее привожу естественно возникшие вопросы

          • В каком компоненте будет реализована навигация?
          • Я так понимаю, что данные в DataStorage пока не чистятся. В какой момент будет происходит очистка?
          • Будет ли активность использовать несколько Request и DataStorage для переиспользования кода?
          • Могу ли отменить Request?
          • Как обрабатываются ошибки?


          Думаю хватит, не буду к каждой мелочи придираться. Вообще это хорошо, что ты придумываешь как было бы лучше писать приложения, но трудно оценить реальную пользу на искусственном примере

          А от статей по VIPER у вас волосы дыбом не встают?

          Я Android разработчик, у нас VIPER не так популярен как на iOS, но благодаря Вам я прочёл одну статью, и на удивление мне всё понятно. Это обычная реализация Clean Architecture
            0
            Сразу скажу, предлагаемая архитектура в статье описывает вопросы по проектированию приложения, а не то, на какой секунде выполения удалять данные из кеша, это зависит от задачи

            Я имел ввиду законченное клиент-серверное приложение среднего масштаба. Например небольшой клиент для GitHub.
            Если будет свободное время, то выложу конечно

            Сильное заявление, проверять я его, конечно, не буду.
            Аргументация уровня ТНТ

            В каком компоненте будет реализована навигация?
            Что вы имеете ввиду под навигацией? Переходы между Activity? Как правило в Activity. В кейсах когда Activity на экране нет, то подходящий вариант — Application

            Я так понимаю, что данные в DataStorage пока не чистятся. В какой момент будет происходит очистка?

            Зависит от вашей задачи и требований. Самый частые кейсы — перезаписывать новыми данными, удалять лишние при обновлении.

            Будет ли активность использовать несколько Request и DataStorage для переиспользования кода?
            Вопрос вообще не очень корректно поставлен. Request\DataStorage — статик классы, вы можете использовать их из любой части приложения. В RESS рекомендуется делать по одному Request и DataStorage на все приложение, и разбивать их на несколько только в случае сильного разрастания их объема(Например, при широком API, или большом разнообразии данных)

            Могу ли отменить Request?
            Опять же, не корректный вопрос. Request — статик класс с набором методов, делающим запросы к REST-API, а не отдельный запрос. Если вы хотели, спросить можно ли отменить запрос посланный к сервису, этого нет в примере, но никаких ограничений архитектура не накладывает.

            Как обрабатываются ошибки?
            Зависит от задач и типа ошибок. Ошибки от сервиса приходят вместе с answer в листенер подписанный на Request. Подписывайтесь где вам удобно и обрабатывайте. Визуальные можете в Activity, с данными в DataStorage, или вообще вынести отдельный статик ErrorHandler. Не думаю что это имеет смысл разжовывать, это очевидно

            Я Android разработчик, у нас VIPER не так популярен как на iOS, но благодаря Вам я прочёл одну статью, и на удивление мне всё понятно
            Хорошо. Вот, например, мне не все понятно. Если вы разобрались, распишите почему разбиение одного экрана приложения, даже очень простого, на ~10 сущностей(Router, Configurator, Presenter, Interactor, View и интерфейс к каждому) и файлов некоторые люди, в том числе видимо и вы, считают адекватным инженерным подходом?
              +1
              Далее не вижу смысла задавать вопросы, лучше посмотрю на реализацию всего этого чуда. Лучше отвечу про VIPER, а точнее про то как я реализовываю Clean Architecture в своём приложении. Становится ясно, что вы не поняли её смысл. Никаких 10 сущностей на каждый экран нет. На каждый экран есть View и Presenter, причём презентер в некоторых редких случаях можно переиспользовать. Роутер у вас один на всё приложение, а не на каждый экран, не знаю откуда вы это взяли вообще. Далее интеракторы и репозитории тоже на всё приложение и разбиты по фичам, то есть максимально переиспользуются во всём приложении. Далее даю ссылки на статью1 и статью2 для ознакомления. Вы же пишите в топик по Android и должны их в пример приводить, а не VIPER с реализацией под iOS. Судя по вашему коду и ответам на вопросы у вас не так много опыта в разработке больших приложений под Android. Не хотелось бы ещё разводить холивар по поводу переноса фигурной скобки на новую строку, но в Java так не пишут, есть устоявшийся стиль форматирования (Ctrl + Alt + L в помощь)
                –6
                но в Java так не пишут, есть устоявшийся стиль форматирования
                Всю жизнь в джаве скобку на новую строку ставил и никто не умер. В тексте прям чувствуется ваше желание меня поучить. Вы на чем-то кроме джавы писали?

                Вы же пишите в топик по Android и должны их в пример приводить, а не VIPER с реализацией под iOS
                Вы видите прям такую принципиальную разницу между этими системами в части реализации архитектуры?

                На каждый экран есть View и Presenter
                Про интерфейс для View не забыли? Итого, классический MVP. По 3 сущности на экран. Уже не 10 конечно, но тоже перебор.

                Далее интеракторы и репозитории тоже на всё приложение и разбиты по фичам
                Ага, видимо по одному интерактору и репозиторию на фичу, а фича чаще всего соответствует экрану. Т.е добавили новую фичу, впилили кучу классов и интерфейсов на всех уровнях, и это еще без реализации самой фичи. Это адекватный инженерный подход? Ок

                В приведенных вами статьях, нет ни слова про VIPER. А в той, что привел я, автор предлагает делать ViewController, Presenter, Iteractor, Configurator под каждый экран, и говорит что это соотвествует принципам Clean Architecture. Вы же говорите третье.
                Где же правда?

                В любом случае даже в приведенных вами статьях, как и в VIPER подходах, общий посыл это наплодить 100500 сущностей, где можно обойтись двумя-тремя. Автор вероятно в детстве порезался бритвой Оккама
                  +1
                  Дабы не быть многословным, я просто подскажу книгу, которая поможет тебе в твоём познании в архитектуре приложений. У тебя огромные пробелы в понимании чистого кода и архитектуры. Я просто не хочу сейчас разводить бессмысленные споры в комментариях

                  Мартин Роберт. Чистая архитектура. Искусство разработки программного обеспечения
                    –2
                    Аргумент «просто не шаришь», я тебя понял
                0
                1) onHandleAnswer — это вообще что такое? а если будет 30 запросов, там будет ifelse на 30 условий? рили?
                2) урлы запросов тоже будете дублировать?
                3) сейчас ваша архитектура не позволяет отменять запросы, а если добавить эту возможность, то опять же будет множество переменных для каждого запроса
                4) если экрану не нужен ответ от определённого запроса, то зачем он подпсывается на него?
                5) а если нужно обработать два ответа разных запросов и только после этого показывать пользователю? подозреваю, появится куча флагов.
                6) и где будет происходить эта обработка? dataStorage отвечает только за хранение данных, а screen должен отвечать только за отображение
                7) если делать обработку в screen, то получится каша из логики обработки и отображения, в которой разбираться долго, как минимум, придётся как-то отделять/разносить, с чем отлично справляется mvp
                8) плюс mvp, например, в том, что я могу просто открыть интерфейс презентера/view и по методам понять, что происходит на экране. в вашей архитектуре это сделать очень сложно
                9) у вас есть проблемы со структурой класса и, как уже отметили ниже, с кодстайлом, если уж пишите на java, то соблюдайте принятые нормы. в чужой монастырь со своим уставом, как говорится

                конечно, в мелкие проекты на 1-2 экрана тащить clean или просто mvp смысла нет, но в остальном такое вот юзать будут только очень странные люди
                  0
                  Наконец-то свидетели MVP подъехали, без вас было скучно

                  Как сказал один мой друг увидев ваш комент:
                  Чувак не может url в конфиг вынести, о чем ещё говорить?)

                  1. См.цитату
                  2. См.цитату
                  3. См.цитату
                  4. В листенере несколько методов? Проблема проблем. См.цитату
                  5. Показывать после второго? State
                  6. Плохо читали статью. dataStorage отвечает в том числе и за приведение данных в удобоваримый вид
                  7. См. пункт 6
                  8. Натягиваение глобуса на сову
                  9. Какие конкретно? Скобка не там — ррря, проблемы с кодстайлом!

                  Архитектура регламентирует подход к проектированию, а не количество ифов в методе. Вы не можете элементарно модифицировать простейший код чтобы несколько ифов убрать, как маленькие прямо.

                  Зато считаете что разбираетесь в проектировании, прочитав пару статей про Clean и MVP
                    0

                    Использую я mvvm, так что мимо


                    1) ну вы привидите решение без ifelse. В данном случае красиво это не решить
                    2) вынести можно всё, но не всему там место, здесь претензия была больше к коду. Вы же пытаетесь прорекламировать вашу архитектуру, а код пишите такой себе, Алик плюсов и не видно, если они есть, конечно
                    3) вообще без аргументов
                    4) тут отсутствует логика, как минимум
                    5) вы мой комментарий читали? Я и говорю, что появится куча флагов
                    6) следуя из названия, класс отвечает за хранение данных. Проблемы в нейминг?
                    7) ответил выше
                    8) опять без аргументов написали бред
                    9) если проблемы с русским, то 'и' — это союз, перед которым было написано про структуру класса


                    Если не можете воспринимать критику, то удалите статью просто, не ваше это
                    Я показал конкретные недостатки в вашей реализации вашей архитектуры. Сказать можно много чего, а вы попробуйте написать правильный и красивый код с данной "архитектурой", потом уже можете говорить что-то про ее идеальность и превосходство

                      0
                      1. Аннотации
                      2. Я и не спорю что код в примерах не идеален, это не главное в статье
                      3. Запросы в мапку, отменять по idшнику. Один метод cancel(id). Очевидные вещи нужно расписывать?
                      4. В чем проблема то вообще? Вы предлагаете делать по одному листенеру на ответ или что?
                      5. А вы ответ читали? Обновлять UI после последнего ответа из нескольких и никаких флагов
                      6. Проблема проблем. Ну назовите подругому
                      8. Вы в качестве плюса MVP приводите смехотворный и вообще сомнительный пример. Обычно чем занимается экран понятно по названию, или проблемы в нейминг?
                      9. Нечего сказать — докопайся до орфографии. Так и вопрос был, какие именно проблемы со структурой? И как сделать лучше, по-вашему?

                      Если ты не согласен с моим мнением, то удоли статью!
                      Я смотрю вы критику и сами не воспринимаете толком. Вы приводите недостатки кода в конкретном примере. Я же отвечаю что, конкретно к архитектуре большая часть этих недостатков отношения не имеет. Код в статье чтобы показать общую схему работы, а не чтобы его копипастить.

                      Ну в целом я согласен, что видимо стоит в примеры вылизанный вариант выкладывать, чтобы можно было сразу в проект копировать, а не головой думать

                        0
                        1) не решают
                        2) ещё раз повторяю: вы продаёте архитектуру, а в данном случае она выглядит слишком убого, поэтому в ваших интересах написать правильный код. вы же наставиали на использовании asyncTask, то есть выставили требования к реализации. или это работает так «ну я вот тут я разобрался, как делать, поэтому делайте так, а тут не разобрался, поэтому придумайте сами»? двойными стандартами пахнет
                        3) а вы часто пишите велосипеды? зачем писать что-то своё, если есть аналог, который поддерживается людьми, тестируется и имеет больше функционала?
                        4) я говорю про то, что в любой архитектуре, которые вы так сильно хаете, этой проблем нет, ибо подписка идёт только на то, что нужно экрану и всё
                        5) как вы поймёте, какой ответ был последний? или будете запускать их последовательно?
                        6) ну вот ещё один косяк в вашей «арзитектуре»: она должна быть понятна изначально, все термины/названия должны быть понятны, а не так, как вы говорите «ну переименуй, в чём проблема». я могу и модифицировать её до mvp, но это же уже не будет вашим решением
                        8) «экран отображает сообщения» и «на экране обрабатываются такие-то эвенты, отображаются такие-то данные и открываются такие-то экраны» — это совсем разные знания, согласны? интерфейс предоставляет второе, что гораздно полезнее
                        9) я не докапывался до орфографии, просто вы слишком часто подменяете понятия. в данном случае структура класса — это последовательность полей и методов с различными областями видимости

                        вы не можете этого видеть, потому что меня сейчас не критикуют
                        снова повторяю, все недостатки имеют отношение к архитектуре, потому что вы должны показать рабочий, карсивый и правильный вариант её использования, а показываете вот это

                        снова подмена понятий: никтоне собирается копировать ваш код, успокойтесь уже
                        «mvp — слишком сложна, я не хачу думать» — вот суть ваших претензий, а сейчас, так что ваш ответ выглядит глупо
                          0
                          1. Решают
                          2. Еще, раз, в статье описан подход, а вы хотите чтобы я каждый чих регламентировал. А потом набежит еще десяток таких как вы и будет спорить сколько на самом деле методов должно быть в листенере по фен-шую. Пусть лучше каждый сам для себя эти мелочи решает. На AsyncTask я не настаивал, в статье прямым текстом написано, используйте че хотите если не нравится AsyncTask. AsyncTask там для примера, чтобы показать тем, у кого с ним возникают проблемы, что проблемы не с ним, а в архитектуре. И в RESS с ним никаких проблем нет

                          Вся суть RESS это:

                          1. сетевые запросы в отдельный статик\синглтон класс
                          2. все данные в хранилище данных в отдельный статик\синглтон класс
                          3. активити и фрагменты как стандартные viewcontroller, данные берут только из хранилища
                          4. однонаправленный поток данных через эвенты -> Request -> Storage -> UI

                          Как это будет реализовано, не особо важно

                          Да, теперь я согласен с вами, что в статье стоит приводить вылизанный код.

                          3. А вы видимо часто 3 строчки кода заменяете на готовую библиотеку. Юношеский максимализм, что поделать
                          4. То что вы описываете, это вообще не проблема. нравится делать сотню разных листенеров вместо 1-2 — делаете, если считаете что так правильнее.
                          Спойлер
                          не правильнее
                          5. Конечно. Обработка нескольких запросов, приходящих в разном неопределенном порядке в любой архитектуре будет выглядеть убого
                          6. Непонятно пока только вам.
                          8. Вы высасываете преимущество из пальца. Да, давайте сделаем 3 класса место одного, наделеем между ними мусорных связей, раздуем код, будем заниматься бессмысленными рассуждениями о том, какая часть кода относится чисто ко view, а какая еще и логику содержит.
                          Спойлер
                          В клиентских приложениях — все приложение одно большое View, нет четких критериев чтобы разделить значительную часть логики и представления
                          В итоге получим либо кашу, либо кашу которую нужно регулярно рефакторить. Естественно наделаем больше ошибок и багов. И чтобы эту проблему решить, обложим тестами. В итоге пишем херовый код, чтобы потом его тестами сделать менее херовым. Но ЗАТО, вы можете взглянуть на интерфейс и понять что происходит, и это если в интерфейсе хороший нейминг.
                          9. Так и что конкретно вам не нравится в стркутуре класса?

                          потому что меня сейчас не критикуют
                          Как это? Я вас критикую.
                          все недостатки имеют отношение к архитектуре
                          Реально? Т.е. если я сейчас перепишу код из статьи на идеальный, оставив ту же архитектуру, вы скажете что недостатки архитектуры исчезли? Браво

                          «mvp — слишком сложна, я не хачу думать»
                          Думать надо над решением задач, а не над тем как побольше абстракций в архитектуру запихать. MVP — не сложна, а избыточно сложна, дополнительный слой абстракции должен упрощать систему, а в MVP он усложняет, это fail. Ваше поколение почемуто считает, что чем сложнее — тем круче, нет — чем сложнее в архитектуре тем хуже. Сделать просто сложнее, чем сделать сложно. Думать не хочет не тот, кто видит лишнюю сложность, а тот кто усложняет вместо того чтобы подумать и упростить.

                          P.S. спорю с человеком, который вопросы на StackOverflow задает в русскоязычном разделе, мне стыдно
                            0
                            Чтобы вы совсем со стыда сгорели можете еще с 1сником поспорить).
                            По теме — сильно не вникал в спор, читал ради развлечения, но вроде как
                            Конечно. Обработка нескольких запросов, приходящих в разном неопределенном порядке в любой архитектуре будет выглядеть убого

                            вполне нормально должна выглядеть с RX, Observable.combineLatest. Или чем то подобным, по крайней мере интуитивно кажется что должно лечь.
                              0
                              Да, с Rx скорее всего будет ок
                              0
                              снова подмена понятий, я не говорил про 3 строчки. вы в каждом проекте всё пишете с 0? слышали про переиспользование? почитайте, интересно

                              как раз много их не будет, их будет столько же в общем случае, просто они будут расписаны в конкретном презентере, это называется порядком

                              вы реально считаете, что класс на 30 запросов — это правильно?
                              dataStorage в котром есть логика по хранению, маппинг json -> object, objects -> object, оповещение подписчиков — это правильно? класс, в котором намешана логика и отображение — это правильно? или логика «я пишу без ошибок, мне не надо тестировать»? если хоть где-то ответ был «да», то мне стыдно за вас

                              обработка нескольких запросов будет выглядеть убого у вас

                              ошибок будет как раз больше здесь, ибо всё намешано именно тут, в правильных архитектурах всё упорядоченно

                              если меняется бэк, то в клине достаточно поменять слой data. сколько работ будет в вашем случае?
                              я не скажу такого, потому что это не архитектура
                              mvp — это легко, жалко вас, если не смогли понять
                              а самое тупое — это то, что ваш вариант является самой тупой реализацией того же mvp. и именно из-за того, что реализация очень тупая, вытекают все недостатки. поучаствуйте в крпных проектах, поймёте недостатки, а сейчас вы мыслите слишком недалёко

                              ps в чём проблема русскоязычного StackOverflow? комьюнити тупое? или стыдно на русском спрашивать?
                              спорю с человеком, который пишет статьи на русском хабре, а не на английском медиуме, мне стыдно
                              pps да вы ещё и вопросы задаёте на тостере, боже
                                0
                                Ох, мой юный друг, в вашем возрасте я точно так же рассуждал и думал что все знаю. Только ваше поколение в добавок к этому еще думает догмами и паттернами, а не головой. Чистая Архитектура — манна небесная, разделять логику и представление нужно во что бы ты ни стало, чем больше абстракций — тем лучше, на каждый чих по отдельному классу — идеально, 100% покрытие тестами и т.п.

                                Реальность же состоит в том, что везде нужен баланс. Нет 100% правильного варианта, но ударяться в крайности почти всегда — неправильно. Где-то нужно оставить задел на будущее, а где-то нафиг не надо. Гдето нужно разбить класс или интерфейс на несколько, а где-то хватит одного. Где-то нужно выделить абстракцию, а где-то написать все в одном месте. Вам это еще предстоит понять, вы пока нахватались модных веяний с строго им следуете.

                                Нужно не каждый раз выносить отдельный слой дата, на случай если бек поменяется, а сначала оценить вероятность того что это вообще случится, и время на внесение изменений в код в данном случае. Если выгоднее будет переписать, то смысла оставлять задел нет.

                                Тем более что в моем случае изменений будет ровно столько же, если не меньше, тупо потому что кода меньше.

                                Про разделение логики и представления я уже писал. В клиентских приложениях это разделение весьма условно и размыто. В большинстве случаев разнесение View и Presenter приводит тупо к дублированию мусорного кода в обоих классах и либо говнокоду, либо постоянному рефакторингу.

                                ваш вариант является самой тупой реализацией
                                реализация очень тупая
                                Ха-ха, так вот что вы не понимаете, вы считаете что тупо это проблема. Нет, в проектировании систем — тупо это хорошо, чем тупее тем лучше. Вот когда слишком тупо, чтобы решать поставленные задачи — тогда плохо, нужно усложнять.

                                То что вы этого не понимаете, говорит о том, что проектировать вы не умеете, и опыта самостоятельного проектирования архитектуры у вас нет. Прочитать книжку про Clean Arhitecture еще не значит уметь проектировать. * Шутка про фотографов и ноутбук *

                                Если задача стоит сделать табуретку — тупо доска + 4 ножки это идеальное решение, если этого мало то можно приделать спинку. Вы же считаете что нужно всегда делать кресло с кожаной обивкой, подлокотниками, пружинами и гидравлическим механизмом. И обязательно модульное, а то вдруг нужен будет не гидравлический механизм, а электрический. А о том, что вероятность этих изменений в данной задаче равна 0.001% вы естественно не думаете.

                                поучаствуйте в крпных проектах, поймёте недостатки, а сейчас вы мыслите слишком недалёко
                                Я учавствовал, и в этих проектах использовали RESS, она прекрасно масштабируется до больших размеров, особенно если головой думать, а не догмам по инструкции следовать. Если в проектах такого масштаба использовать Чистую Архитектуру, там половину времени будет занимать рефакторинг, а IDE от количества файлов повесится просто, не говоря уже о том что размер проекта выйдет за рамки понимания.

                                Вы сами то крупные проекты делали? То, что лежит у вас на гитхабе, это откровенная мелочь. Они конечно кому-то могут казаться средними, но это исключительно из-за раздутой архитектуры. И половина коммитов — рефакторинг, think about it.

                                Поработайте в реальных проектах, реальных командах с реальными людьми, состоящими не целиком из адептов вашей секты Чистого Кода, сильно удивитесь. Это когда пишешь все один по фану и дедлайнов нет, можно бесконечным рефакторингом и написанием лишнего кода заниматься, а в реальной жизни и времени это все у вас не будет, и соседи по команде будут пачкать ваши чистые абстракции, и MVP они будут понимать по-другому, потому что фанатеют от другого гуру.

                                И все ваши влажные мечты про просто поменять слой data пойдут прахом, когда будете за 5 минут до дедлайна за джуном код рефакторить, потому что он наплевал на ваши абстракции и убежал в универ экзамены сдавать.

                                ps в чём проблема русскоязычного StackOverflow? комьюнити тупое?
                                Да, именно так. Все, кто могут идут сразу на англоязычный
                                  0
                                  перечитайте внимательно моё сообщение, я говорил, что в мелкие проекты тащить клин не нужно, хватит уже нести чушь, которую вы сами придумали
                                  в крпупном проекте я участвовал и участвую
                                  и такие «крупные» проекты на «великой архитектуре ress», которые никто не хочет поддерживать, потому что написано полное и унылое, я тоже видел достаточно
                                  ps вы так аккуратно опустили моё замечание про хабр и тостер, смешной вы человек
                                    0
                                    я говорил, что в мелкие проекты тащить клин не нужно
                                    Да, вот только во все свои мелкие и опубликованные проекты клин вы затащили

                                    такие «крупные» проекты на «великой архитектуре ress», которые никто не хочет поддерживать, потому что написано полное и унылое, я тоже видел достаточно
                                    Очевидно, у вас пробелы в понимании того, что такое RESS

                                    вы так аккуратно опустили моё замечание про хабр и тостер,
                                    Да потому что это замечание ниочем, подкалывать вы не умеете. Русский SO — бедный родственник англоязычного для тех кто не знает английский с аудиторией из вчерашних школьников. А хабр и тостер самодостаточные сайты, если они вам не нравятся, зачем вы тогда тут сидите?

                                    Про то что, вы неаккуратно опустили 80% моих замечаний, я промолчу, т.к. вам на них видимо ответить нечего.
                                      0
                                      что я тащу в мелкие проекты — это уже моё дело, есть такая вещь, как учебные и пет проекты
                                      либо у вас проблемы в презентации
                                      вам же не нравится, что сайт русский, так не пишите на русском сайте, пишите на медиуме, знаете ведь английский. а юзать тостер, ну это уже совсем такое

                                      да потому что общаться с вами скучно, ибо слишком скудоумны, вот и нет желания расписывать ваши «замечания»
                                        0
                                        что я тащу в мелкие проекты — это уже моё дело
                                        В любом случае ваши утверждения расходятся с делом. Вы советуете одно, а делаете другое.

                                        вам же не нравится, что сайт русский, так не пишите на русском сайте
                                        Повторите свой, как вам кажется, очень-очень удачный подкол, четвертый раз, может после этого он станет хоть немного остроумным. Я вам на него уже ответил намного больше раз, чем он этого заслуживает.

                                        ибо слишком скудоумны
                                        Раз уже перешли на личности, значит точно по существу ответить нечего.
                                          0
                                          вы предлагаете учиться на крупных проектах, на которые тратятся большие деньги?
                                          где подкол? медиум хуже хабра? тостер лучше стека?
                                          вы считаете, что человек не может отвечать на русском и английском, а должен выбрать что-то одно, и, по вашей логике, на медиуме пишут умные люди, знающие англ, а все остальные остались на хабре
                                          это вы давно перешли на личности, а я просто ответил на ваше замечание
                                            0
                                            на медиуме пишут умные люди, знающие англ, а все остальные остались на хабре
                                            Если судить исключительно по вашим комментариям, то такая мысль конечно может придти в голову, да

                                            Давайте уже закончим этот спор. Очевидно что по существу, вам ответить больше нечего, вы уже начали заниматься сплошной демагогией.
            0
            Очень простая архитектура TEA (The Elm Architecture) хорошо себя зарекомендовала в веб-приложениях. В том числе и в работе с http.
            Думаю, для мобильных приложений она бы тоже подошла.
              0
              Восстановил пароль от Хабра для этого комментария.
              Полностью поддерживаю автора. Ушел на похожую «архитектуру» год назад (с MVP, кстати). Не пожалел ни разу:
              1. «Эфемерность» андроидных вью решается встроенным объектом стейта (vanilla/Bundle, Icepick), либо «улучшенными фрагментами Conductor».
              2. Однонаправленный поток данных? Сетевой слой в сервис (по возможности IntentService — чтобы о байндингах и экземплярах при множественных запросах не думалось). Никаких ответов во вью слой: результат — в удобный storage ObjectBox.
              3. Обновление вью? Подписка на представление хранилища (Rx рулит).
              4. Несколько сетевых API, использование аппаратных провайдеров, логика на стороне клиента? О да, достаем DI. Только не монструозного Dagger2, а лаконичного Toothpick.

              — И, ой! Это же классический MVC?! Ему же лет 40! Фу! Да, это так.
              — И что, приложение магазина «весит» меньше 10МБ вместе с картинками? Да, но мне и не за LOC (количество строк кода) платят.
              — А какая «магия» нужна для обновления данных отображаемых в неактивном parentView при изменениях в его Child? Никакой, это дефолтная особенность «архитектуры».
                0
                Тоже поддерживаю автора. Плюсанул бы, да кармы нет)
                Вообще за 5 лет тоже пришел к тому, что существующие решения переусложнены. Сам часто делаю на упрощенной clean, да и только потому, что заказчик непредсказуем, а чистого rest становится всё меньше.
                Не понимаю всего фана по архитектуре- многие воспринимают mvp\viper как стандарт де-факто. Ни шагу в сторону. На практике- да, знать их надо, но использовать надо как базу, а не идентичное решение. Как паттерны. Потому чем больше типовых архитектур- тем лучше.
                И вообще, всё чаще прихожу к тому, что миром девелоперов сегодня больше управляет мода нежели здравый смысл.
                На мой взгляд, RESS (как бы его не называли) выглядит вполне рабочим решением для определенного круга проектов (не только rest).
                Два вопроса:

                1. Я не против асинктасков, но Вам не кажется, что сегодня ходить в веб через HttpUrlConnection банально опасно, например, отказом нормальной работы? Например, у OkHttp свое обращение с сокетами- бесшовный реконнект, более грамотная работа с heartbeat, нет проблем с gzip и много еще чего. От того HttpUrlConnection, начиная с 5го андроида, основан на OkHttp в кишках.
                Пример из практики- надо закачать файл на сервак. Реализация на HttpUrlConnection. На 4.4+ всё норм, на 4.3 возвращалась ошибка. Как выяснилось- рано закрывался сокет, на 4.3 и ниже HttpUrlConnection не умеет его переоткрывать. Решилось переделкой на OkHttp.
                На мой взгляд, асинктаски можно использовать в ряде задач вместе с грамотным управлением потоками (экзекуторы), но внутри от HttpUrlConnection лучше отказаться.

                2. А что с тестированием на моках? Получается, так или иначе, придем к DI на Gradle Flavours\Dagger и репозиториям? а там уже «привет clean architecture»?
                  0
                  HttpUrlConnection лучше отказаться.
                  Так я и не против, для примера взял то что под руку подвернулось.

                  А что с тестированием на моках?
                  А какой в нем смысл? REST-клиент это приложение на 80% состоящее из UI, если и тестировать то UI. А учитывая качество UI-компонентов в Android, то и большая часть багов там. Усложнять код, только для того чтобы протестировать копеечную бизнес логику, не выглядит хорошей идеей
                    +1
                    Если говорить о простеньком варианте приложения (который Вы, как я понял, подразумеваете)- то да, тут Вы правы. Но ведь такие rest-клиенты никто (ну или почти никто) в продакшене не ждет?
                    Добавим сюда кеширование или возможность работы оффлайн, пуши, авторизацию, обновляемые токены. Возможно даже простенький чат на rest. И вот это уже похоже на определенный минимум, который (на мой взгляд) вполне укладывается в RESS, и бизнес-логика там не такая уж и копеечная. Так или иначе придем к тому, что минимум 1-2 компонента надо тестировать
                      0
                      То что вы описали, вполне стандартная и не сложная логика, половина вообще обертка над запросами ответами к REST-API, которое и так тестами обложена.

                      Я вообще скепьтически отношусь к обкладыванию всего и вся тестами, особенно если игнорируется UI. Смысл тратить время на тестирование Presenterа, если потом всеравно косяки во View нам наделают багов. Это вообще какаято малоосмысленная затея, как по мне. Больше похоже на культ карго, чем на реальную необходимость.

                      Мы усложняем архитектуру, плодим абстракции, затем пишем мусорный код для их связи, как следствие получаем больше багов, тратим больше времени. А потом еще тратим время и пишем тесты, чтобы эти баги отловить. Выгода тут вообще сомнительная
                • НЛО прилетело и опубликовало эту надпись здесь
                    0
                    А можно привести пример, чуть конкретнее? Я не понял, где же, собственно, логика? Или «клиент»-«серверов» несколько?
                    • НЛО прилетело и опубликовало эту надпись здесь
                        0
                        Ну во приложение с двумя компонентами: 1) поле ввода текста, 2) div, отображающий текст из компонента 1. Тут как будет устроено?
                        • НЛО прилетело и опубликовало эту надпись здесь
                            0
                            То есть микросервис «поле вывода»: поле-клиент, модель-сервер; микросервис «дисплей вывода»: вью-сервер; и еще сессия. Правильно?

                            И сессия выступает для каждого из микросервиса и клиентом, и сервером?
                            • НЛО прилетело и опубликовало эту надпись здесь
                                0
                                Несостыковка:
                                клиентами могут быть все компоненты кроме Сессии
                                в Сессию, а та рассылает по своим Вью

                                А вообще, создаетсся впечатление, что это эволюция MVC/MVP/etc. Там у нас одна Model, один View/etc., один Controller/etc. А тут: одна Session (похожая на контроллер?) и много Client'ов и Server'ов, которые могут выполнять, в т.ч., роли моделей и вьюх. Итак, отличия: 1) замена на «абстрактные» Client и Server, 2) не по одному, а много.
                    0
                    Спасибо за новую архитектуру!

                    Я правильно понимаю, что «под мобильные приложения» здесь важно, и имеется ввиду «под нынешние фреймворки Android/iOS»? Я не занимался мобильными приложения, но в веб-, используя, полагаю, MVP, я не наблюдал проблемы «усложненной архитектуры», но, надо признать, для этого я написал свою микро-библиотеку (а-ля урезанный React.JS, видимо), то есть я не был стеснен ничем, кроме «вид — это DOM».

                    P.S. Я не спорю с Вами, я просто хочу сравнить с MVP подробнее. Ибо как увидел схему VIPER, так срочно закрыл вкладку от ужаса. Там все понятно, а про MVP интересно.
                      0
                      Вспоминаю — использовал я EventBus и забыл я что такое слушатели. Опыт использования Контроллера Прерываний при проектировании железа как никак был. И было все шустро и красиво. Но народ почему то плевался. Ну очень нравятся слушатели народу. И перешел я на Service Locator. И плевать мне, делим ли мы на View/Presenter/Router или не делим. И плевать мне на все, что выдумает Google, а завтра это же и обосрет. Главное — решает ли проблемы архитектуры андроид вкупе с задачами клиента или нет.
                      github.com/shishkin1966/CleanArchitecture5
                        +1
                        С виду ваша архитектура — обычное MVC.
                        Не понимаю откуда столько агрессии к MVP, отличная архитектура, которая позволяет отлично разделять логику и переиспользовать компоненты, на данный момент у меня в проекте VIPER(проект больше 200к строчек кода), без випера давно утонул уже километровых классах( презентер в главном экране, где есть куча бизнесс логики — 300 строчек, большая часть методов по 5-10 строчек и обращаются с многочисленными интеракторами, каждый из которых отвечает строго за свою логику), как результат легко тестировать, легко поддерживать, для маленьких и тестовых проектов можно хоть всю логику писать в активити
                          0
                          у меня в проекте VIPER
                          Ну так поэтому и 200к строчек. На каждый чих абстракции добавлять, ясное дело будет много кода.

                        Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                        Самое читаемое