Привет, Хаброжители! Создать надежное и безопасное приложение гораздо проще, если упаковать код в аккуратные блоки. Система модулей в Java представляет собой языковой стандарт для создания таких блоков. Теперь вы можете контролировать взаимодействия различных JAR и легко обнаруживать недостающие зависимости. Фундаментальные изменения архитектуры затронули ядро Java, начиная с версии 9. Все API ядра распространяются в виде модулей, а для библиотек, фреймворков и приложений аналогичный подход можно считать хорошей практикой и рекомендацией.
Вы освоите наилучшие практики модульного проектирования, отладки приложения и его развертывания перед сдачей в продакшен.
В главах 6, 7 и 8 обсуждаются технические подробности перехода на Java 9+ и превращения существующей кодовой базы в модульную. В этой главе мы более подробно рассмотрим, как наилучшим образом объединить эти подробности в успешные усилия по миграции и модуляризации. Сначала обсудим, как провести постепенную миграцию, которая хорошо согласуется с процессом разработки, особенно с инструментами сборки и непрерывной интеграцией. Далее рассмотрим, как использовать безымянный модуль и автоматические модули в качестве строительных блоков для конкретных стратегий модуляризации. И наконец, разберем варианты превращения JAR в модульные — их самих или их зависимостей. К концу этой главы вы не только поймете механизмы, стоящие за миграционными проблемами, но и узнаете, как наилучшим образом использовать их в своих целях.
Благодаря знаниям, полученным в главах 6 и 7, вы стали готовыми к любому бою, в который Java 9+ может втянуть вас. Теперь пришло время расширить кругозор и разработать более масштабную стратегию. Как лучше организовать фрагменты, чтобы сделать миграцию максимально тщательной и предсказуемой? В этом разделе приведены рекомендации по подготовке к миграции, оценке усилий по ее проведению, настройке непрерывной сборки на Java 9+ и преодолению недостатков параметров командной строки.
Итак, если вы еще не используете Java 8, то советую тотчас обновить приложение до этой версии! Сделайте себе одолжение и не переходите сразу на две или более версии Java. Обновитесь, приведите в действие все инструменты и процессы, на некоторое время запустите приложение, а затем приступайте к следующему обновлению. Те же действия подойдут, если нужно обновиться с Java 8 до 11, — делайте это по одному шагу за раз. Если возникнут какие-либо проблемы, то вы действительно захотите узнать, какая версия Java или обновление зависимостей вызвали их.
Говоря о зависимостях, еще одно действие, которое можно сделать, даже не принимая во внимание Java 9+, — это начать обновлять и их, и инструменты. Помимо общих преимуществ обновления, можно непреднамеренно обновить версию, в которой есть проблемы с Java 9+, до версии, прекрасно с ней совместимой. Вы даже не заметите, что проблема существовала. Если версии, совместимой с Java 9+, еще нет, то обновление зависимости или инструмента по-прежнему упростит процесс перехода после публикации совместимой версии.
Есть пара вещей, которые можно сделать, чтобы понять предстоящее, и мы рассмотрим их в первую очередь. Следующим шагом будет оценка и классификация найденных проблем. Я завершу этот подраздел небольшой заметкой об оценке конкретных чисел.
Поиск проблем
Вот наиболее очевидный порядок действий для составления списка проблем.
Тщательно проанализируйте вывод, запишите новые предупреждения и ошибки и попытайтесь связать их с тем, что обсуждалось в предыдущих главах. Обратите внимание на удаленные параметры командной строки, описанные в подразделе 6.5.3.
Рекомендуется применить несколько быстрых исправлений, таких как добавление экспорта или модулей JEE. Это позволит заметить более сложные проблемы, которые могут скрываться за доброкачественными. На данном этапе ни одно исправление не является слишком быстрым или слишком грязным — все, что заставляет сборку выдать новую ошибку, считается победой. Если выводится слишком много ошибок компиляции, то можно скомпилировать проект на Java 8 и просто запустить тесты на Java 9+ (Maven: mvn surefire:test).
Затем запустите JDeps для проекта и зависимостей. Проанализируйте зависимости от внутренних API JDK (см. подраздел 7.1.2) и запишите все модули JEE (раздел 6.1). Кроме того, обратите внимание на разделение пакетов между платформенными модулями и JAR-файлами приложения (см. подраздел 7.2.5).
Наконец, найдите в кодовой базе вызовы AccessibleObject::setAccessible (см. подраздел 7.1.4), приведения к TOURLClassLoader (раздел 6.2), анализ системных свойств java.version (см. подраздел 6.5.1) или напрямую вписанные в код URL ресурсов (см. раздел 6.3). Поместите все, что нашли, в один большой список — теперь пришло время проанализировать его.
Насколько все плохо?
Найденные проблемы должны быть разделены на две категории: «Я видел подобное в этой книге» и «Что, & *1 #?, происходит?». Далее разделите проблемы из первой категории на «Имеет по крайней мере временное решение» и «Это сложная проблема». Особо сложные проблемы — удаленные API и разделения пакетов между платформенными модулями и JAR, которые не реализуют утвержденный стандарт или самостоятельную технологию.
Важно не путать распространенность с важностью! Вы можете получить около тысячи ошибок, поскольку отсутствует модуль JEE, но это можно с легкостью исправить. С другой стороны, у вас большие проблемы, если основная функция зависит от приведения загрузчика классов приложения к URLClassLoader. Или же может присутствовать критическая зависимость от удаленного API, но, поскольку вы хорошо спроектировали свою систему, это просто вызывает несколько ошибок компиляции в одном подпроекте.
Хороший подход состоит в том, чтобы задавать себе вопрос о каждой конкретной проблеме, для которой нет известного решения: «Насколько было бы плохо, если бы я вырезал проблемный код и все, что от него зависит?» Насколько это повредит проекту? Таким образом, можно ли временно деактивировать проблемный код? Тесты можно игнорировать, а функции — переключать с помощью флагов. Узнайте, насколько реально отложить исправление и запустить сборку и приложение без него.
К концу анализа у вас должен получиться список проблем в этих трех категориях:
Для проблем в последних двух категориях нужно знать, насколько они опасны для проекта и насколько легко можно обойтись без их неотложного исправления.
Об измерении оценки
Скорее всего, кто-то захочет, чтобы вы дали оценку, которая включает в себя несколько точных цифр — например, в часах или валюте. По умолчанию это уже сложно, но в данном случае оценивание особенно проблематично.
Миграция к Java 9+ ставит вас лицом к лицу с музыкой давно принятых решений. Проект может быть тесно связан с устаревшей версией веб-фреймворка, который нужно было обновить еще несколько лет назад, или же он накопил большой технический долг вокруг несопровождаемой библиотеки. И, к сожалению, оба из них перестают работать в Java 9+. Что нужно сделать сейчас — это погасить некоторые технические долги, и, как всем известно, оценить пени и проценты будет весьма непросто. Наконец, точно так же, как в старой доброй битве с боссом, критическая проблема — та, которая стоит дороже всех, — может быть скрыта за несколькими другими нарушителями спокойствия, поэтому ее нельзя увидеть, не подобравшись вплотную. Я не говорю, что эти сценарии очень вероятны, просто они возможны, поэтому будьте осторожны, гадая, сколько времени может уйти на переход к Java 9.
Учитывая предположение, что вы постоянно разрабатываете проект, следующим шагом будет создание успешной сборки Java 9+. Есть много решений, требующих принятия.
В конце концов, нужно найти ответы, соответствующие вашему проекту и настройке непрерывной интеграции (НИ). Позвольте поделиться идеями, которые хорошо работали во время моей собственной миграции, — вы можете объединить их так, как вам нравится.
Какую ветку собирать
У вас может возникнуть соблазн настроить собственную ветку для миграции и позволить НИ-серверу создать данную ветку на Java 9+, а остальные — на Java 8, как и раньше. Но миграция может занять некоторое время, что приведет к долгоживущей ветке, и я обычно стараюсь не делать это по разным причинам:
Хотя проведение первоначального исследования миграции в отдельной ветке может иметь смысл, я рекомендую сначала переключиться на основную ветку разработки и настроить НИ там. Это требует немного больше усилий для инструмента сборки, поскольку понадобится разделить некоторые части конфигурации (например, параметры командной строки для компилятора) по версии Java (компилятору Java не нравятся неизвестные параметры).
Какую версию собирать
Должна ли сборка Java 9+ создавать отдельную версию артефактов — что-то вроде -JAVA-LATEST-SNAPSHOT? Вслед за решением создать отдельную ветку Java 9+, вероятно, придется создавать отдельную версию. В противном случае можно легко смешать артефакты снимков файловой системы из разных веток, что нарушает сборку тем чаще, чем больше веток отклоняется. Если вы решили проводить сборку из основной ветки разработки, то создание отдельной версии может быть непростым, но я никогда не пытался это делать, поскольку не нашел на то веских причин.
Независимо от способа работы с версиями, при попытке заставить что-то работать на Java 9+ вы, вероятно, иногда будете собирать один и тот же подпроект с одной и той же версией на Java 8. Единственное, что я делаю снова и снова, даже если решаю не делать, — устанавливаю артефакты, собранные с помощью Java 9+, в локальный репозиторий. Знаете, тот самый рефлекторный mvn clean install?
Это не очень хорошая идея: тогда нельзя будет использовать эти артефакты в сборке Java 8, поскольку она не поддерживает байт-код Java 9+.
При локальной сборке на Java 9+ старайтесь не устанавливать артефакты! Я для этого использую mvn clean.
Что собирать на Java 9+
Конечная цель — запустить инструмент сборки на Java 9+ и собрать все проекты на всех этапах/задачах. В зависимости от того, сколько элементов в данном списке вы создали ранее, возможно, потребуется всего лишь изменить парочку вещей, чтобы этого достичь. В таком случае выполните данные действия — не нужно усложнять процесс. С другой стороны, если список более сложный, то разделить сборку Java 9 можно несколькими способами:
Вообще говоря, я предпочитаю подход «по целям/задачам» для крупных монолитных проектов и подход «по подпроектам», если проект разбит на части, достаточно маленькие, чтобы их можно было разрешить за один раз.
Если вы выбираете подпроект, но по какой-либо причине один из подпроектов не может быть собран на Java 9+, то не получится легко собрать подпроекты, зависящие от него. Однажды я попал в такую ситуацию, и мы решили настроить сборку Java 9 в два этапа.
1. Собрать все на Java 8.
2. Собрать все на Java 9+, кроме проблемных подпроектов (подпроекты, зависящие от них, были собраны на основе артефактов Java 8).
» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — Java
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
Вы освоите наилучшие практики модульного проектирования, отладки приложения и его развертывания перед сдачей в продакшен.
Стратегии миграции и модуляризации
- Подготовка к переходу на Java 9 и выше.
- Непрерывная интеграция изменений.
- Постепенная модуляризация проектов.
- Генерация декларации модуля с помощью JDeps.
- Взлом сторонних JAR-файлов с помощью инструмента jar.
- Публикация модульных JAR для Java 8 и старше.
В главах 6, 7 и 8 обсуждаются технические подробности перехода на Java 9+ и превращения существующей кодовой базы в модульную. В этой главе мы более подробно рассмотрим, как наилучшим образом объединить эти подробности в успешные усилия по миграции и модуляризации. Сначала обсудим, как провести постепенную миграцию, которая хорошо согласуется с процессом разработки, особенно с инструментами сборки и непрерывной интеграцией. Далее рассмотрим, как использовать безымянный модуль и автоматические модули в качестве строительных блоков для конкретных стратегий модуляризации. И наконец, разберем варианты превращения JAR в модульные — их самих или их зависимостей. К концу этой главы вы не только поймете механизмы, стоящие за миграционными проблемами, но и узнаете, как наилучшим образом использовать их в своих целях.
9.1. Стратегии миграции
Благодаря знаниям, полученным в главах 6 и 7, вы стали готовыми к любому бою, в который Java 9+ может втянуть вас. Теперь пришло время расширить кругозор и разработать более масштабную стратегию. Как лучше организовать фрагменты, чтобы сделать миграцию максимально тщательной и предсказуемой? В этом разделе приведены рекомендации по подготовке к миграции, оценке усилий по ее проведению, настройке непрерывной сборки на Java 9+ и преодолению недостатков параметров командной строки.
ПРИМЕЧАНИЕ
Многие темы в данном разделе связаны с инструментами сборки, но они достаточно универсальны, поэтому вам не требуется знание какого-либо конкретного инструмента. В то же время я хотел поделиться своим опытом с Maven (единственным инструментом сборки, который я использовал на Java 9+), поэтому иногда упоминал функцию Maven, применяемую мной для выполнения определенных требований. Я не буду вдаваться в подробности, поэтому вам самим придется выяснить, как работают все эти функции.
9.1.1. Подготовительные обновления
Итак, если вы еще не используете Java 8, то советую тотчас обновить приложение до этой версии! Сделайте себе одолжение и не переходите сразу на две или более версии Java. Обновитесь, приведите в действие все инструменты и процессы, на некоторое время запустите приложение, а затем приступайте к следующему обновлению. Те же действия подойдут, если нужно обновиться с Java 8 до 11, — делайте это по одному шагу за раз. Если возникнут какие-либо проблемы, то вы действительно захотите узнать, какая версия Java или обновление зависимостей вызвали их.
Говоря о зависимостях, еще одно действие, которое можно сделать, даже не принимая во внимание Java 9+, — это начать обновлять и их, и инструменты. Помимо общих преимуществ обновления, можно непреднамеренно обновить версию, в которой есть проблемы с Java 9+, до версии, прекрасно с ней совместимой. Вы даже не заметите, что проблема существовала. Если версии, совместимой с Java 9+, еще нет, то обновление зависимости или инструмента по-прежнему упростит процесс перехода после публикации совместимой версии.
Обзор охвата с AdoptOpenJDK
AdoptOpenJDK, «сообщество членов групп пользователей, разработчиков и поставщиков Java, которые являются сторонниками OpenJDK», предоставляет список различных проектов с открытым исходным кодом и их совместимостью с последней и следующей версиями Java: mng.bz/90HA.
9.1.2. Оценка усилий
Есть пара вещей, которые можно сделать, чтобы понять предстоящее, и мы рассмотрим их в первую очередь. Следующим шагом будет оценка и классификация найденных проблем. Я завершу этот подраздел небольшой заметкой об оценке конкретных чисел.
Поиск проблем
Вот наиболее очевидный порядок действий для составления списка проблем.
- Настройте сборку для компиляции и тестирования на Java 9+ (Maven: набор инструментов) в идеале таким образом, чтобы можно было собирать все ошибки вместо того, чтобы останавливаться на первой (Maven: --fail-never).
- Запустите всю сборку на Java 9+ (Maven: ~/.mavenrc), снова собирая все ошибки.
- Если вы разрабатываете приложение, то создайте его как обычно (имеется в виду еще не в Java 9+), а затем запустите в Java 9+. Используйте --illegal-access=debug или deny для получения дополнительной информации о несанкционированном доступе.
Тщательно проанализируйте вывод, запишите новые предупреждения и ошибки и попытайтесь связать их с тем, что обсуждалось в предыдущих главах. Обратите внимание на удаленные параметры командной строки, описанные в подразделе 6.5.3.
Рекомендуется применить несколько быстрых исправлений, таких как добавление экспорта или модулей JEE. Это позволит заметить более сложные проблемы, которые могут скрываться за доброкачественными. На данном этапе ни одно исправление не является слишком быстрым или слишком грязным — все, что заставляет сборку выдать новую ошибку, считается победой. Если выводится слишком много ошибок компиляции, то можно скомпилировать проект на Java 8 и просто запустить тесты на Java 9+ (Maven: mvn surefire:test).
Затем запустите JDeps для проекта и зависимостей. Проанализируйте зависимости от внутренних API JDK (см. подраздел 7.1.2) и запишите все модули JEE (раздел 6.1). Кроме того, обратите внимание на разделение пакетов между платформенными модулями и JAR-файлами приложения (см. подраздел 7.2.5).
Наконец, найдите в кодовой базе вызовы AccessibleObject::setAccessible (см. подраздел 7.1.4), приведения к TOURLClassLoader (раздел 6.2), анализ системных свойств java.version (см. подраздел 6.5.1) или напрямую вписанные в код URL ресурсов (см. раздел 6.3). Поместите все, что нашли, в один большой список — теперь пришло время проанализировать его.
Насколько все плохо?
Найденные проблемы должны быть разделены на две категории: «Я видел подобное в этой книге» и «Что, & *1 #?, происходит?». Далее разделите проблемы из первой категории на «Имеет по крайней мере временное решение» и «Это сложная проблема». Особо сложные проблемы — удаленные API и разделения пакетов между платформенными модулями и JAR, которые не реализуют утвержденный стандарт или самостоятельную технологию.
Важно не путать распространенность с важностью! Вы можете получить около тысячи ошибок, поскольку отсутствует модуль JEE, но это можно с легкостью исправить. С другой стороны, у вас большие проблемы, если основная функция зависит от приведения загрузчика классов приложения к URLClassLoader. Или же может присутствовать критическая зависимость от удаленного API, но, поскольку вы хорошо спроектировали свою систему, это просто вызывает несколько ошибок компиляции в одном подпроекте.
Хороший подход состоит в том, чтобы задавать себе вопрос о каждой конкретной проблеме, для которой нет известного решения: «Насколько было бы плохо, если бы я вырезал проблемный код и все, что от него зависит?» Насколько это повредит проекту? Таким образом, можно ли временно деактивировать проблемный код? Тесты можно игнорировать, а функции — переключать с помощью флагов. Узнайте, насколько реально отложить исправление и запустить сборку и приложение без него.
К концу анализа у вас должен получиться список проблем в этих трех категориях:
- известная проблема с легким решением;
- известная сложная проблема;
- неизвестная проблема, требующая изучения.
Для проблем в последних двух категориях нужно знать, насколько они опасны для проекта и насколько легко можно обойтись без их неотложного исправления.
Об измерении оценки
Скорее всего, кто-то захочет, чтобы вы дали оценку, которая включает в себя несколько точных цифр — например, в часах или валюте. По умолчанию это уже сложно, но в данном случае оценивание особенно проблематично.
Миграция к Java 9+ ставит вас лицом к лицу с музыкой давно принятых решений. Проект может быть тесно связан с устаревшей версией веб-фреймворка, который нужно было обновить еще несколько лет назад, или же он накопил большой технический долг вокруг несопровождаемой библиотеки. И, к сожалению, оба из них перестают работать в Java 9+. Что нужно сделать сейчас — это погасить некоторые технические долги, и, как всем известно, оценить пени и проценты будет весьма непросто. Наконец, точно так же, как в старой доброй битве с боссом, критическая проблема — та, которая стоит дороже всех, — может быть скрыта за несколькими другими нарушителями спокойствия, поэтому ее нельзя увидеть, не подобравшись вплотную. Я не говорю, что эти сценарии очень вероятны, просто они возможны, поэтому будьте осторожны, гадая, сколько времени может уйти на переход к Java 9.
9.1.3. Непрерывная интеграция в Java 9+
Учитывая предположение, что вы постоянно разрабатываете проект, следующим шагом будет создание успешной сборки Java 9+. Есть много решений, требующих принятия.
- Какую ветку нужно собрать?
- Нужна ли отдельная версия?
- Как разделить сборку, если она не может полностью работать на Java 9+ с первого дня?
- Как поддерживать параллельные сборки Java 8 и Java 9+?
В конце концов, нужно найти ответы, соответствующие вашему проекту и настройке непрерывной интеграции (НИ). Позвольте поделиться идеями, которые хорошо работали во время моей собственной миграции, — вы можете объединить их так, как вам нравится.
Какую ветку собирать
У вас может возникнуть соблазн настроить собственную ветку для миграции и позволить НИ-серверу создать данную ветку на Java 9+, а остальные — на Java 8, как и раньше. Но миграция может занять некоторое время, что приведет к долгоживущей ветке, и я обычно стараюсь не делать это по разным причинам:
- вы сами по себе и ваши изменения не находитесь под постоянным контролем команды, которая работает на их основе;
- обе ветки могут претерпеть множество изменений, что увеличивает вероятность конфликтов при обновлении или объединении ветки Java 9+;
- если для того, чтобы продлить изменения в основной ветке разработки к ветке Java 9+, потребуется некоторое время, то остальная часть команды тогда же может свободно добавлять в нее код, который создаст новые проблемы в Java 9+ при отсутствии немедленной обратной связи.
Хотя проведение первоначального исследования миграции в отдельной ветке может иметь смысл, я рекомендую сначала переключиться на основную ветку разработки и настроить НИ там. Это требует немного больше усилий для инструмента сборки, поскольку понадобится разделить некоторые части конфигурации (например, параметры командной строки для компилятора) по версии Java (компилятору Java не нравятся неизвестные параметры).
Какую версию собирать
Должна ли сборка Java 9+ создавать отдельную версию артефактов — что-то вроде -JAVA-LATEST-SNAPSHOT? Вслед за решением создать отдельную ветку Java 9+, вероятно, придется создавать отдельную версию. В противном случае можно легко смешать артефакты снимков файловой системы из разных веток, что нарушает сборку тем чаще, чем больше веток отклоняется. Если вы решили проводить сборку из основной ветки разработки, то создание отдельной версии может быть непростым, но я никогда не пытался это делать, поскольку не нашел на то веских причин.
Независимо от способа работы с версиями, при попытке заставить что-то работать на Java 9+ вы, вероятно, иногда будете собирать один и тот же подпроект с одной и той же версией на Java 8. Единственное, что я делаю снова и снова, даже если решаю не делать, — устанавливаю артефакты, собранные с помощью Java 9+, в локальный репозиторий. Знаете, тот самый рефлекторный mvn clean install?
Это не очень хорошая идея: тогда нельзя будет использовать эти артефакты в сборке Java 8, поскольку она не поддерживает байт-код Java 9+.
При локальной сборке на Java 9+ старайтесь не устанавливать артефакты! Я для этого использую mvn clean.
Что собирать на Java 9+
Конечная цель — запустить инструмент сборки на Java 9+ и собрать все проекты на всех этапах/задачах. В зависимости от того, сколько элементов в данном списке вы создали ранее, возможно, потребуется всего лишь изменить парочку вещей, чтобы этого достичь. В таком случае выполните данные действия — не нужно усложнять процесс. С другой стороны, если список более сложный, то разделить сборку Java 9 можно несколькими способами:
- запустить сборку на Java 8 и только компилировать и тестировать на Java 9+. Я поговорю об этом чуть позже;
- провести миграцию для каждой цели/задачи. Это значит, что сначала нужно попытаться скомпилировать весь проект с помощью Java 9+, прежде чем запускать тесты;
- провести миграцию по подпроекту, то есть сначала попытаться скомпилировать, протестировать и упаковать один подпроект, прежде чем переходить к следующему.
Вообще говоря, я предпочитаю подход «по целям/задачам» для крупных монолитных проектов и подход «по подпроектам», если проект разбит на части, достаточно маленькие, чтобы их можно было разрешить за один раз.
Если вы выбираете подпроект, но по какой-либо причине один из подпроектов не может быть собран на Java 9+, то не получится легко собрать подпроекты, зависящие от него. Однажды я попал в такую ситуацию, и мы решили настроить сборку Java 9 в два этапа.
1. Собрать все на Java 8.
2. Собрать все на Java 9+, кроме проблемных подпроектов (подпроекты, зависящие от них, были собраны на основе артефактов Java 8).
» Более подробно с книгой можно ознакомиться на сайте издательства
» Оглавление
» Отрывок
Для Хаброжителей скидка 25% по купону — Java
По факту оплаты бумажной версии книги на e-mail высылается электронная книга.
APXEOLOG
Почему на обложках большинства книг по Java изображена древность?
Pavel1114
Тоже зашёл про обложку спросить. Есть вообще какая то связь с тематикой книги? Или рандом?
anonymous
у manning уже 10 лет обложки украшаются изображениями из старых книг костюмов разных народов без всякой связи с содержанием. во введении в отдельном разделе к каждой книге написано что именно изображено на обложке и откуда взято изображение.
Pavel1114
Так себе идея
ph_piter Автор
Об обложке
Иллюстрация на обложке книги называется «Житель Флориды» и изображает коренного американца из Флориды. Она взята из коллекции нарядных костюмов разных стран из французской книги Costumes civils actuels de tous les peuples connus Жака Грассе де Сен-Совера (Jacques Grasset de Saint-Sauveur) (1775–1810), опубликованной в 1788 году. Каждая иллюстрация создана и раскрашена вручную. Богатство коллекции Грассе де Сен-Совера напоминает о том, как в культурном плане взаимно далеки были города и поселения мира всего 200 лет назад. Изолированные друг от друга, люди говорили на разных языках и диалектах. На улицах города или в селах — везде легко было определить, основываясь лишь на одежде, откуда человек родом, каковы его профессия и материальное положение.
С тех пор манера одеваться значительно изменилась и разнообразие регионов, столь богатое в прошлом, сгладилось. Сегодня сложно различить даже жителей разных континентов, не говоря уже о городах, регионах или странах. Возможно, мы обменяли культурное разнообразие на жизнь, более богатую в личном плане и, конечно, быстро развивающуюся в технологическом.
Сейчас, когда одну техническую книгу сложно отличить от другой, Manning является образцом оригинальности и изобретательности в компьютерном бизнесе, выпуская книги с обложками, которые представляют богатое разнообразие жизни в регионах, имевшееся более двух столетий назад и возрожденное на картинах Грассе де Сен-Совера.