Comments 60
паскаль не так плох чтобы его выбрасывать
Добавь информацию, где люди смогут более подробно узнать о Delphi. Книги, ссылки. Ссылки на форумы, где можно получить необходимую информацию для людей и где им смогут помочь, если встретятся с какими-то проблемами.
Из книг (многие подустарели, но актуальной информация, думаю, остаётся по сей день):
Дмитрий Осипов "Delphi XE2", 2012
Всеволод Леонов "Обучение мобильной разработке на Delphi", 2014-1015
Рубанцев Валерий "Большой самоучитель Delphi XE3", 2013
Дмитрий Осипов "Delphi. Программирование для Windows, OS X, iOS и Android", 2014
Была вроде недавно какая-то книга выпущена, но уже не помню какая.
вот это охуенно:
Многопоточное программирование в Delphi для начинающих
Автор: Логинов Д.С.
https://github.com/loginov-dmitry/multithread/blob/master/multithread_in_delphi_for_beginners.md
Обучение мобильной разработке на delphi всеволод леонов 2-e издание. Интересная книга по разработке андроид приложений на delphi.
Заходите в телеграмм-чаты, один из них https://t.me/Delphi_Lazarus, людей хватает, постоянно какой-то движ в чате
https://delphiworld.narod.ru/
Офигеть! Зашел, а он еще работает с 2002 :)
Как работает под линуксом? Для альт линукс приложения можно делать?
А в Delphi уже есть деревья выражений как в .net (но без .net)?
Лямбда как таковых нет, только анонимные функции и процедуры. А лямбды я так понимаю их часть. Так что, вероятно ответ нет. Или может я не так понимаю "деревья выражений".
Это следующий шаг после лямбд, который сделал C#.
В данном выражении mydb.customers.Where(x => x.Name == "John")
функция внутри скобок - не лямбда (т.е. не обычная функция, скомпилированная в код), а так называемое "дерево выражений" - это AST, который компилятор преобразовал в некоторый объект, данные которого доступны к анализу в рантайме, и его можно использовать во фрейморках, рекурсивно проходя по структуре выражения и транслируя его в SQL, например. Тут нужна серьёзная поддержка со стороны компилятора, но и возможности можно получить интересные.
Это типа такого, что ли?
Вот я пишу кодом Delphi кодом SQL запрос, а на выходе получаю готовый SQL и набор параметров
Мы можем использовать любые операторы, например, сложение, вычитание, деление и т.д., но вычисляться они не будут
Да, это оно. Это рабочий пример?
Интересно, как оно реализовано.
Например, параметр функции Where имеет какой тип?
Здесь работает на "перегрузке операторов". Тип параметра Where (или любого условия) - TSQLCondition, который просто перекрывает все операторы (включая приведение) и записывает в себя информацию при операции.
Реализация очень простая. За пару вечеров реализовал, когда идея в голову пришла
Это даже не класс, а просто структура. А структура эта - это поле для орм. Т.е. описываем класс таблицы из полей нужных типов.
Для орм все равно описываем класс таблиц и связи. Эти же типы позволяют и вот это реализовать
Я так понимаю, поле Name должно быть не как в БД - string, а свой тип, чтобы перегрузить оператор =
Значит, классы моделей надо дублировать - для генератора запросов своё, для загрузки в них записей из БД - своё?
А ещё непонятно, в выражении Where(User.Name = 'Dan')
что есть User
?
Как будто глобальная переменная, иначе как в этом контексте это становится синтаксически корректно.
Целиком весь тестовый проект
program SQLG;
{$APPTYPE CONSOLE}
{$R *.res}
uses
System.SysUtils,
System.TypInfo,
System.Rtti,
SQLG.Condition in '..\SQLG.Condition.pas',
SQLG.CreateTable in '..\SQLG.CreateTable.pas',
SQLG.Field in '..\SQLG.Field.pas',
SQLG.Params in '..\SQLG.Params.pas',
SQLG.Select in '..\SQLG.Select.pas',
SQLG.Table in '..\SQLG.Table.pas',
SQLG.Types in '..\SQLG.Types.pas';
type
[TableName('user')]
TUser = class(TSQLTable)
[FieldName('id'), UNIQUE, PRIMARYKEY]
Id: TFGUID;
[FieldName('role_id')]
RoleId: TFGUID;
[FieldName('status')]
Status: TFInteger;
[FieldName('name')]
Name: TFString;
end;
[TableName('user_role')]
TUserRole = class(TSQLTable)
[FieldName('id')]
Id: TFGUID;
[FieldName('type')]
RoleType: TFInteger;
[FieldName('desc')]
Desc: TFString;
[FieldName('name'), LENGTH(20)]
Name: TFVARCHAR;
end;
procedure Test;
begin
var User := TUser.Create;
var UserRole := TUserRole.Create;
var Params: TSQLParams;
var SubQuery := Select('*').From(UserRole).Where(UserRole.RoleType = 1);
var Sel :=
Select([User, UserRole.Desc.Table('ur').&As('description')]).
From(User).
LeftJoin(SubQuery, 'ur').On(User.RoleId = UserRole.Id.Table('ur')).
Where(not (User.Id = TGUID.NewGuid) or (User.Status in [1, 2, 3])).
Where(User.Status and 1 = 0).
Where(User.Name = 'Dan').
Where(User.Status in
Select(User.Status).
From(User).Where(User.RoleId in [TGUID.NewGuid, TGUID.NewGuid])).
OrderBy([User.Name, DESC(User.Status)]).
GroupBy(User.Id);
writeln(Sel.Build(Params));
writeln;
for var Param in Params do
writeln(Param.Key, ': ', Param.Value.TypeInfo.Name, ' = ', Param.ToString);
Params := [];
Sel :=
Select('*').From(
Select('*').From(UserRole).&As('asd')
);
writeln(Sel.Build(Params));
writeln;
for var Param in Params do
writeln(Param.Key, ': ', Param.Value.TypeInfo.Name, ' = ', Param.ToString);
User.Free;
UserRole.Free;
end;
begin
try
Test;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
Readln;
end.
User - класс, его можно один раз за программу инициализировать и использовать для построения запросов. Лучше, наверно, придумать класс "схема", который будет управлять таблицами.
Этот же класс можно использовать для получения данных из БД. Поля могут хранить значение (User.Name.Value).
Но в Delphi удобнее использовать датасеты, тем более что запросы будут выдавать разный набор полей.
Т.е. мы используем эти классы и структуры для создания и миграции БД. Создаем нужные нам запросы (при изменении чего-либо будут синтаксические ошибки в запросах, т.е. сразу видно, где нужно поправить). Отдаем эти запросы на выполнение и получаем датасеты. А датасеты могут быть с частичной подгрузкой (т.е. не сразу млн. записей, а только часть с автоматической догрузкой).
Для орм все равно описываем класс таблиц и связи
Best Practices для современных ORM, типа Entity Framework, рекомендуют описывать "чистые" классы данных, без всякой ORM-разметки (так их можно переиспользовать например в клиентских проектах, не таща на фронт-энд в зависимости ORM-либы), а разметку выносить в отдельный модуль, который подключен только к бекэнду.
https://learn.microsoft.com/en-us/ef/core/modeling/
Fluent API вместо Data Annotation.
Это всё понятно, но эти поля только методы хранят и ничего более. Ну и, конечно же, само значение. Т.е. их можно использовать в клиенте. Именно так это и подразумевалось делать. Мы описываем классы таблиц в отдельных модулях, или даже целую схему. А сервер и клиент их используют. Один для генерации запросов, второй для получения данных.
Но мне больше нравится подход, когда мы получаем только нужные данные, т.е. именно датасетами. Именно те поля и именно нужной мне вложенности. Это работает быстрее, чем целиком ОРМ в её понимании.
У нас, как правило не исключительно CRUD на серверах, а наоборот, CRUD там в меньшинстве. Преобладают уникальные запросы на сервер. И сервер, на такие запросы тратит куда меньше времени и ресурсов, чем, если использовался ОРМ. БД принимает точный SQL запрос, который ещё и может быть подготовлен заранее (т.е. не строиться каждый раз), и только нужно передать нужный набор параметров. Другими словами, используя мой подход, я могу при старте сгенерировать нужные запросы один раз и только принимать разные параметры. В ответ датасет с только нудными данными, которые уже передаются на клиент в нужном ему виде.
Т.е. их можно использовать в клиенте. Именно так это и подразумевалось делать. Мы описываем классы таблиц в отдельных модулях, или даже целую схему
Ну имеется ввиду, что uses SQLG.Field, SQLG.Table
хочется вообще убрать с клиента. Хотя, учитывая скорость компиляции Delphi и то, что компилятор вырежет всё лишее, не должно быть проблемой.
Но мне больше нравится подход, когда мы получаем только нужные данные, т.е. именно датасетами
В EF тоже с этим нет проблем, под это даже придумали концепцию "анонимных классов". То есть, язык сильно расколбасили конкретно под ORM-ы и работу с данными
var clientShortInfo = dbContext.Orders
.Where(order => order.Date < DateTime.Today.AddDays(-2))
.Select(order => new { order.Id, order.Client.Fio, order.Client.City.Name });
и будет конкретный SQL select с указанными полями и join-ами. Но при этом более эффективный по памяти, т.к. используется анонимный класс с конкрентыми типизированными полями, а не абстрактный dataset, у которого все поля типа Variant, и плюс сверху - метаинформация.
Вы немного не правы. Датасеты хранят данные в сыром виде. Внутри там, буквально, указатели на данные. Variant там как способ получить данные. Т.е. датасет имеет доступ к области памяти, где в сыром виде доступны значения из бд. И, в зависимости от того, как ты хочешь забрать, данные в нужном виде забираются из памяти. Так что, где ещё экономнее, можно поспорить. А мета информация хранится в одном экземпляре на весь класс, т.е. не дублируется. Т.е. в конечном итоге, мы имеем данные из бд в чистом виде и не перекладываем их, кроме как когда сериализуем их.
Было бы интересно узнать про отличия VCL от FireMonkey. А также плюсы и минусы последнего.
Коснусь этого, когда буду писать вторую часть.
На самом деле, отличается кардинально. Все контролы полностью реализованы на Делфи и соответственно платформонезависимые, однако, некоторые контролы являются "presentable", т.е. имеют общую модель данных, но могут иметь разные представления (реализации). А именно: fmx-реализацию (как и другие контролы) и нативную реализацию. Например, TEdit может быть как чисто fmx, а может быть виндовым нативным или андроид нативным.
Когда контрол для разных платформ имеет разную реализацию - это тот же LCL из Lazarus. В fmx же разная нативная реализация - лишь опция для некоторых контролов.
В VCL каждый контрол - отдельное окно и отдельный канвас. В fmx, только один общий канвас. Конечно, если контрол не переключен в режим "нативность". Но это исключение, опция. Мало кто этим пользуется. Потому что, если контрол нативный и имеет свой канвас, значит он находится над общим канвасом и всегда будет поверх остальных не нативных контролов.
В fmx контрол - лишь набор данных и событий. При первом появлении на "сцене", контрол запрашивает свое представление. Как правило это стиль из пула стилей. Стиль может состоять из чего угодно, но в конечном итоге - это графические примитивы, которые и рисуются. Каждый контрол, при применении стиля или изменении своих данных, ищет внутри стиля "особые" элементы. Например, кнопка будет искать элемент текстового типа с названием "text", куда будет передавать значение из своего свойства "Text" (подпись кнопки) и настройки текста этой кнопки.
Однако, никто не запрещает с стиле иметь и другие элементы, о которых кнопка не знает. А ко всем элементам стиля каждого контрола можно обращаться из кода.
Всё это позволяет убрать ограничения на то, как контрол будет выглядеть и даже на то, что может контрол изначально. Внутрь стиля кнопки мы можем поместить контрол кнопки и будет кнопка в кнопке (SplitButton). К внутренней кнопке мы тоже можем обращаться и назначать свойства и события.
Теперь про отличия, т.к. мы знаем весь бэкграунд.
Теперь мы можем без проблем делать любую полупрозрачность, т.к. канвас у нас один.
А т.к. в fmx окно по умолчанию "слоистое" в Винде, то мы можем убирать фон окна при этом оставляя полупрозрачность элементов окна. Мы можем даже 3D сцену иметь без фона и рисовать 3D объекты как они есть (Wallpaper Girl 3D теперь очень легко делать =)).
Терпеть, т.к. у нас только у окна есть Handle - только оно может получать события от винды. И именно окно генерирует события для контролов.
Другими словами, fmx ближе к графическим движкам, типа Unity.
Канвас, в fmx, кстати тоже имеет разные реализации. Несколько реализаций для винды и по 1-2 для других платформ. Канвас может быть d2d, directx, opengl, opengles, opengl (иосовский), metal, skia и т.д.
Delphi еще жив? последний раз о нем слышал в 2010
Delphi жив и развивается, вдобавок на нём даже все ещё работают и есть компании которые ищут себе сотрудников. Плюс в сам язык вносят множество вещей. Появилась(но уже давно) разработка под IOS и Android
Некогда ему умирать. Да и регулярные обновления, вебинары, нововведения и прочее, не дают.
zloyreznic еще жив? никогда не слышал о нём :-)
Мобильные приложения, кстати, на Delphi создаются быстрее, чем на Kotlin. И для мобильных приложений в Delphi имеется два фреймворка: штатный фреймворк FMX (о котором статья), не нативный (может быть частично нативным) и FGX Native отдельный, специально разработанный для создания мобильных приложений.
есть ссылка на какой то opensource проект? даже интересно стало
Мобильные приложения, кстати, на Delphi создаются быстрее
До тех пор, пока не надо взаимодействовать с java-библиотеками. А то разработчик каких-нибудь смарт-часов выдаст в виде набора jar-файлов свою SDK, где надо регистрировать ресиверы в AndroidManifest и реализовывать их в виде своих классов, отнаследованных от его библиотечных, и приехали. Ну или какой-то клиент закапризничает и захочет нативный контрол, который 100 лет есть в XML-разметке Android Layout, но в FMX его нет / выглядит иначе.
Проблем с java классами нет. Реализовывать их не сложно. И, jar библиотеки легко подключаются к проекту.
Контролов в fmx куда больше, чем в нативной разметке, а если надо именно нативный, то и его можно добавить кодом. Не говоря уже о том, что любой кастомный (или просто особый) контрол очень легко реализуется стилями.
В fmx есть прямой доступ к view и соответственно можно что угодно туда создавать.
Я реализовывал целиком стиль Material Design 3, со всеми присущими ему контролами.
Не понимаю, как это работает, если у java свой runtime. Банально, тип string от java не совпадает со string от delphi.
Есть дженерик врапперы. Это все давно решено. Собственно на jar классах и работает реализация фреймворка fmx для андроид.
Вот примерно так это выглядит. В этот список мы можем добавлять и другие классы.
Добавить-то понятно можно. Оно без изменений перейдёт в apk-файл после компиляции.
Интересно, как это вызывается. Как создать класс из jar в delphi-синтаксисе, можно ли от него отнаследоваться, чтобы получить delphi-класс.
Это просто. Вот пример. Описываем интерфейс и указываем java класс.
Как видишь, строка тут JString. Но она имеет явные и неявные приведения, так что работа с ней такая же простая.
Ниже описание класса на основе базового враппера
Молодцы, провели большую работу по интеграции.
Главное, чтобы у них хватило запала поддерживать. А то новые версии Android пекутся каждый год, как горячие пирожки. И выйдет, как в Delphi в Windows - Microsoft выпустило DX SDK версии 9, на в комплекте с Delphi портированные хидеры только на DX5.
При чем, это автогенерация. Есть штатные инструменты конвертации.
Помню была (и наверное еще есть) такая библиотека KOL/MCK, как альтернатива VCL.
Даже писал на ней немного — минимализм высшей категории:
https://ru.wikipedia.org/wiki/KOL
Перестали её развивать почти сразу как выпустили. Вероятно, тогда GUI для Linux были не так нужны, как сейчас, особенно под гнетом того, что Винду то тут, то там отменяют. Библиотека опередила своё время, к сожалению.
У «Борланда» был еще «Kylix», с VLC для Linux. Точнее, не VCL, а некоей адаптацией VCL под Linux. Убили после трех релизов. :-(
Пару недель назад купил Delphi Professional в качестве благодарности за многие годы использования Delphi 7 в прошлом. В работе Delphi уже давным-давно не применяю, но осталось много старых проектов (ностальгия). Так вот был неприятно удивлен качеством IDE по сравнению с современными IDEA (при цене в 1600$ за то, что я купил).
Начал с того, что попытался отформатировать код встроенным средством. Некоторые файлы отформатировались нормально, а некоторые поехали. То есть встроенная функция форматирования не поддерживает весь синтаксис языка, что удивительно (в моем случае всё сбивается после record в var).
При редактировании файлов (копирование, вставка, отмена действий) как-то постоянно неудобно сбивался курсор, но возможно дело привычки. Общее впечатление какой-то глючности и сырости.
Но хотя бы HighDPI уже нормально поддерживается средой. Пару лет назад скачивал бесплатную версию, там этого еще не было.
И с непривычки правда очень утомляют begin и end, хотя я много лет назад смеялся с этих претензий.
В общем, к Delphi очень теплые чувства, но IDE в плане удобства использования оставляет желать лучшего (как-то она не поспевает за современными требованиями к пользовательскому опыту). Может просто избалован IDEA. Больше 10 лет использовал Delphi 7, и в целом было нормально :)
Спасибо за статью. Интересно будет как-нибудь опробовать этот FireMonkey.
Попробуй Lazarus, в плане IDE. Правда под FireMonkey он не заточен...
сбивался курсор
Да, тут привыкать нужно, потому что перемещение каретки тоже считается изменением редактора и может быть отменено. Когда привыкнешь - наоборот удобнее становится. Видишь как скакал, как изучал код.
С форматированием могут быть какие-то косяки, его не особо правили после крупных изменений в языке (инлайн, вывод типов, анонимки и т.д.)
Но над IDE очень усердно работают в последнее время. И даже я, часто замечаю, что ту или иную вещь действительно поправили. Кстати, именно текущая бета версия, которая тестируется, является обновлением с уклоном на "правки и улучшения" в IDE.
Для Делфи все очень дорого, не только она сама. Сообщество к сожалению очень небольшое, по сравнению с другими. Если бы эмбаркадеро пустила разработку в open source и платила бы за нее, думаю это только пошло бы на пользу всем. Уж IDE энтузиасты бы запилили для своего удобства)
Писал на Delphi когда-то, своего рода революционная IDE для своего времени. А старое VCL-приложение на Delphi под Винду можно конвертировать в мультиплатформенное без больших переделок?
Кроссплатформенный фреймворк FireMonkey, Delphi