Да ладно, че там сложного. Загрузил столбы с проводами на железнодорожную платформу, едешь и ставишь столбы по пути. Наличие дороги все сильно упрощает, даже если она изначально не строилась под электропоезда.
Нет не веган. Ем все съедобное, пью все жидкое. Смотрел как-то давно видео на ютубе, в котором какой-то мужик призывал не пить молоко и не есть хлеб. с тех пор стараюсь не пить молоко и молочные продукты, и не ем хлеб.
Так детям молоко как раз надо пить, им полезно. А вот взрослым нет, не полезно. Молоко провоцирует развитие остеопороза у взрослых, взрослым молоко и молочные продукты есть не нужно.
Я не про это понимание, я про понимание на кровне концепций и организации кода. Вот кстати хороший пример с библиотекой. Представьте, вы написали программу, разложили все по интерфейсам и объектам, придумали как это все должно быть организовано, реализовали. А потом вдруг решили использовать библиотеку, которая лезет в ваши объекты с помощью сопоставления с образцом, или рефлекшеном. Все, можно сказать ваш дизайн больше не ваш, вы вынуждены подстраиваться под то, как библиотека использует ваши объекты, под библиотечный дизайн кода, библиотека начинает диктовать вам как вам писать программу. И весь код превращается в кашу, потому что нет больше оригинального дизана. Никогда не используйте такие библиотеки, всегда делайте как минимум свои обертки, ну или свои библиотеки.
Сборщик мусора это другое дело, он же появился как замена, а не как дополнение. У программиста в Java нет выбора, использовать его или нет, он может его только настраивать.
Функциональное программирование — интересная штука. Не призываю всех переходить на него, но его элементы мигрируют во все обычные ЯП. И это прекрасно.
На самом деле ничего хорошего. Потому что теперь у программиста появляется выбор. Раньше ему приходилось добавлять новый функционал, добавляя новые интерфейсы, методы и классы, а теперь он может добавить новый функционал используя паттерн матчинг и внешние (по отношению к классам) функции. Из-за этого фукнционал размазывается по разным местам и становится непонятно что и где происходит, код становится менее понятный и менее поддерживаемый (т.н. write-only код).
А это плохо, особенно если в команде больше одного программиста, или высокая текучка.
Поэтому — пишите в функциональном стиле на функциональных языках, ненадо тащить это в меинстрим.
Можете прямо сегодня выпаивать их из своего смартфона. По идее все эти данные, которые они собрались собирать, уже есть у владельцев платформ, Apple и Google. Видимо с Российскими компаниями они этими данными не делятся, вот и решили сделать свое следящее приложение.
Как вариант. Особенно если мы бы хотели сэкономить количество чтений EEPROM. Но с точки зрения упаковки всех настроек таймера в struct вообщем неважно есть там чтения пачки байт или нет, можно также int-ами читать, это все равно будет скрыто в функции чтения.
Крутизна, в 14 лет уже программы для микроконтроллеров пишете.
Я ничего не соображаю в электронике, но есть у меня пара советов по организации кода. Организация кода даже в таком маленьком проекте очень важна, программу будет проще понять и даже вы сами когда захотите добавить тот же датчик температуры через полгода, быстрее поймете что же там как работает.
Итак, во-первых и самое главное, в программе мало названий, относящихся к предметной области (управлению устройствами по таймерам), все названия очень асбтрактные и относятся к платформе (values, dititalWrite, EEPROM и т.п.). Нет даже ни одного названия timer. Из-за этого логика работы плохо понятна, даже в такой небольшой программе.
iarduino_RTC time(RTC_DS1307);
Лучше назвать эту переменную clock. На это кстати даже намекает название класса RTC (Real Time Clock)
int values;
Эта переменная используется только внутри функции loop, там её лучше и объявить. Глобальные переменные плохи тем, что их значение может меняться где угодно в программе, из-за этого разные функции оказываются слишком сильно связанны между собой, через глобальную переменную.
Кроме того, название во множественном числе values предполагает массив значений, а тут оно только одно, лучше value или data
Но в целом эта переменная вообще не нужна, ниже будет видно почему
unsigned int to_minutes(unsigned int hours, unsigned int minutes)//функция для перевода часов и минут в минуты
{
return (60*hours)+minutes;
}
Вот например хорошая функция, все понятно что она делает благодаря названиям переменных и самой функции. Ну а коментарий тут вообще ненужен, он ничего нового не объясняет, все итак понятно из кода
if(Serial.available() > 0) //если что-то пришло
Опятьже, такие коментарии объясняющие очевидное только увеличивают размер текста необходимый для прочтения и не несут полезной информации
Функция loop и делает слишком много работы, и устанавливает настройки с телефона в энергонезависимую память, и включает/выключает устройства по таймерам. Лучше разбить её на две, а лучше три или четыре
Например
void storeTimers()
{
for (int i = 0 ; i < EEPROM.length() ; i++)
EEPROM.write(i, 0);
unsigned int timers = 0;
while (Serial.read() > 0) { //пока есть что получать
delay(50);
for(int i = 0; i < 7; ++i) {
EEPROM.write(timers * 7 + i, Serial.read());
delay(50);
}
++timers;
EEPROM.write(EEPROM.length() - 1, timers);
}
}
Как уже правильно написали, логика записи настроек таймеров не очень понятна, скорее всего работает запись настроект только одного таймера.
Далее, комментарий типа
он как раз и написан потому что без него вообще непонятно чего к чему. Но, если добавить функцию с подходящим названием, коментарий становится ненужен. Вот например так
pinMode(EEPROM.read(i * 7 + 5), OUTPUT); //настраиваем пин таймера как выход
digitalWrite(EEPROM.read(i * 7 + 5), EEPROM.read(i * 7 + 6)); //посылаем на пин нужное значение
#include <Wire.h>
#include <iarduino_RTC.h>
#include <EEPROM.h>
iarduino_RTC clock(RTC_DS1307);
unsigned int to_minutes(unsigned int hours, unsigned int minutes)
{
return (60*hours)+minutes;
}
void setup()
{
time.begin();
Serial.begin(9600);
}
// читает текущее время из последовательного порта
void storeTime()
{
clock.settime(0, -1, Serial.read());
delay(50);
clock.settime(0, Serial.read());
}
// читает настройки таймеров из последовательного порта и сохраняет в энергонезависимой памяти
void storeTimers()
{
for (int i = 0 ; i < EEPROM.length() ; i++)
EEPROM.write(i, 0);
unsigned int timers = 0;
while (Serial.read() > 0) {
delay(50);
for(int i = 0; i < 7; ++i) {
EEPROM.write(timers * 7 + i, Serial.read());
delay(50);
}
++timers;
EEPROM.write(EEPROM.length() - 1, timers);
}
}
bool enabled(int timer)
{
return EEPROM.read(timer * 7) == 1;
}
bool ready(int timer)
{
return (to_minutes(EEPROM.read(timer * 7 + 1), EEPROM.read(timer * 7 + 2)) <= to_minutes(clock.Hours, clock.minutes)) &&
(to_minutes(EEPROM.read(timer * 7 + 3), EEPROM.read(timer * 7 + 4)) > to_minutes(clock.Hours, clock.minutes));
}
void set(int timer, bool invert)
{
pinMode(EEPROM.read(timer * 7 + 5), OUTPUT);
unsigned int val = EEPROM.read(timer * 7 + 6);
digitalWrite(EEPROM.read(timer * 7 + 5), invert ? !val : val);
}
void loop()
{
if(Serial.available() > 0) {
storeTime();
storeTimers();
}
//проход по каждому таймеру
int timers = EEPROM.read(EEPROM.length() - 1);
for(int timer = 0; timer < timers; ++timer) {
if(enabled(timer)) {
clock.gettime();
if(ready(timer))
set(timer, false);
else {
bool found = false;
//ищем таймер с таким же пином
for (int t = 0; t < timers && !found; ++t) {
if ((t != timer) && enabled(t) && ready(t) &&
EEPROM.read(timer * 7 + 5) == EEPROM.read(t * 7 + 5)) {
set(t, false)
found = true;
}
}
if(!found)
set(timer, true);
}
}
}
}
Примерно так. Такой код намного легче понять и в нем не требуются коментарии, потому что все функции маленькие и имеют названия из предметной области. Надеюсь будет полезно, удачи.
PS: Имя, ник и теги огонь
сопоставление с образцом используется в конструировании потока управления данными, например. писать отдельный класс под каждый промежуточный вариант — та еще тухлятина.
Сопоставление с образцом используется в функциональных языках, потому что в них нет понятия инкапсуляции и типов (в том понимании в котором они используются в Java например).
В Java да, именно что под каждый промежуточный вариант надо делать свой класс. Так принято в Java. Но ненадо в Java тащить чужеродные концепции. Если не умеете программировать на Java, и очень хочется использовать pattern matching — идите в Haskel, Lisp, Scala и т.п.
примеры из статьи — искуственные и показывают что можно делать (но отнюдь не что нужно).
Приведите нормальный пример, который доказывает нужность этой концепции и этой библиотеки. Пока что все абсолютно примеры доказывают её вредность и ненужность.
Ну может вот этот только можно использовать
switch (side, width) {
case "top", 25 -> System.out.println("top");
case "bottom", 30 -> System.out.println("bottom");
case "left", 15 -> System.out.println("left");
case "right", 15 -> System.out.println("right");
case null -> System.out.println("Null value ");
default -> System.out.println("Default value ");
};
Совершенно независимо от качества примеров автора (и ограниченности данной библиотеки), вы похоже не понимаете, что такое pattern matching, и зачем он нужен.
Я прекрасно понимаю что это за паттерн и зачем он нужен. И что он принципиально не совместим с Java.
Не было бы. Представьте на мгновение, что figure — это не ваши классы, что они написаны сторонним разработчиком, и менять (а то и посмотреть на их код) вы не можете.
В этом случае тем более никак и никогда не могу использовать паттерн матчинг, потому что все что я вижу из библиотеки — это интерфейсы. я не вижу внутренностей объектов. Попытка использовать тяжелую артиллерию вроде рефлекшена для того чтобы подсмотреть внутренности объектов в библиотеке — это ядерная бомба замедленного действия. В следующей версии библиотеки автор поменяет свои объекты, и весь паттерн матчинг рухнет как карточный домик.
Если я хочу что-то добавить к коду библиотеки, я оборачиваю библиотечные классы своими классами, но никак не лезу рефлекшеном внутрь библиотечных классов.
Вы просто не понимаете суть ООП, если для вас добавление чего-то к библиотечному коду это обязательно означает разобрать объект на кусочки.
Но никак не так как в примерах из статьи. Тоесть, либо одно, либо другое, вместе никак.
Ну а если в программе возникает необходимость писать подобный код
switch (data) {
case Integer i -> System.out.println(i * i);
case Byte b -> System.out.println(b * b);
case Long l -> System.out.println(l * l);
case String s -> System.out.println(s * s);
case null -> System.out.println("Null value ");
default -> System.out.println("Default value: " + data);
};
это значит лишь то что объектно-ориентированный дизайн зашел куда-то совсем не туда. Ну или это код внутри самой JDK, что врядли.
На самом деле ничего хорошего. Потому что теперь у программиста появляется выбор. Раньше ему приходилось добавлять новый функционал, добавляя новые интерфейсы, методы и классы, а теперь он может добавить новый функционал используя паттерн матчинг и внешние (по отношению к классам) функции. Из-за этого фукнционал размазывается по разным местам и становится непонятно что и где происходит, код становится менее понятный и менее поддерживаемый (т.н. write-only код).
А это плохо, особенно если в команде больше одного программиста, или высокая текучка.
Поэтому — пишите в функциональном стиле на функциональных языках, ненадо тащить это в меинстрим.
Я ничего не соображаю в электронике, но есть у меня пара советов по организации кода. Организация кода даже в таком маленьком проекте очень важна, программу будет проще понять и даже вы сами когда захотите добавить тот же датчик температуры через полгода, быстрее поймете что же там как работает.
Итак, во-первых и самое главное, в программе мало названий, относящихся к предметной области (управлению устройствами по таймерам), все названия очень асбтрактные и относятся к платформе (values, dititalWrite, EEPROM и т.п.). Нет даже ни одного названия timer. Из-за этого логика работы плохо понятна, даже в такой небольшой программе.
Лучше назвать эту переменную clock. На это кстати даже намекает название класса RTC (Real Time Clock)
Эта переменная используется только внутри функции loop, там её лучше и объявить. Глобальные переменные плохи тем, что их значение может меняться где угодно в программе, из-за этого разные функции оказываются слишком сильно связанны между собой, через глобальную переменную.
Кроме того, название во множественном числе values предполагает массив значений, а тут оно только одно, лучше value или data
Но в целом эта переменная вообще не нужна, ниже будет видно почему
Вот например хорошая функция, все понятно что она делает благодаря названиям переменных и самой функции. Ну а коментарий тут вообще ненужен, он ничего нового не объясняет, все итак понятно из кода
Опятьже, такие коментарии объясняющие очевидное только увеличивают размер текста необходимый для прочтения и не несут полезной информации
Функция loop и делает слишком много работы, и устанавливает настройки с телефона в энергонезависимую память, и включает/выключает устройства по таймерам. Лучше разбить её на две, а лучше три или четыре
Например
Как уже правильно написали, логика записи настроек таймеров не очень понятна, скорее всего работает запись настроект только одного таймера.
Далее, комментарий типа
он как раз и написан потому что без него вообще непонятно чего к чему. Но, если добавить функцию с подходящим названием, коментарий становится ненужен. Вот например так
Соответственно проверка превращается в
Гораздо понятнее и не требуется коментарий
Далее, повторяющиеся конструкции вроде
и
тоже вынесем в функции
Итого получается следующий код
Примерно так. Такой код намного легче понять и в нем не требуются коментарии, потому что все функции маленькие и имеют названия из предметной области. Надеюсь будет полезно, удачи.
PS: Имя, ник и теги огонь
Сопоставление с образцом используется в функциональных языках, потому что в них нет понятия инкапсуляции и типов (в том понимании в котором они используются в Java например).
В Java да, именно что под каждый промежуточный вариант надо делать свой класс. Так принято в Java. Но ненадо в Java тащить чужеродные концепции. Если не умеете программировать на Java, и очень хочется использовать pattern matching — идите в Haskel, Lisp, Scala и т.п.
Приведите нормальный пример, который доказывает нужность этой концепции и этой библиотеки. Пока что все абсолютно примеры доказывают её вредность и ненужность.
Ну может вот этот только можно использовать
Я прекрасно понимаю что это за паттерн и зачем он нужен. И что он принципиально не совместим с Java.
В этом случае тем более никак и никогда не могу использовать паттерн матчинг, потому что все что я вижу из библиотеки — это интерфейсы. я не вижу внутренностей объектов. Попытка использовать тяжелую артиллерию вроде рефлекшена для того чтобы подсмотреть внутренности объектов в библиотеке — это ядерная бомба замедленного действия. В следующей версии библиотеки автор поменяет свои объекты, и весь паттерн матчинг рухнет как карточный домик.
Если я хочу что-то добавить к коду библиотеки, я оборачиваю библиотечные классы своими классами, но никак не лезу рефлекшеном внутрь библиотечных классов.
Вы просто не понимаете суть ООП, если для вас добавление чего-то к библиотечному коду это обязательно означает разобрать объект на кусочки.
В ООП это было бы
или
Но никак не так как в примерах из статьи. Тоесть, либо одно, либо другое, вместе никак.
Ну а если в программе возникает необходимость писать подобный код
это значит лишь то что объектно-ориентированный дизайн зашел куда-то совсем не туда. Ну или это код внутри самой JDK, что врядли.