Статьи

Java превосходно реагирует на нативную практически во всех отношениях

Я вступил в дискуссию с коллегой по теме Java против JavaScript, которая с самого начала является проблемной. Затем он упомянул, какой замечательный React Native, я решил, что мне нужно изучить его и, возможно, найти некоторые идеи для Codename One …

Там есть несколько хороших идей, но ни одна из них не является революционной или исключительной, и большинство из них — довольно старые новости для разработчиков Codename One, работающих на Java 8.

Одна вещь, которая мне понравилась, заключалась в том, насколько коротким был демонстрационный код React , поэтому я перенес его на Codename One и получил примерно такой же объем кода и, возможно, лучший / более простой код!

Посмотрите полный список в конце статьи или в проекте github здесь , но давайте сначала рассмотрим, почему код Java «лучше».

Синхронное выполнение

Поклонники JavaScript ненавидят это, но все же факт, что синхронный код проще читать, отслеживать и отлаживать. Например, это версия кода React Native, которая извлекает данные:

01
02
03
04
05
06
07
08
09
10
11
fetchData: function() {
  fetch(REQUEST_URL)
        .then((response) => response.json())
        .then((responseData) => {
             this.setState({
                  dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
                  loaded: true,
             });
         })
  .done();
},

У меня более 20 лет профессионального опыта программирования, и это все еще трудно понять. Видимо, если done() опущена, вы не получите никакой обработки ошибок?

Это странно и подвержено ошибкам. Я чувствую, что за этим скрывается большая часть кода, что делает краткость более запутанной, чем упрощенной (вроде как после политической дискуссии в Twitter). Для меня наш код намного проще:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
react.add(BorderLayout.CENTER, new InfiniteContainer() {
    public Component[] fetchComponents(int index, int amount) {
        try {
            Collection data = (Collection)ConnectionRequest.fetchJSON(REQUEST_URL).get("movies");
            Component[] response = new Component[data.size()];
            int offset = 0;
            for(Object movie : data) {
                response[offset] = createMovieEntry(Result.fromContent((Map)movie));
                offset++;
            }
            return response;
        } catch(IOException err) {
            Dialog.show("Error", "Error during connection: " + err, "OK", null);
        }
        return null;
    }
});

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

1
2
3
4
5
6
try {
    Collection data = (Collection)ConnectionRequest.fetchJSON(REQUEST_URL).get("movies");
    ...
} catch(IOException err) {
    ...
}

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

Разработчики часто ненавидят функцию проверенных исключений Java, и я должен согласиться с тем, что они иногда бывают болезненными (например, InterruptedException глупо), но это прекрасный пример того, почему проверенные исключения имеют значение. Мы ДОЛЖНЫ правильно обрабатывать ошибки, и мы не можем просто игнорировать их, пока наш код не достигнет производства с этим прекрасным комментарием «TODO», который никто не потрудился прочитать.

Один язык — меньше кода

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

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

Обновить == Применить изменения кода

React Native может быть отлажен путем перезагрузки, что поможет при работе с ужасным эмулятором Android. К счастью, Codename One не нуждается в этом эмуляторе, вам также не нужно перезапускать свое приложение для перезагрузки скомпилированных изменений… Например, в NetBeans просто используйте «Apply Code Changes» в отладчике, и ваши изменения мгновенно отражаются в работающем приложении.

Языки сценариев проблематичны «на устройстве»

Это не совсем специфическая «React Native», она относится ко всем инструментам, упаковывающим JavaScript в комплекте приложений. Языки сценариев отлично подходят для Интернета, они похожи на «клейкую ленту». Покажите мне хакера, который не любит клейкую ленту!

Соблазн поставить приложение, созданное с помощью такой клейкой ленты, велик, но в отличие от Интернета, где вы можете просто исправить эту «странную неопределенную» ошибку в производственном процессе, установив новое обновление. С приложениями вам нужно пройти процедуру одобрения Apple … Это означает, что производственные ошибки остаются, пока вы наблюдаете падение рейтинга.

Да, модульные тесты, lint и многие другие решения должны улавливать эти вещи, но когда вы используете современную среду IDE и она обнаруживает потенциальный нулевой вывод благодаря строгому синтаксису языка, это довольно удивительно!

Например, отличный пример для JavaScripts по упрощению проблем был бы в коде, как это:

1
2
3
4
5
6
7
function reduce(var a) {
      if(...) {
         a = a - 1;
      } else {
         a = a + 1;
      }
}

Если бы это был код Java, мы могли бы точно сказать, что здесь произойдет … В JavaScript это не совсем так! Предположим, что из-за ошибки a каким-то образом была строка, равная "11" если условие истинно (что может иметь место во всех тестовых случаях), это будет действовать как число. Например, станет "10" . Но в производстве, если условие по какой-либо причине становится ложным, a станет "111" . Если a представляет что-то ценное (например, долг, кредит и т. Д.), Приложение с этой ошибкой в ​​магазине может быть очень болезненным.

Среда

React native использует собственные среды разработки, а это значит, что для разработки под iOS требуется Mac. Это также означает, что вы выполняете часть работы в Android IDE, часть — в Xcode, а JavaScript — с помощью текстового редактора. Мне удивительно, что разработчики готовы отбросить 30-летнюю эволюцию IDE для какой-нибудь синтаксической конфетки ??? Неужели мы получили травму от «Затмения»? Сегодняшние IDE потрясающие, и тот факт, что вы можете отслеживать / отлаживать весь код с помощью одной IDE, неоценим. У нас, как у команды, есть возможность мгновенно увидеть, кто что использовал и для какой цели поразителен, я не могу понять, как нечто подобное может использовать команда из более чем 2 человек, особенно в распределенной рабочей силе.

Что мне понравилось в JavaScript

Одна вещь, которая мне действительно нравится в работе с JavaScript, — это простота работы с JSON, хотя в приведенном ниже коде я значительно уменьшил его почти до того же размера, но он все еще не так элегантен. Я до сих пор не фанат утилитного типа или языков сценариев, но мне бы очень хотелось добавить что-то вроде объектов свойств в Codename One и улучшить интегрированный синтаксический анализ.

Последнее слово

Одна из проблем, с которыми я сталкиваюсь при кратком программировании, состоит в том, что люди используют его для сокрытия основных понятий, поэтому слишком многое происходит «негласно». Это делает краткий код таким же легким для прочтения, как и твит, к сожалению, если вам нужно выразить даже умеренно сложную идею, Twitter просто не обрезает его, и это большая проблема с некоторыми из этих API.

У React native есть свои поклонники, в конце концов, он, вероятно, лучше, чем PhoneGap, у которого есть свой набор ограничений. Но это все еще ограниченная концепция, стоящая на цыпочках скриптовой инфраструктуры. Он не имеет реального преимущества по сравнению с Codename One и имеет некоторые очевидные потенциальные проблемы.

Листинг Java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class ReactDemo {
    private Form current;
    private EncodedImage placeholder;
 
    public void init(Object context) {
        UIManager.initFirstTheme("/theme");
    }
     
    public void start() {
        if(current != null){
            current.show();
            return;
        }
        placeholder = EncodedImage.createFromImage(Image.createImage(53, 81, 0), false);
        Form react = new Form("React Demo", new BorderLayout());
        react.add(BorderLayout.CENTER, new InfiniteContainer() {
            public Component[] fetchComponents(int index, int amount) {
                try {
                    Collection data = (Collection)ConnectionRequest.fetchJSON(REQUEST_URL).get("movies");
                    Component[] response = new Component[data.size()];
                    int offset = 0;
                    for(Object movie : data) {
                        response[offset] = createMovieEntry(Result.fromContent((Map)movie));
                        offset++;
                    }
                    return response;
                } catch(IOException err) {
                    Dialog.show("Error", "Error during connection: " + err, "OK", null);
                }
                return null;
            }
        });
        react.show();
    }
     
    Component createMovieEntry(Result data) {
        Container entry = BorderLayout.center(
                BoxLayout.encloseY(
                        new SpanLabel(data.getAsString("title"), "Line1"),
                        new Label(data.getAsString("year"), "Line2"))).
                add(BorderLayout.WEST,
                        URLImage.createToStorage(placeholder, data.getAsString("id"),
                                    data.getAsString("posters/thumbnail")));
        return entry;
    }
 
    public void stop() {
        current = Display.getInstance().getCurrent();
    }
     
    public void destroy() {
    }
}