Как бы вы назвали класс, у которого есть база данных и множество связанных методов?
- Привет, Алиса, мне было интересно, не могли бы вы помочь мне с одним вопросом по программированию. А именно, вторая сложная проблема в информатике.
- Конечно, как вы пытаетесь назвать?
- Я создал класс, который содержит database
и предоставляет набор методов, связанных с базами данных.
- Почему бы не назвать его Repository
?
- Ну, Repository
на самом деле является клиентом этого класса.
Компонент, над которым я сейчас работаю, обеспечивает надежный доступ к Coffee
. Одна его часть - CoffeeRepository
с обычными методами поиска-поиска-получения-чего угодно. Он CoffeeRepository
содержит database
и множество вещей, связанных с ним, таких как количество повторных попыток, префиксы и суффиксы имен таблиц и так далее. Так что я подумал, что было бы неплохо переместить всю эту низкоуровневую механику на свое место, и теперь мне интересно, как бы было хорошее название для этого места.
- Значит, этот класс - внутренняя деталь реализации CoffeeRepository
?
- Да, конечно.
- Тогда на самом деле не имеет значения, как вы его назовете. Вы могли бы назвать это даже LowLevelMechanicalCoffeeRepositoryStuff
, и это было бы хорошо. Я бы назвал его как-то вроде CoffeeDatabaseUtils
и перейду к работе над важными вещами.
- Да, вот что я сделал. Я назвал его CoffeeDatabaseHelper
, и код сейчас находится на рассмотрении. Но этот случай до сих пор меня озадачивает. Это как песня, которую вы точно знаете, что где-то слышали, и очень стараетесь вспомнить, где именно вы ее слышали. Для этого варианта использования должен существовать шаблон проектирования; просто я его еще не опознал.
- CoffeeDatabaseHelper
просто нормально. Чем больше объем объекта, тем важнее дать ему хорошее имя. Если это внутреннее дело, *Helper
в порядке
- Да, эта головоломка с именами непрактична, но от этого становится еще интереснее. Разве вы не согласны с тем, что самые прекрасные вещи вообще не имеют применения в реальном мире?
- Хорошо. Что делает этот класс?
- Как я уже сказал, он содержит database
и связанную с ним конфигурацию, а также предоставляет методы для получения самого database
(т. Е. Получателя), имен таблиц и столбцов.
- Зачем какому-либо компоненту знать имена таблиц?
- Потому что наш CoffeeRepository
строит SQL-запросы, и для этого нам нужны имена столбцов и таблиц. Было бы хорошо оставить его в CoffeeRepository
, если бы эти имена были постоянными значениями, но они создаются динамически. Например. имя таблицы <Table prefix> + <Table name>
, но иногда <Table prefix> + <Table name> + <Table suffix>
. Имена столбцов строятся аналогично. Не спрашивайте меня, зачем мы это делаем, все было так, как было раньше, а теперь миграция потребует слишком больших усилий. Прямо сейчас все эти методы находятся в CoffeeRepository
, и я хочу убрать его.
- Правильно. Напомните мне, пожалуйста, почему вы думаете, что CoffeeDatabaseUtils
(или CoffeeDatabaseHelper
) - плохое имя?
- Во-первых, я не ожидал, что класс *Utils
будет удерживать состояние. А здесь мы держим database
и его конфиг. На мой взгляд, *Utils
больше похож на набор статических методов - WriteToFile
, ExtractDateFromId
, GenerateBackupFilePath
и так далее. Кроме того, *Utils
слишком общий. Вы могли бы назвать это Random*Stuff
, и это было бы даже честнее, чем называть его *Utils
.
- Ну, то, что вы описали, в основном случайное, не так ли? Вы пытаетесь очистить свой репозиторий и переместить все неактуальные вещи в отдельное место. Вы делаете предположение, что все эти вещи принадлежат к одному набору, и это нормально, если вы поместите их в один класс. Иногда это так, а иногда - нет. Например, как насчет класса с именем CoffeeTables
, который будет предоставлять имена таблиц и столбцов?
- Хм, CoffeeTables
действительно хорош. Однако есть одна проблема - для CoffeeTables
нет смысла хранить database
.
- Да, вам придется хранить database
отдельно.
- И тогда мне нужно будет сохранить конфигурацию, например, количество повторов или время ожидания запроса также отдельно, возможно, в другом классе, например CoffeeDatabaseConfig
. И у метода checkPermissions()
должен быть целый отдельный класс CoffeeDatabasePermissionChecker
.
Тогда это как бы нарушает всю цель этого упражнения. Дело в том, чтобы упростить CoffeeRepository
, но вместо этого мы создаем несколько новых сущностей - CoffeeTables
, CoffeeDatabaseConfig
, CoffeeDatabasePermissionChecker
. Это могло бы быть хорошо с чисто дизайнерской точки зрения, но, честно говоря, это кажется немного переосмысленным.
Хороший API должен быть максимально простым. Сущности не следует умножать без необходимости. Я хотел бы попытаться объединить все это в один класс, и я считаю, что для этого есть образец. Но какой узор?
Прокси-сервер? Нет, прокси дает вам идентичный интерфейс, который является своего рода дешевым и легким заполнителем для реального объекта. Например, в некоторых странах вы все еще можете использовать ретро-метод оплаты, называемый чеками: вы записываете, сколько денег вы даете человеку, на листе бумаги, и этот человек может пойти в bank и снимите эту сумму со своего счета. Этот чек является прокси на сумму денег.
Адаптер? Это тоже не переходник. Адаптер - это когда вы хотите сделать одно и то же разными способами. Допустим, вы пишете сообщение и хотите поделиться им в Facebook, Twitter, Instagram, канале Telegram и других социальных сетях. Может быть приложение, в котором вы пишете свой пост, нажимаете «Опубликовать», и оно выполняет всю публикацию для всех платформ - каждый из классов, которые будут взаимодействовать с API конкретной платформы, будет адаптером. Здесь дело обстоит не так.
Декоратор? Декораторы обычно незаметно добавляют какое-то поведение к существующему интерфейсу. Хорошим примером декоратора является скиммер банкомата - он похож на устройство чтения карт банкомата, но он также отправляет данные карты тому, кто установил скиммер. Мы добавляем здесь поведение, но добавляем его с помощью новых методов, поэтому это не похоже на декоратор, подходящий для нашего случая.
Фасад? Кажется, это более-менее хороший кандидат. Шаблон призван скрыть сложность и предоставить упрощенный интерфейс. Например, кредитная карта - это фасад для банковского счета. Похоже на то, что мы здесь пытаемся сделать.
Знаете что, я думаю, что фасад на самом деле неплохой выбор.
- Как вы думаете, CoffeeDatabaseFacade
лучше, чем DatabaseHelper
?
- DatabaseHelper
немного лучше, чем DatabaseUtils
в том смысле, что не будет большим сюрпризом, если *Helper
сохранит какое-то состояние. Но мне все равно кажется, что это CofeeDatabaseWhatever
name.
Я имею в виду, что вы читаете код и видите класс с именем CoffeeDatabaseHelper
. Что это имя сообщит вам? Конечно, в этом классе есть что-то связанное с базой данных. Это команда CLI для создания дампа базы данных? Может быть. Это набор методов для предоставления нескольких экземпляров Postgres для этой базы данных? Почему нет?
- CoffeeDatabaseFacade
тоже не похоже на информативное имя.
- Я бы так не сказал. Ожидаете ли вы, что CoffeeDatabaseFacade
будет содержать код DevOps? Возможно нет. Конечно, когда вы видите *Facade
, непонятно, что там происходит, и это странное название. Хотя теоретически он должен говорить вам, что имеет какое-то отношение к database
, он специфичен для Coffee
и обеспечивает упрощенный интерфейс для чего-то сложного. Но разве вы не согласны с тем, что описание узора здесь хорошо подходит?
- Думаю, можно так сказать. Не то чтобы генерация имен - это что-то действительно сложное с инженерной точки зрения, но можно сказать, что это сложно с точки зрения бизнес-логики.
- Ладно, значит, фасад. Но я согласен, что *Facade
- странное имя. Как вы думаете, если бы я назвал класс CofeeDatabase
? И я бы упомянул фасад в комментарии:
// CoffeeDatabase is a facade for |database|.
// It holds |database| instance and provides methods to get table
// and column names, settings like retry count or timeout, etc.
class CoffeeDatabase {
// ...
}
- Я бы не сказал, что это лучшее имя, но и не могу сказать, что оно и слишком плохое. Допустим, все в порядке.
- Привет, Боб, как дела с тем классом CoffeeDatabase
, который мы обсуждали? Вы его переименовали?
- Ну нет. Как вы указали в самом начале, этот класс является деталью реализации. CofeeDatabaseHelper
- достаточно хорошее имя, и я не хотел вызывать споры о лучшем имени для внутреннего компонента. Так что нет, я его не переименовывал. Но я по-прежнему считаю наше обсуждение очень ценным мысленным экспериментом. Спасибо!