В предыдущей статье мы рассмотрели некоторые подходы к кодогенерации, теперь я хочу взглянуть на многоуровневую абстракцию и произвести некоторый анализ.
Данная статья содержит лишь теорию. Практической будет следующая статья (постараюсь чередовать).
Многоуровневая абстракция — разделение компонента приложения на несколько уровней абстракции так, что на каждом уровне абстракция согласована. Это несколько заумно может звучать. Суть в том, чтобы разделить компонент на несколько уровней, таким образом, чтобы мы могли относительно автономно работать с данным уровнем в его абстракции и не держать в голове информацию о других уровнях.
1. Борьба со сложностью. На каждом уровне применяются методы именно данного уровня.
2. Уменьшение связности.
3. Обеспечение взаимозаменяемости классов на всех уровнях кроме верхнего.
Идем по убыванию уровня абстракции:
* Класс-сущность реального мира
* Провайдер данных
* Реальные библиотеки работы с данными
Пример:
* User
* IUserProvider, SqlUserProvider, XmlUserProvider,…
* SqlClient, XmlDocument,…
При этом мы получаем низкую связность: User знает про интерфейс IUserProvider, SqlUserProvider и XmlUserProvider выполняют IUserProvider и пользутся библиотеками SqlClient и XmlDocument. Более того, объекты определенного уровня абстракции работают только с объектами следующего (нижнего) уровня абстракции, но никак не наоборот, что не допускает циркулярных связей.
1. Обычно в одном проекте несколько многоуровневых абстрактных моделей. Проблемы возникают если несколько абстрактных моделей надо подвергнуть однообразным изменениям. При этом приходится вносить изменения во все промежуточные уровни абстракции включая верхний.
2. При прототипировании накладные расходы на проектирование многоуровневой абстрактной модели могут быть слишком высоки и возможно написание временного кода без уровней абстракции, который придется выкинуть после утверждения прототипа.
3. Абстракция от источников данных может породить (и порой порождает) неоптимальную работу с источниками данных.
Кодогенерация во многих случаях (не всегда) может заменить многоуровневую абстракцию. При этом будут генерироваться конечные классы (из верхнего уровня абстракции), содержащие в себе методы работы с выбранным источником данных.
1. Имея в основе метаданные, мы можем вносить изменения в алгоритмы генерации кода и разом модифицировать всю модель.
2. При наличии метаданных прототип модели можно получить в кратчайший срок.
3. За счет наличия генераторов для каждого источника данных, модель будет сгенерирована с приемлемой оптимальностью работы с выбранным источником данных, учитывая его специфику.
Сложные приложения всегда задают много вопросов. По моим наблюдениям, на бОльшую часть из них можно ответить еще в процессе разработки (например, на сайте нужно кеширование или нет; какая операционная система будет на сервере; использовать буфферизацию вывода или нет...). Если мы ответим на этим вопросы заранее — мы можем избежать лишней сложности программы, лишних действий, лишних проверок и т.п. Более того, кодогенератор сам может собрать в среде назначения некоторые данные заранее, которые он может использовать для оптимизации работы.
Но это не значит, что меньше результирующего кода = проще система. Кодогенератор сам должен быть достаточно качественный для того, чтобы генерировать качественный код.
Данные подходы не противоречат друг другу и могут использоваться совместно. Например, в ASP.NET существует система хранения личных данных пользователей в профилях (Profile). Там выстроена абстрактная модель с несколькими уровнями абстракции, а сверху лежит ProfileBase. Если список свойств мы зададим в конфигурации, то будет сгенерирован потомок класса ProfileBase — ProfileCommon, который будет содержать те свойства, которые мы указали в конфигурации. Фактически, в конфигурации мы указали метаданные.
В следующей статье мы разработаем определенный несложный кодогенератор.
Данная статья содержит лишь теорию. Практической будет следующая статья (постараюсь чередовать).
Многоуровневая абстракция
Многоуровневая абстракция — разделение компонента приложения на несколько уровней абстракции так, что на каждом уровне абстракция согласована. Это несколько заумно может звучать. Суть в том, чтобы разделить компонент на несколько уровней, таким образом, чтобы мы могли относительно автономно работать с данным уровнем в его абстракции и не держать в голове информацию о других уровнях.
Зачем вообще делят на уровни абстракции?
1. Борьба со сложностью. На каждом уровне применяются методы именно данного уровня.
2. Уменьшение связности.
3. Обеспечение взаимозаменяемости классов на всех уровнях кроме верхнего.
Многоуровневая абстракция работы с данными
Идем по убыванию уровня абстракции:
* Класс-сущность реального мира
* Провайдер данных
* Реальные библиотеки работы с данными
Пример:
* User
* IUserProvider, SqlUserProvider, XmlUserProvider,…
* SqlClient, XmlDocument,…
При этом мы получаем низкую связность: User знает про интерфейс IUserProvider, SqlUserProvider и XmlUserProvider выполняют IUserProvider и пользутся библиотеками SqlClient и XmlDocument. Более того, объекты определенного уровня абстракции работают только с объектами следующего (нижнего) уровня абстракции, но никак не наоборот, что не допускает циркулярных связей.
Когда возникают проблемы?
1. Обычно в одном проекте несколько многоуровневых абстрактных моделей. Проблемы возникают если несколько абстрактных моделей надо подвергнуть однообразным изменениям. При этом приходится вносить изменения во все промежуточные уровни абстракции включая верхний.
2. При прототипировании накладные расходы на проектирование многоуровневой абстрактной модели могут быть слишком высоки и возможно написание временного кода без уровней абстракции, который придется выкинуть после утверждения прототипа.
3. Абстракция от источников данных может породить (и порой порождает) неоптимальную работу с источниками данных.
Что делать?
Кодогенерация во многих случаях (не всегда) может заменить многоуровневую абстракцию. При этом будут генерироваться конечные классы (из верхнего уровня абстракции), содержащие в себе методы работы с выбранным источником данных.
1. Имея в основе метаданные, мы можем вносить изменения в алгоритмы генерации кода и разом модифицировать всю модель.
2. При наличии метаданных прототип модели можно получить в кратчайший срок.
3. За счет наличия генераторов для каждого источника данных, модель будет сгенерирована с приемлемой оптимальностью работы с выбранным источником данных, учитывая его специфику.
Чем кодогенерация может помочь в сложных моделях
Сложные приложения всегда задают много вопросов. По моим наблюдениям, на бОльшую часть из них можно ответить еще в процессе разработки (например, на сайте нужно кеширование или нет; какая операционная система будет на сервере; использовать буфферизацию вывода или нет...). Если мы ответим на этим вопросы заранее — мы можем избежать лишней сложности программы, лишних действий, лишних проверок и т.п. Более того, кодогенератор сам может собрать в среде назначения некоторые данные заранее, которые он может использовать для оптимизации работы.
Но это не значит, что меньше результирующего кода = проще система. Кодогенератор сам должен быть достаточно качественный для того, чтобы генерировать качественный код.
Кодогенерация + многоуровневая абстрактная модель
Данные подходы не противоречат друг другу и могут использоваться совместно. Например, в ASP.NET существует система хранения личных данных пользователей в профилях (Profile). Там выстроена абстрактная модель с несколькими уровнями абстракции, а сверху лежит ProfileBase. Если список свойств мы зададим в конфигурации, то будет сгенерирован потомок класса ProfileBase — ProfileCommon, который будет содержать те свойства, которые мы указали в конфигурации. Фактически, в конфигурации мы указали метаданные.
В следующей статье мы разработаем определенный несложный кодогенератор.