При разработке кода у вас всегда есть два варианта.
- просто сделайте это, и в этом случае практически любое решение будет работать для вас
- быть педантичным и разработать решение, которое использует причуды языка и его идеологию (в данном случае ОО-языки - использование полиморфизма в качестве средства для принятия решения)
Я не собираюсь сосредотачиваться на первом из двух, потому что на самом деле нечего сказать. Если вы просто хотите заставить его работать, вы можете оставить код как есть.
Но что произойдет, если вы решите сделать это педантично и действительно решите проблему с шаблонами проектирования, так, как вы этого хотели?
Вы могли бы смотреть на следующий процесс:
При разработке ОО-кода, большинство из тех, 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.
Это не конкретный язык. Это универсальный подход, любое ключевое слово для представления может быть любого типа, в языке, подобном C #, вы можете заменить его на generics ( <T>
).
Я собираюсь предположить, что прямо сейчас у вас может быть два класса, отвечающих за сжатие и шифрование.
class Compression
{
Compress(data : any) : any { ... }
}
class Encryption
{
Encrypt(data : any) : any { ... }
}
В корпоративном мире даже эти конкретные классы с большой вероятностью будут заменены интерфейсами, например, class
ключевое слово будет заменено на interface
(если вы имеете дело с такими языками, как C #, Java и / или PHP) или class
ключевое слово останется, но Compress
и Encrypt
методы будут определены как чисто виртуальные , если вы будете кодировать на C ++.
Чтобы сделать адаптер, мы определяем общий интерфейс.
interface DataProcessing
{
Process(data : any) : any;
}
Затем мы должны предоставить реализации интерфейса, чтобы сделать его полезным.
// when neither encryption nor compression is enabled
class DoNothingAdapter : DataProcessing
{
public Process(data : any) : any
{
return data;
}
}
// when only compression is enabled
class CompressionAdapter : DataProcessing
{
private compression : Compression;
public Process(data : any) : any
{
return this.compression.Compress(data);
}
}
// when only encryption is enabled
class EncryptionAdapter : DataProcessing
{
private encryption : Encryption;
public Process(data : any) : any
{
return this.encryption.Encrypt(data);
}
}
// when both, compression and encryption are enabled
class CompressionEncryptionAdapter : DataProcessing
{
private compression : Compression;
private encryption : Encryption;
public Process(data : any) : any
{
return this.encryption.Encrypt(
this.compression.Compress(data)
);
}
}
Делая это, вы получаете 4 класса, каждый из которых делает что-то совершенно другое, но каждый из них предоставляет один и тот же публичный API. Process
Метод.
В вашей бизнес-логике, где вы имеете дело с решением «нет / шифрование / сжатие / оба», вы создадите свой объект так, чтобы он зависел от DataProcessing
интерфейса, который мы проектировали ранее.
class DataService
{
private dataProcessing : DataProcessing;
public DataService(dataProcessing : DataProcessing)
{
this.dataProcessing = dataProcessing;
}
}
Сам процесс может быть таким простым:
public ComplicatedProcess(data : any) : any
{
data = this.dataProcessing.Process(data);
// ... perhaps work with the data
return data;
}
Нет больше условий. Класс DataService
не имеет представления о том, что на самом деле будет сделано с данными, когда они будут переданы dataProcessing
члену, и он на самом деле не заботится об этом, это не его обязанность.
В идеале у вас должны быть модульные тесты, тестирующие 4 класса адаптеров, которые вы создали, чтобы убедиться, что они работают. И если они пройдут, вы можете быть уверены, что они будут работать независимо от того, где вы их называете в своем коде.
Так что, делая это таким образом, я никогда больше не буду if
в своем коде?
Нет. Вы менее склонны иметь условные выражения в своей бизнес-логике, но они все равно должны быть где-то. Место это ваши фабрики.
И это хорошо. Вы разделяете проблемы создания и фактического использования кода. Если вы делаете свои фабрики надежными (в Java вы можете даже пойти на то, чтобы использовать что-то вроде фреймворка Guice от Google), в вашей бизнес-логике вас не беспокоит выбор правильного класса для внедрения. Потому что вы знаете, что ваши фабрики работают и доставят то, что просят.
Нужно ли иметь все эти классы, интерфейсы и т. Д.?
Это возвращает нас к началу.
В ООП, если вы выбираете путь для использования полиморфизма, действительно хотите использовать шаблоны проектирования, хотите использовать возможности языка и / или хотите следовать всему, что является объектной идеологией, тогда это так. И даже тогда, этот пример не даже показать все заводы , которые вы собираетесь потребность , и если вы были рефакторить Compression
и Encryption
классы и сделать их интерфейсы вместо этого, вы должны включить их реализацию , а также.
В итоге вы получите сотни маленьких классов и интерфейсов, сфокусированных на очень специфических вещах. Что не обязательно плохо, но не может быть лучшим решением для вас, если все, что вам нужно, это сделать что-то простое, как сложение двух чисел.
Если вы хотите , чтобы сделать это и быстро, вы можете получить решение Ixrec в , который , по крайней мере удалось устранить else if
и else
блоки, которые, на мой взгляд, даже чуть - чуть хуже , чем в равнине if
.
Примите во внимание, что это мой способ сделать хороший дизайн ОО. Кодирование интерфейсов, а не реализаций, это то, как я делал это в течение последних нескольких лет, и этот подход мне наиболее удобен.
Мне лично больше нравится программирование if-less, и я был бы очень признателен за более длинное решение из 5 строк кода. Я привык к проектированию кода, и мне очень удобно его читать.
Обновление 2: была дикая дискуссия о первой версии моего решения. Обсуждение в основном вызвано мной, за что я прошу прощения.
Я решил отредактировать ответ таким образом, чтобы это был один из способов взглянуть на решение, но не единственный. Я также удалил часть декоратора, где вместо этого я имел в виду фасад, который я в конце концов решил полностью исключить, потому что адаптер - это вариант фасада.
if
заявления?