Как стать автором
Обновить

Подходы к кодогенерации

Время на прочтение 4 мин
Количество просмотров 6.3K
В предыдущей статье мы рассматривали то, как применяется кодогенерация в реальных продуктах.
В этой статье мы рассмотрим то, как какие подходы к кодогенерации могут облегчить нам жизнь.

Кодогенерация


Кодогенерация — это процесс генерации кода на основе определенных данных.

Я бы хотел выделить некоторые направления:

Генерация кода на основе более высокоуровневого


Этот подход нам позволяет получить на основе простого интуитивно понятного кода — код более низкого уровня. Такой подход помогает сохранить абстракцию. Язык, на котором написан исходный код, может не совпадать с языком кода, который будет сгенерирован.

Например, мы пишем сайт на каком-то своем языке. А на сервере у нас стоит PHP. Давайте посмотрим какая теоретически возможна генерация.
user = Users.find(5);
user.lastActivity = DateTime.Now;
user.save();

Мы сконфигурировали сайт на использование MySQL и можем получить на выходе подобный код:
$user_query = mysql_query('SELECT * FROM `users` WHERE `id`=5', $mysql_connection);
$user = mysql_fetch_assoc($user_query);
mysql_query('UPDATE `users` SET `lastActivity`='.time().' WHERE `id`='.$page['id'], $mysql_connection);

А если кодогенератор умный, то он может сгенерировать нечто такое:
mysql_query('UPDATE `users` SET `lastActivity`='.time().' WHERE `id`=5', $mysql_connection);

Вдруг мы решили не использовать MySQL, а использовать, например, прямую запись в файлы, то код может быть таким:
$user_list = unserialize(file_get_contents('users.txt'));
foreach ($user_list as $current_user)
{
  if ($current_user->id == 5)
  {
    $user = $current_user;
    break;
  }
}
$user->lastActivity = time();
file_put_contents('users.txt', serialize($user_list));


Еще один пример. Мы пишем на своем языке:
deletedRowsCount = query: delete from myTable where id>5;

В случае использования MySQL, мы могли бы получить такой код:
$query = mysql_query('DELETE FROM `myTable` WHERE `id`>5', $mysql_connection);
$deletedRowsCount = mysql_affected_rows($mysql_connection);

В случае использования файлов, мы могли бы получить такой код:
$deletedRowsCount = 0;
$myTable_list = unserialize(file_get_contents('myTable.txt'));
foreach ($myTable_list as $key_myTable=>$current_myTable)
{
  if ($current_myTable->id > 5)
  {
    unset($myTable_list[$key_myTable]);
    $deletedRowsCount++;
  }
}
file_put_contents('myTable.txt', serialize($myTable_list));


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

Хочу так же привести пример работы реального кодогенератора, работающего по такому принципу. Это кодогенератор LINQ.

Исходный код:
byte[] source = new byte[] { 1, 5, 7, 4, 3, 9, 8, 2, 6 };
var dest = from n in source where n > 5 select n;


Результирующий код:
IEnumerable<byte> dest = Enumerable.Where<byte>(new byte[] { 1, 5, 7, 4, 3, 9, 8, 2, 6 }, new CS$<>9__CachedAnonymousMethodDelegate1(Main_b__0));

Генерируется так же делегат:
[CompilerGenerated]
private static Func<byte, bool> CS$<>9__CachedAnonymousMethodDelegate1;

И метод:
[CompilerGenerated]
private static bool Main_b__0(byte n)
{
  return (n > 5);
}


Генерация на основе метаданных


Этот подход позволяет нам создавать код работы с данными на основе описания структуры данных (метаданных). Например, у нас есть БД MySQL. Я хочу сгенерировать сущности для работы с БД. Наиболее простые данные о структуре БД получить легко:
show tables;
+---------------+
| Tables_in_gen |
+---------------+
| users         |
+---------------+

describe users;
+-------+--------------+------+-----+---------+----------------+
| Field | Type         | Null | Key | Default | Extra          |
+-------+--------------+------+-----+---------+----------------+
| id    | int(11)      | NO   | PRI | NULL    | auto_increment |
| name  | varchar(100) | YES  |     | NULL    |                |
+-------+--------------+------+-----+---------+----------------+

Из этого можно сгенерировать примитивный класс хранения информации о пользователе.
class User
{
  public $id;
  public $name;
}


Если подумать, то можно еще реализовать методы select, update, insert, delete. В будущем мы столкнемся с подобными кодогенераторами и даже рассмотрим разработку подобного.

Кодогенерация на основе шаблонов


Это один из простых и повседневных подходов к кодогенерации. Пример такой системы — Smarty. Эта система используется в основном для генерации HTML кода на основе шаблонов. В ходе экспериментов я генерировал PHP и C# код.

Например, у нас в неком источнике $source хранится список полей и нам надо на их основе сделать класс со свойствами.

Шаблон можно взять такой:
class MyClass
{
{{foreach from=$source item=name}}
  public ${{$name}};
{{/foreach}}
}

И при $source = array('width', 'height', 'name') мы получим:
class MyClass
{
  public $width;
  public $height;
  public $name;
}


В следующей статье мы рассмотрим то как можно управлять сложностью с помощью кодогенерации.
Теги:
Хабы:
+17
Комментарии 53
Комментарии Комментарии 53

Публикации

Истории

Ближайшие события

Московский туристический хакатон
Дата 23 марта – 7 апреля
Место
Москва Онлайн