Я собираюсь сохранить некоторую полезную нагрузку строки в базе данных. У меня есть две глобальные конфигурации:
- шифрование
- компрессия
Они могут быть включены или отключены с помощью конфигурации таким образом, что либо только один из них включен, либо включены, либо оба отключены.
Моя текущая реализация такова:
if (encryptionEnable && !compressEnable) {
encrypt(data);
} else if (!encryptionEnable && compressEnable) {
compress(data);
} else if (encryptionEnable && compressEnable) {
encrypt(compress(data));
} else {
data;
}
Я думаю о шаблоне Decorator. Это правильный выбор или есть лучшая альтернатива?
design
design-patterns
object-oriented-design
refactoring
Дамит Ганегода
источник
источник
if
заявления?Ответы:
При разработке кода у вас всегда есть два варианта.
Я не собираюсь сосредотачиваться на первом из двух, потому что на самом деле нечего сказать. Если вы просто хотите заставить его работать, вы можете оставить код как есть.
Но что произойдет, если вы решите сделать это педантично и действительно решите проблему с шаблонами проектирования, так, как вы этого хотели?
Вы могли бы смотреть на следующий процесс:
При разработке ОО-кода, большинство из тех,
if
которые находятся в коде, не обязательно должны быть там. Естественно, если вы хотите сравнить два скалярных типа, таких какint
s илиfloat
s, у вас, вероятно, естьif
, но если вы хотите изменить процедуры на основе конфигурации, вы можете использовать полиморфизм для достижения того, что вы хотите, перенести решения (if
s) от вашей бизнес-логики до места, где создаются объекты - на фабрики .На данный момент ваш процесс может пройти 4 отдельных пути:
data
не шифруется и не сжимается (ничего не вызывать, возвращатьdata
)data
сжат (позвонитеcompress(data)
и верните его)data
зашифрован (позвонитеencrypt(data)
и верните его)data
сжат и зашифрован (позвонитеencrypt(compress(data))
и верните его)Просто глядя на 4 пути, вы обнаружите проблему.
У вас есть один процесс, который вызывает 3 (теоретически 4, если вы считаете, что ничего не вызываете как один) разные методы, которые манипулируют данными, а затем возвращают их. Методы имеют разные имена , разные так называемые публичные API (способ, которым методы передают свое поведение).
Используя шаблон адаптера , мы можем решить возникновение конфликта имен (мы можем объединить публичный API). Проще говоря, адаптер помогает двум несовместимым интерфейсам работать вместе. Кроме того, адаптер работает, определяя новый интерфейс адаптера, который классы пытаются объединить в своем API.
Я собираюсь предположить, что прямо сейчас у вас может быть два класса, отвечающих за сжатие и шифрование.
В корпоративном мире даже эти конкретные классы с большой вероятностью будут заменены интерфейсами, например,
class
ключевое слово будет заменено наinterface
(если вы имеете дело с такими языками, как C #, Java и / или PHP) илиclass
ключевое слово останется, ноCompress
иEncrypt
методы будут определены как чисто виртуальные , если вы будете кодировать на C ++.Чтобы сделать адаптер, мы определяем общий интерфейс.
Затем мы должны предоставить реализации интерфейса, чтобы сделать его полезным.
Делая это, вы получаете 4 класса, каждый из которых делает что-то совершенно другое, но каждый из них предоставляет один и тот же публичный API.
Process
Метод.В вашей бизнес-логике, где вы имеете дело с решением «нет / шифрование / сжатие / оба», вы создадите свой объект так, чтобы он зависел от
DataProcessing
интерфейса, который мы проектировали ранее.Сам процесс может быть таким простым:
Нет больше условий. Класс
DataService
не имеет представления о том, что на самом деле будет сделано с данными, когда они будут переданыdataProcessing
члену, и он на самом деле не заботится об этом, это не его обязанность.В идеале у вас должны быть модульные тесты, тестирующие 4 класса адаптеров, которые вы создали, чтобы убедиться, что они работают. И если они пройдут, вы можете быть уверены, что они будут работать независимо от того, где вы их называете в своем коде.
Так что, делая это таким образом, я никогда больше не буду
if
в своем коде?Нет. Вы менее склонны иметь условные выражения в своей бизнес-логике, но они все равно должны быть где-то. Место это ваши фабрики.
И это хорошо. Вы разделяете проблемы создания и фактического использования кода. Если вы делаете свои фабрики надежными (в Java вы можете даже пойти на то, чтобы использовать что-то вроде фреймворка Guice от Google), в вашей бизнес-логике вас не беспокоит выбор правильного класса для внедрения. Потому что вы знаете, что ваши фабрики работают и доставят то, что просят.
Нужно ли иметь все эти классы, интерфейсы и т. Д.?
Это возвращает нас к началу.
В ООП, если вы выбираете путь для использования полиморфизма, действительно хотите использовать шаблоны проектирования, хотите использовать возможности языка и / или хотите следовать всему, что является объектной идеологией, тогда это так. И даже тогда, этот пример не даже показать все заводы , которые вы собираетесь потребность , и если вы были рефакторить
Compression
иEncryption
классы и сделать их интерфейсы вместо этого, вы должны включить их реализацию , а также.В итоге вы получите сотни маленьких классов и интерфейсов, сфокусированных на очень специфических вещах. Что не обязательно плохо, но не может быть лучшим решением для вас, если все, что вам нужно, это сделать что-то простое, как сложение двух чисел.
Если вы хотите , чтобы сделать это и быстро, вы можете получить решение Ixrec в , который , по крайней мере удалось устранить
else if
иelse
блоки, которые, на мой взгляд, даже чуть - чуть хуже , чем в равнинеif
.Обновление 2: была дикая дискуссия о первой версии моего решения. Обсуждение в основном вызвано мной, за что я прошу прощения.
Я решил отредактировать ответ таким образом, чтобы это был один из способов взглянуть на решение, но не единственный. Я также удалил часть декоратора, где вместо этого я имел в виду фасад, который я в конце концов решил полностью исключить, потому что адаптер - это вариант фасада.
источник
Compression
иEncryption
интерфейсы кажутся совершенно излишними. Я не уверен, предлагаете ли вы, что они каким-то образом необходимы для процесса украшения, или просто подразумеваете, что они представляют собой извлеченные понятия. Вторая проблема заключается в том, что создание подобного классаCompressionEncryptionDecorator
приводит к тому же виду комбинаторного взрыва, что и условные выражения ОП. Я также не вижу шаблон декоратора достаточно ясно в предложенном коде.Единственная проблема, с которой я сталкиваюсь в вашем текущем коде, - это риск комбинаторного взрыва, когда вы добавляете больше настроек, которые можно легко уменьшить, структурировав код следующим образом:
Я не знаю ни о каком «шаблоне дизайна» или «идиоме», который можно было бы считать примером.
источник
else
между моими двумя утверждениями if нет и почему я назначаюdata
каждый раз. Если оба флага имеют значение true, тогда compress () выполняется, а encrypt () выполняется на результат compress (), как вы хотите.Я полагаю, что ваш вопрос ищет не практичности, и в этом случае ответ lxrec является правильным, а узнать о шаблонах проектирования.
Очевидно, что шаблон команды является излишним для такой тривиальной проблемы, как та, которую вы предлагаете, но для иллюстрации здесь это звучит так:
Как видите, размещение команд / преобразований в списке позволяет выполнять их последовательно. Очевидно, он будет выполнять оба или только один из них в зависимости от того, что вы поместили в список без условий if.
Очевидно, что условные выражения окажутся в какой-то фабрике, которая собирает список команд.
РЕДАКТИРОВАТЬ для комментария @ texacre:
Есть много способов избежать условий if в творческой части решения, давайте возьмем, например, приложение с графическим интерфейсом для настольных компьютеров . Вы можете иметь флажки для параметров сжатия и шифрования. В
on clic
случае этих флажков вы создаете соответствующую команду и добавляете ее в список или удаляете из списка, если отмените выбор опции.источник
commands.add(new EncryptCommand());
илиcommands.add(new CompressCommand());
соответственно.Я думаю, что «шаблоны дизайна» излишне ориентированы на «шаблоны» и полностью избегают гораздо более простых идей. Здесь мы говорим о (простом) конвейере данных.
Я хотел бы попытаться сделать это в ближайшем будущем. Любой другой язык, где функции первоклассны, вероятно, тоже подойдет. Может быть, я мог бы позже привести пример на C #, но это не так хорошо. Моим способом решения этой проблемы были бы следующие шаги с некоторыми объяснениями для не-клюжанцев:
1. Представьте набор преобразований.
Это карта, то есть таблица поиска / словарь / что угодно, от ключевых слов до функций. Другой пример (ключевые слова для строк):
Итак, написание
(transformations :encrypt)
или(:encrypt transformations)
вернет функцию шифрования. ((fn [data] ... )
это просто лямбда-функция.)2. Получить параметры в виде последовательности ключевых слов:
3. Отфильтруйте все преобразования, используя предоставленные параметры.
Пример:
4. Объедините функции в одну:
Пример:
5. А потом вместе:
ЕДИНСТВЕННЫЕ изменения, если мы хотим добавить новую функцию, скажем, «debug-print», следующие:
источник
funcs-to-run-here (map options funcs)
выполняет фильтрацию, таким образом выбирая набор функций для применения. Может быть, я должен обновить ответ и углубиться в детали.[По сути, мой ответ является продолжением ответа @Ixrec выше . ]
Важный вопрос: будет ли расти число различных комбинаций, которые вам нужно охватить? Вы лучше знаете свой предметный домен. Это ваше суждение.
Может ли количество вариантов увеличиться? Ну, это не немыслимо. Например, вам может потребоваться адаптировать более разные алгоритмы шифрования.
Если вы ожидаете, что число различных комбинаций будет расти, то шаблон Стратегии может вам помочь. Он предназначен для инкапсуляции алгоритмов и обеспечения взаимозаменяемого интерфейса с вызывающим кодом. У вас все еще будет небольшое количество логики, когда вы создаете (создаете) соответствующую стратегию для каждой конкретной строки.
Вы прокомментировали выше, что не ожидаете изменения требований. Если вы не ожидаете, что число вариантов будет расти (или вы можете отложить этот рефакторинг), оставьте логику такой, какая она есть. В настоящее время у вас есть небольшое и управляемое количество логики. (Возможно, добавьте примечание к себе в комментариях о возможном рефакторинге к шаблону Стратегии.)
источник
Один из способов сделать это в Scala:
Использование шаблона декоратора для достижения вышеуказанных целей (разделение логики отдельной обработки и способ их соединения) было бы слишком многословным.
Там, где вам потребуется шаблон проектирования для достижения этих целей проектирования в парадигме программирования ОО, функциональный язык предлагает встроенную поддержку, используя функции в качестве первоклассников (строки 1 и 2 в коде) и функциональную композицию (строка 3).
источник