четверг, 23 августа 2018 г.

A programmer: JavaScript #2

Для начала небольшой disclaimer.

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

Во-вторых, прошлый пост получился большой и много о чём. Поэтому придется внести некоторую структуру в излагаемое. Итак, кроме моих мотивов, связанных с изучением JavaScript'a, содержательно в прошлом посте речь шла о NodeJS и создании веб-сервера.

Соответственно, в этом посте - о маршрутизации и верстке.


Умное слово "маршрутизация" в данном конкретном случае означает то, что мы определяем то, что программа должна показывать пользователю, если он совершил то или иное действие. Собственно, в прошлом посте об этом уже сказано. Там мы использовали объект app для того, чтобы работать с библиотекой express. Если бы мы не использовали  express, наша маршрутизация выглядела бы иначе:


То есть мы бы указывали маршрут через проверку условий if-else, а результат был бы тот же. Просто подключив express, мы написали отдельные обработчики, что, мне кажется, удобнее.

В чем ещё есть отличия с примером из прошлого поста? В том, что мы подключили модуль fs - он обеспечивает ввод-вывод данных. Как я говорил, можно подключить и собственный модуль. Тогда пришлось бы написать:

const some_module = require('/some_module');

Слеш перед именем модуля означает, что модуль должен находиться в папке с проектом. Другие модули загружаются в папку node_modules. Для того, чтобы из собственного модуля, который часто представляет собой просто файл, сделать доступными вовне те или иные переменные или функции, надо написать (при этом и функцию можно присвоить переменной и вызывать её через неё):

module.exports.some_value = some_value;

Но вернемся к маршрутизации и верстке. Итак, пусть мы написали обработчики, которые должны показывать нужные страницы в зависимости от запроса. Возникает вопрос: а где же страницы? Они лежат в папке с проектом. Там могут лежать сами страницы - например, какой-нибудь index.html, а могут - шаблоны (обычно в папке /views). Поскольку мы работаем с express, то шаблоны у нас - это файлы с расширением .ejs, которые выглядят, например, вот так:


Похоже на обычный html-файл - почему это шаблон? Потому что в нём есть исполняемый функционал. Он заключен в соответствующие скобки <% ... %> или <%= ... %> (в первом случае имеется в виду некоторое действие, во втором - просто вывод заданного параметра). Естественно, что выводимые параметры должны быть переданы шаблону в файле с маршрутизацией. В нашем примере - это параметр newsId, а также объект obj с параметрами  title, id и массивом paragraphs.

Здесь также видим, что командой include подключается файл header.ejs, находящийся не в самой папке /views, а в подпапке /blocks (строка 11). А в 8 строке подключается файл со стилями, который находится по адресу /public/css. Собственно, в шаблон можно подключить и сторонний код, например, из библиотеки bootstrap. А дальше всё зависит от фантазии и чувства вкуса.

На сегодня всё.

среда, 22 августа 2018 г.

A programmer: JavaScript #1.

Мои метания вокруг программирования привели меня к JavaScript'у. На самом деле это несколько странно, учитывая, что ещё полгода-год назад я учился программировать для Android с тем, чтобы написать несколько набольших полезных программ и выложить в Play Store (он же Play Market). Я видел в этом то преимущество, что эти программы были бы свидетельством моего умения, во-первых, и, возможно, по мере их (программ) развития, приносили бы какую-нибудь копеечку, во-вторых. Собственно, я планирую к этому ещё вернуться.


Но JavaScript для меня оказался привлекательным рядом дополнительных плюсов:

  • Под Android из моих друзей никто не программирует, а значит, спросить совета у живого человека было бы затруднительно. В свою очередь, на курсах по Java, на которые я ходил 2 года назад, я познакомился с Лёшей (фамилию уточнять не буду, ибо не уполномочен), который оказался замечательным ментором. А он некоторое время назад как раз получил работу как JavaScript-программист. Значит, в крайнем случае мне есть с кем проконсультироваться.
  • Как я знаю, JavaScript является весьма востребованным языком программирования, и порог вхождения в него существенно ниже, чем для Java. С некоторых пор я существенно более, чем раньше, заинтересован в том, чтобы найти работу в IT.
  • JavaScript заточен под Web-разработку, а мне ещё в детстве нравилось то, что теперь наывается версткой (стенгазеты - наше всё:)).

Вот, собственно, поэтому я решил попробовать себя здесь.

Лёша предложил мне попробовать сделать приблизительно такое же тестовое задание, которое обычно дают при приёме на работу:

  • поднять сервер NodeJS;
  • сделать к нему фронт на каких-нибудь шаблонах (например, на ejs);
  • подключить базу данных (MongoDB или PostgreSQL);
  • сделать в БД пару таблиц со связями;
  • сделать к ним CRUD;
  • вывести их на фронт;
  • дополнительно - какой-нибудь функционал для работы с таблицами;
  • дополнительно - аутентификацию.

Собственно, этим я и занимаюсь уже второй месяц - пока, к сожалению, не так интенсивно, как хотелось бы. Зачем я вдруг решил поделиться этим с "миром"? Затем, что мне нужен конспект, а писать его только самому себе - скучно. Плюс Ричард Фейнман справедливо полагал, что лучший способ что-то понять и выучить - это рассказать об этом другим. Это же я имел в виду, например, когда выкладывал свои учебные результаты по Java. Теперь собираюсь быть более последовательным, чем тогда.

***
Для чего нужен и в чём особенность JavaScript'a? Если коротко, то JavaScript изначально заточен на то, чтобы сделать вебстраницы интерактивными. Хотя впоследствии на нём стали писать и серверную часть, и всё остальное, главное в нём именно это - поэтому он и входит третьим элементом в стэк веб-технологий:


Особенностью JavaScript'а является то, что это язык со слабой типизацией - то есть типы использующихся объектов не должны быть строго типизированы, как в Java, например. В этом есть свои недостатки, но в то же время за счет этого JavaScript более прост в изучении.


NodeJS - это программная платформа для JavaScript'а, то есть то, что и позволяет работать JavaScript'у, в том числе и в первую очередь в качестве языка для веб-сервера. Так, чтобы создать веб-сервер нужен следующий код (скан из Википедии):



Зачем нужен сервер? Вопрос вроде бы странный, но у меня он почему-то возник на горизонте сознания, поэтому стоит ответить на него. Если вспомнить, что веб-приложения выполняют некоторый код (то есть, в сущности, производят вычисления), то возникает вопрос, где - грубо говоря, на каком оборудовании - эти вычисления происходят? Это может быть либо компьютер пользователя ("клиент"), либо удаленный компьютер, специально предназначенный для этого ("сервер"). Другими словами, всё то, что происходит не на компьютере пользователя (в случае веб-приложений - в браузере), происходит на сервере. Но для этого там должно быть установлено сообветствующее программное обеспечение (в нашем случае - NodeJS), которое и взаимодействует с "железом". Вот мы на основе этого программного обеспечения создаем свой веб-сервер. Мой вариант выглядит вот так:


Как видно, он практически такой же, как предыдущий, только сервер другой (оба - локальные, а не удалённые, у нас пока удалённых нету) и выводится другое. А именно: вместо "некоторый html" можно подставить этот самый html, отображающий что-то в окне браузера, хотя чаще там находится ссылка на находящуюся в другом месте страницу (обычно свёрстанную с использованием шаблонов, которые ещё где-то отдельно находятся).

Пока что запуск нашего файла выводит в консоль заданный текст. А что вообще делает наш сервер? Он "слушает", что делает пользователь. То есть он воспринимает его действия на переданной ему нами странице (в нашем случае это http://127.0.0.1:3000/) и должен как-то реагировать на эти действия. Собственно, наша задача и состоит в том, чтобы сервер на определенные действия (запросы) реагировал определенным образом.

Какие это могут быть действия? Обычно в случае простого веб-приложения или сайта их набор довольно небольшой, например: "показать такую-то страницу" - с информацией о том или об этом. То есть, если действие такое, показываем одно, если другое, то другое, если действие не предусмотрено нами, то показываем сообщение об ошибке, например. Но что такое "действие" в нашем случае?

Самый простой вариант - пользователь что-то ввёл в строке браузера после http://127.0.0.1:3000/ (то есть, находясь на том сервере, который мы "слушаем"). Например, он набрал "http://127.0.0.1:3000/about", желая получить информацию, допустим, о сайте (ну или чаще - нажал кнопку, соответствующую этому действию). Такое действие называется GET-запрос - это когда передаваемый пользователем запрос достаточно простой и может передаваться через строку браузера. Если мы хотим, чтобы наше приложение в ответ на этот запрос что-то сделало, мы, соответственно, должны это запрограммировать. Например так:


Здесь: если пользователь ничего не делал, показываем ему файл "index", если набрал "about", показываем ему файл "about" (оба файла лежат в другом месте).

Главный нюанс состоит в следующем - чтобы всё, что мы написали, работало, мы должны использовать объект app (на котором вызываем функцию get), а чтобы его использовать, мы должны подключить модуль "express" - одну из библиотек, работа с которыми (а не только с синтаксисом) и является, пожалуй, сутью программирования на том или ином языке. NodeJS как раз и хорош тем, что позволяет легко подключать разные модули - как чужие, так и написанные самостоятельно.

Разумеется, соответствующие файлы должны находиться в папке проекта. Для того, чтобы удобно закачивать себе всё, что нужно, NodeJS имеет собственный пакетный менеджер (npm - node packages manager), хотя есть и сторонние разработки (например, yarn). С его помощью легко инсталлировать нужные модули себе в проект.

И тут появляется нюанс. Часть модулей нужны для функционирования приложения, а часть - только для его разработки (например, проверка синтаксиса или стиля кода). Если над проектом работают больше одного человека, последние модули могут быть не нужны кому-то другому (а место занимать), значит, надо как-то обозначить, чтобы при клонировании проекта они не загружались автоматически. Это, как и другие вещи, описываются в файле package.json.

Собственно, когда мы инициализируем новый проект в IDE (я использую Atom), нам как раз и предлагается определить некую базовую информацию, которая будет записана в package.json. Выглядит он приблизительно так:


Здесь среди прочего (имя, версия, описание, автор, лицензия, стартовый файл и т.д.) указаны зависимости (внешние пакеты - "depenencies", с указанием их версий), которые надо подгружать в наш проект автоматически, зависимости, использующиеся для разработки, о которых мы говорили ("devDependencies"), скрипты, выполняемые при старте, и т.д.

***

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


воскресенье, 30 апреля 2017 г.

Just run it

Пора написать что-то, что не относилось бы к программированию вообще, и к Java, в частности. Просто из-за большой нагрузки на работе не удается посвящать этому столько времени, скольхо хотелось бы - успеваю разве что пройти тест по Java раз в день и иногда немного почитать/послушать/посмотреть что-нибудь обучающее. Поэтому востребованным оказывается то, что требует меньше времени, а именно - физкультура и чтение книг по саморазвитию. Событие сегодняшнего дня - пробежка в лесу!
В лесу я не бегал с прошлого года, наверное, с сентября. Это было очень классно! Надо сказать, что за последний месяц мне удалось пройти 10 тренировок (громкое слово "бодибилдинг"), а потому тело хочет нагрузок. А хорошая погода стимулирует желание быть быстрым и легким. Сегодня все сошлось, и я доволен.

воскресенье, 12 марта 2017 г.

A programmer: Java #6: A a = new B();

Долго не мог разобраться с тем, что означает выражение

A a = new B(); // B extends A

Первый мой ответ на этот вопрос был таким:

Когда на объекте а вызывется метод, будет вызван тот его вариант, который переопределен в классе-наследнике. То есть:

Класс-наследник, кроме собственных методов, имеет все методы классов-родителей. Последние могут быть переопределны в классе-наследнике:

Пусть у класса А есть метод do() { // реализация для А};
И у класса В есть метод do() { // реализация для В}

Соответственно, в нашем случае - для a.do(); будет выбрана реализация для В:

Class Животное {
    издатьЗвук() {
        "Звук-Звук";
    };
}

Class Cобака extends Животное {
    издатьЗвук() {
        "Гав-Гав";
    };
}
Животное животное = new Собака();
животное.издатьЗвук();

Результат: "Гав-Гав".

Но возникал вопрос: а что в данном случае происходит с полями? Будут это поля из А или из В?

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

Но правильный ответ - нет, потому что в нашем случае объект а - это объект класса В, который приведен к типу А. Что это значит? Мне посоветовали читать "Философию Java", но хороший ответ нашел тут: "Преобразование типов".

Это значит, что если а - это объект класса В, то поля относятся к классу В. А "приведение ссылочных типов" означает только то, что в приведенном объекте не видны те поля, которых нет в классе-родителе (хотя они есть!). Следовательно, логика тут такая - обрезать то, чего нет в родителе, а в оставшихся полях будут значения из класса-наследника.

Другими словами, если у Животного не предусмотрен Хвост (а предусмотрен только у Собаки), то у нас будет Собака, которой нельзя использовать Хвост (хотя он есть). А если у Животного Хвост предусмотрен, то это будет Хвост Собаки.


UPD: На самом деле поля будут и от А, и от В. Но в случае прямого приведения типов ((А) а) поля от В будут скрыты (как если бы их не было). Другими словами Хвост будет не Хвостом Собаки, а Хвостом Животного.

На эту тему также посмотреть можно здесь, здесь, здесь и здесь.

понедельник, 6 февраля 2017 г.

A programmer: Time checking

Очень коротко о времени. Помнится, я собирался заниматься программированием 1 час в день (см.: A programmer: Time is experience). Как же обстоят дела спустя год?


В течении года были разные периоды. Например, иногда у меня по разным не получалось заниматься неделями и даже месяцами, и я очень переживал по этому поводу. С ноября я пошел на курсы, которые существенно активизировали работу, и поэтому смог наверстать упущенное. Таким образом, в итоге оказалось, что за 2016 год я потратил на программирование 384 часа (что больше, чем в среднем 1 час в день :)). Учитывая, что за этот год на данный момент я занимался 97 часов, а это почти 3 часа в день, то такая динамика мне весьма нравится. И дело не во времени как таковом - я вижу результат. Он, конечно, пока не такой, как хотелось бы, но я думаю, что около 20% того результата, при котором я мог бы чувствовать себя уверенным программистом (разумеется, при наличии постоянной практики - включительно), у меня уже есть. С одной стороны, получается, что, если динамика сохранится, то до 100% результата мне надо еще около 2 лет (а не 33 :) - уже хорошо). С другой, и динамика может измениться, и чувство уверенности прийти раньше, да и в не совсем уверенном состоянии тоже можно утверждаться в качестве программиста.

пятница, 6 января 2017 г.

A programmer: Java #5: Abstract and Final classes

Небольшая заметка для себя насчет abstract и final классов.

Abstract - класс, от которого нельзя создать экземпляр (инстанциироваться). Обычно нужен, чтобы от него наследовались другие классы. Соответственно, он может иметь только статические методы и переменные - для их вызова не нужны экземпляры.

Final - класс, от которого нельзя наследоваться. Тем самым - нельзя переопределять его переменные и методы (то есть они неявно тоже final). Но создавать экземпляры такого класса можно.

Класс может быть одновременно и abstract, и final. В этом случае, соответственно, доступны только его собственные статические методы и переменные, поскольку, как было сказано, у abstract класса не может быть экземпляров.

понедельник, 2 января 2017 г.

A programmer: Java #4: Annotations


После долгих (и не очень нужных, видимо) моральных приготовлений возвращаюсь не просто к блогу, но к Java. Сегодняшняя тема — аннотации.

Когда я учился Java, я видел код — переменные, методы, циклы и прочее, а вот некоторых вещей не видел, и встретил впервые только на курсах. В числе этих невиданных штук были аннотации — это такие надписи со значком @. Очевидно, они для чего-то нужны. Иногда мне рассказывали, для чего, но вот как они работают, я понять не мог очень долго. Мне посоветовали написать аннотацию самому. Но результативно помочь мне в этом лучше всего остального смогла книга Уоллс К. Spring в действии. – М.: ДМК Пресс, 2013 и одно волшебное слово. Про книгу напишу в следующий раз, а про волшебное слово сейчас.

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

@MyAnnotation(здесь могут быть описаны параметры аннотации)

Аннотации основаны на механизме интерфейса, поэтому ключевое слово @interface и будет свидетельствовать о том, что это аннотация:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String info() default "";
int val() default 42;
}

Здесь @Target(ElementType.METHOD) и @Retention(RetentionPolicy.RUNTIME) указывают, что аннотация применима к методу и что она исполняется при выполнении программы, соответственно (разумеется, предусмотрены и другие опции). Для того, чтобы это работало, надо импортировать соответствующие пакеты:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

Как уже говорилось, значения описанных в аннотации переменных (если они иные, чем по умолчанию) могут указываться при применении аннотации.

Данная аннотация пока что только сообщает некоторую информацию, но большинство аннотаций «работают», что-то делают. Вот тут и была самая большая загвоздка для меня: где, собственно, реализация? Где находится кусок кода, который «встраивается» там, где аннотация должна выполнять какие-то действия. Я долго не мог этого понять, пока мне не посоветовали написать не просто какую-то аннотацию, а, например, аннотацию для валидации — то есть для проверки, правильно ли что-то введено пользователем (нет ли недопустимых значений и т.п. - если правильно, то ОК, если нет, то — вывод соответствующего сообщения, например).

Один из типичных примеров аннотаций для валидаций в коде выглядит так:
@Size(min=3, max=20, message="Username must be between 3 and 20 characters long.")
Когда я стал разбираться, как устроены аннотации для валидаций, то выяснилось, что, кроме обычных уже вещей, они содержат еще одну аннтоацию для аннотаций (вот заветное волшебное слово): @Constraint. После него и указывается файл, в котором и происходит соответствующая проверка:
@Constraint(validatedBy = BlaBlaBlaValidator.class)

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

Волшебное слово лучше всего описано здесь:
Spring MVC кастомная аннотация для валидации форм (http://devcolibri.com/2664)
и здесь:

Другие материалы по аннотациям:

1. Об аннотациях вообще:

 - The Java™ Tutorials. Lesson: Annotations (http://docs.oracle.com/javase/tutorial/java/annotations/index.html);
 - Аннотации в Java (http://www.quizful.net/post/annotations-in-java);
 - Аннотации в Java - Annotation Types (http://www.javenue.info/post/79);
 - Spring. Аннотации. Конфигурация с помощью аннотаций - 9 - The Basics of Spring Framework (Юрий Ткач) (https://www.youtube.com/watch?v=cIYUAKDnFo4&list=PL6jg6AGdCNaWF-sUH2QDudBRXo54zuN1t&index=9);
 - Аннотации в Hibernate(http://java-course.ru/student/book2/hibernate-annotation/);
 - Как написать свою Аннотацию в Java? (http://devcolibri.com/1253);

2. Об аннотации @Autowired (как пример):

 - Использование аннотации @Autowired в Spring 3 (http://www.seostella.com/ru/article/2012/02/12/ispolzovanie-annotacii-autowired-v-spring-3.html);

3. Валидация:

 -  Проверка данных формы с помощью аннотаций (@Size, @Email и др) в Spring MVC (http://www.seostella.com/ru/article/2012/06/21/proverka-dannyh-formy-s-pomoschyu-annotaciy-size-email-i-dr-v-spring-mvc.html);
 - Spring. Проверка введенных данных (http://spring-projects.ru/guides/validating-form-input/);
 - Как создать собственный валидатор для Sping MVC (http://www.javacore.ru/topic/96-mvc-spring.htm);
 - Проверка входных данных Spring (http://src-code.net/proverka-vxodnyx-dannyx-spring/).