Полимерная улица — Дмитрий Рогачёв — LiveJournal
Достижения науки и техники чаще всего запоминаются в именах создателей, первооткрывателей или руководителей больших коллективов научных-предприятий. Улица академика Курчатова тому пример. Можно вспомнить города Циолковский и Королёв. А тут в Новогиреево решили увековечить… полимеры. Точнее конечно же не их самих, а достижения советских учёных в этой области химической промышленности. Неподалеку располагался химический завод и, скорее всего, выдающихся людей в этой области производства здесь не было.1. Полимерная улица находится примерно на одинаковом расстоянии от метро Перово и новогиреево. По ней проходят автобусы 7 и 620 от метро Перово, 36 от метро Новогиреево и 254 от Шоссе Энтузиастов.
2. Она начинается от Перовской улицы, продолжая Кусковскую.
3. Удивительно, но люди ходят здесь по льду. Я был один, кто прошёл по асфальту около самого дома. Возможно это из-за проблемы с сосульками, но не этой февральской весной.
4. Улица проходит вдоль промзоны, огибая её. Застройка разных лет. Самые большие дома — панельные девятиэтажки.
5.
6. Как мне показалось, застройка тут была как попало.
7. В одном месте тротуар и широкая зелёная полоса, а соседний дом уже в упор к дороге.
8. Местная автобусная остановка сохранила название «Полимерная улица — Объединение Узоры». Это швейкая фабрика, располагавшаяся в данном зании. Самое известное что им доверяли — знамёна для воинских частей. Есть ли тут сейчас реальное производство?
9.
10. В пристройке расположилась библиотека семейного чтения.
11.
12. Автобусная остановка расположилась около перекрёстка с Новогиреевской улицей.
13. Отремонтированное бело-желтое здание занимает детская школа искусств имени Римского-Корсакова.
14. Небольшой квартал 1959 года
15. А рядом двухэтажка за высоким забором с колючей проволокой.
16. Это здание занимает ФГУП ГЦ «Природа». Карты рисуют.
17. Сохранились старые указатели! До них явно просто не долезли при уничтожении.
18. По соседству пристроился Мосгаз.
19.
20. На пересечении с Мартеновской улицей наша прогулка заканчивается.
Если Вы что-то знаете об этом месте — расскажите в комментариях! Вместе мы узнаем о городе больше!
Если Вас интересуют какие-либо вопросы, есть интересные предложения или хочется что-то сказать — меня легко найти в социальных сетях:
ВКонтакте, Facebook, Instagram, Google+, Twitter, Tumblr, Youtube. Подписывайтесь!
По вопросам сотрудничества обращаться по электронной почте: sk0rp699[dog]mail.ru.
Переулков много не бывает. Вернёмся к ним, небольшим…
Спасибо за внимание! Оставайтесь на связи!
самые древние, самые необычные и самые красивые люки Москвы
По данным последней «переписи» Мосводоканала, количество водопроводных и канализационных люков в столице приближается к полумиллиону.
Для чего в чугун вбивали дерево, а на люки ставили геометки
Первые попытки оградить доступ горожан к внутреннему миру подземных нечистот случились еще более двух тысяч лет назад в Древнем Риме. Но так глубоко мы копать не будем, а начнем наше погружение в историю столичной канализационной атрибутики с 1898 года, когда в Москве была запущена 1-я очередь городской канализации. Это значит, что самым первым люкам сейчас – 120 лет!
Невероятно, но каким-то чудом в старых кварталах первопрестольной до сих пор сохранилось около десятка таких чугунных долгожителей, которые в прежние годы называли «менажницами» – из-за формы крышки с характерными углублениями. Эти полости были сделаны не для красоты, а для комфорта: в них вбивались деревянные бруски, которые зимой не скользили, а летом смягчали удары лошадиных копыт.
«Менажница» ─ старейший дизайн московского люка. Фото: leonovvaleri.livejournal.comВот уже вторую сотню лет ржавые, но все еще крепкие «родоначальники» лежат на территории Боткинской больницы, на Покровке и Покровском бульваре, во дворе дореволюционных домов в Большом Казенном переулке и недалеко от храма святого Николая в Хамовниках. До сих пор на них можно рассмотреть литеры «ГК», которые расшифровываются как «Городская канализация». Хотя, при таком стаже возможно и другое прочтение – допустим, «гордость коллектива».
Еще один интересный и по-своему уникальный экземпляр был обнаружен по адресу 2-й Кадашевский переулок, д. 14, стр. 3. Этот узорчатый люк закрывает доступ к телефонным проводам, и выделяется своей навечно застывшей в чугуне геометкой – точно указанным адресом местонахождения.
Люк по адресу: 2-й Кадашевский переулок, д. 14, стр. 3. Фото: esharova.ru
Антикварная загадка
Канализационные люки «Мюръ и Мерилизъ» имеют богатую родословную и непростую судьбу: до революции основатели фирмы Эндрю Мюр и Арчибальд Мерилиз были известными предпринимателями, благодаря которым на Кузнецком мосту появился главный магазин страны – сегодняшний ЦУМ. В 1903-1912 годах чугунных крышек водосточных колодцев с рекламой «Мюръ и Мерилизъ» по Москве было установлено немало. До наших дней дожили люки в Голиковском переулке Замоскворечья, на ул. Жуковского, Чаянова, во Вспольном переулке и на Малой Пироговской.
Сотню лет назад московские извозчики загадывали приезжим загадку: «А вот у нас на Москве по дороге рубли алексеевские валяются. Большие, завидные. Да вот захочешь поднять — пупок развяжется. А коли подымешь — вовек не расплатишься». Видимо, современные москвичи отгадали правильный ответ, и решили рискнуть пупком: в начале 2019 года с Тимирязевской улицы пропал один из редких антикварных канализационных люков Мюра и Мерилиза, который воришки тут же выставили на интернет-аукционе.
Откуда пошли волны и кто «украл» молнии
Воровство чугунных крышек – это, конечно, горе люковое… Но если вам кажется, что современные люки специально имеют невзрачный вид, чтобы не оказаться в пункте приема чермета, то вам это только кажется. Потому что, например, магистральному люку весом в 200 с лишним килограммов, который способен выдержать нагрузку в 40 тонн, особо не до красоты.
Фото: Shutterstock.com
На самом деле характерные орнаменты для чугунных крышек придумали еще в XIX веке, когда сети городских коммуникаций стали прокладывать под землей не только водопроводчики, но и электрики, и связисты. Чтобы быстро – буквально с полувзгляда – определять «своего среди чужих», для обозначения водопровода выбрали графичный рельеф волн и турбины, «вафли» навечно закрепились за городской канализацией, а телефонные компании стали «рисовать» на крышках паутину или молнии.
В народе «молниеносные» крышки прозвали «тараканами» и «медузами», и сегодня их можно встретить на территории Тимирязевской академии, рядом с пожарной каланчей на ул. Русаковская, д. 26, в Медовом переулке, д. 12, на Беговой, Новослободской и Ленинской Слободе. Аббревиатура на них – «НКС СССР» – расшифровывается как «Народный комиссариат связи», но сам логотип бесцеремонно позаимствован большевиками с еще дореволюционных, буржуйских люков «Шведско-датско-русского телефонного акционерного общества», один из которых с 1901 года лежит возле магазина «Булошная» в Лялином переулке.
Люк около «Булошной» в Лялином переулке. Фото: Shutterstock.com
Зачем кошку скрестили с топором и где в Москве лежат люки из Европы
Гуляя по Маяковке, Новой Басманной, Земляному Валу или Садово-Черногрязской, внимательно смотрите под ноги – и увидите на люках загадочное изображение… нет, не серпа и молота, а скрещенных топора и кошки.
Это – «революционная» эмблема «Народного комиссариата путей сообщения», а еще раньше, с 1830 года – ведомства путей сообщения Российской империи. Дело в том, что первая железная Царскосельская дорога в нашей стране открылась только в 1837-ом, а до этого в путевое хозяйство входили мосты, которые символизировал топор; и речное сообщение, привычно обозначаемое якорем, как тогда говорили – «кошкой». Железнодорожники «свои» люки помечали знаком «топора и кошки» до 1932 года, а затем получили новый символ – скрещенный гаечный ключ и молоток.
Фото: Shutterstock.com
Есть в Москве и «залетные» люки-иностранцы, о происхождении которых история умалчивает. Например, каким-то неведомым образом на брусчатке Цветного бульвара оказался кусочек берлинской канализации, о чем свидетельствует надпись «Kanalisation Berlin» и традиционное для немецкой геральдики изображение орла. Еще один люк родом из Германии замечен по надписи «KanalGuss» на Федеративном проспекте в Перово, а на Крымской набережной среди кубиков гранитной мостовой «приютилась» изящная решеточка компании «Humberg Metall und Kunstguss», которая производит уникальные системы для полива и вентиляции корней деревьев в городской среде.
Водосточная решетка, выпущенная под польским брендом Sfero. Новослободская улица, 2016 год. Фото: Shutterstock.comНа Русаковской улице лежит чугунная крышка финского производства с изображением атомного ядра, из этой же страны озер привезли люки и строители ресторана «Макдоналдс» на Красной Пресне. У станции метро «Автозаводская», возле дома №1 на улице Мастеркова, в асфальт аккуратно закатали люк из польского города Стомпоркув, выпущенный к 50-летию голландской фирмы. Частенько на столичных дорогах попадаются и дождеприемники с отверстиями в виде паутины и скромной надписью «France»…
В мире людей: люки-хамелеоны и люки-попугаи
«Это хорошо, что вы такой зеленый и плоский!», – говаривала старуха Шапокляк крокодилу Гене. То же самое можно сказать о люках, установленных в густой траве на лужайках Тюфелевой рощи: чтобы не мозолить глаза посетителей своим присутствием, они маскируются под цвет травы. Кудрявыми завитками расписана и зеленая эко-крышка возле благоустроенного пруда в бывшем военном городе поселка Мосрентген. Если же вдруг заплутаете в парке Сокольники – верный путь к метро подскажет легкий садовый диск с компасом.
Люк в Мосрентгене. Фото: logoworks.narod.ru
Люк у ресторана «Старая башня». Фото: logoworks.narod.ru
Люк в парке «Сокольники». Фото: logoworks. narod.ru
А вот люк возле бизнес-центра «The Yard» – как будто только что из Лондона: он настолько яркий и настолько желтый, что просто невозможно пройти мимо и не заметить его. Посетители ресторана «Старая башня» на Театральной площади также наверняка запомнили красный люк у входа, на котором изображена Круглая, она же Заиконоспасская башня стены Китай-города, построенная в начале XVI века, снесенная в 1934-ом, и воссозданная в 1997 году.
Люки на территории бывшей кондитерской фабрики «Большевик» на Ленинградском проспекте – пожалуй, самые концептуальные в Москве. Вероятно, новое соседство обязывает: сегодня в этом культурно-деловом комплексе работает Музей русского импрессионизма, где выставлена интересная коллекция русской живописи и графики конца XIX — начала XX века. А когда на месте бывшей кондитерской фабрики «Красный Октябрь» на Болотной набережной открылся арт-кластер, его дорожки также осветили светлые люки с узнаваемым логотипом.
Люк у «Красного Октября». Фото: Shutterstock. com
Люк у БЦ «Большевик». Фото: logoworks.narod.ru
Не молчат и «музы торговли»: именные люки лежат у Усачевского рынка, ТЦ «Тульский» и «Шереметьевский», гостиницы на Никольской улице, в бизнес-парках «Фактория» и «Wall Street», и на дорожках жилого квартала «Савеловский Сити».
Люки класса люкс под ногами москвичей
Однако, тенденция: во многих прогрессивных городах мира люки уже давно не воспринимаются как скучный предмет с узкой функцией. В Японии, Америке и Европе это настоящие арт-объеты, рядом с которыми и селфи сделать не стыдно. В итальянском городе Феррара канализационные люки и ливневые решетки – экспонаты музея, коллекция которого насчитывает более 130 крышек со всего света. А если бы вы встретили на улицах Москвы люк из швейцарского города Шаффхаузен, то удивились бы неожиданно знакомому изображению: как и на гербе столицы России, на нем изображен Георгий Победоносец, побеждающий змия.
Накладка дренажной системы у Храма Христа Спасителя. Фото: logoworks. narod.ruКстати, несколько лет назад аналогичная концепция оформления уже обсуждалась в среде промышленных дизайнеров, которые разработали эскизы «нового лица» люков по мотивам московского герба. Однако до бесшабашной толерантности европейцев нам еще далеко, и главным аргументом противников этой идеи стала фраза «Негоже православным святые лики топтать». Компромисс между стильным дизайном и традиционной религией так и не был достигнут, поэтому пока верующим остается любоваться нейтрально-декоративными узорами накладки дренажной системы площадки у храма Христа Спасителя.
Тем не менее, урбанисты верят, что в недалеком будущем и наши люки превратятся из серой безликой массы в заметный элемент городской инфраструктуры. Тем более, что уже сейчас под ногами москвичей встречаются редкие дизайнерские экземпляры. В 2013 году, к 190-летней годовщине со дня основания Александровского сада в нем появились памятные полимерные люки. При благоустройстве общественного пространства у метро «Чистые Пруды» в гранитные плиты вмонтировали «навигационные» люки с надписью «Бульварное кольцо 2016». А студенты Института отраслевого менеджмента РАНХиГС дружно «вращают планету ногами»: на территории вуза лежат сразу несколько люков с изображением Земли.
Памятный люк в Александровском саду. Фото: Shutterstock.com
В 2018 году в парке «Зарядье» вместо привычно-невзрачных крышек установили красивые люки с орнаментами животных и растений. Там же в прошлом году были замечены чугунные крышки с надписью «Moscow Urban Forum 2018» – они встречали посетителей масштабного мероприятия, на котором специалисты рассуждали о мегаполисе будущего и новом пространстве для жизни.
Люк, установленный в парке «Зарядье» к Moscow Urban Forum 2018. Фото: msk_decorstudio
Крышки с фирменным знаком установили и на территории ВДНХ – так сказать, последний штрих масштабной реконструкции. Оригинальные птички застыли в металле на смотровой площадке на Воробьевых горах. А Мосводоканал в честь 120-летия столичной канализации выпустил юбилейную серию из 200 экземпляров: на «обложке» каждого люка красуется фасад главной насосной станции в Саринском проезде.
Люк на Воробьевых горах. Фото: Shutterstock.com
Почему люки круглые и как расшифровать их «инициалы»
Вообще, люки бывают и квадратные, и треугольные, и даже бочкообразные. Но все же идеальная форма крышки – именно круглая, потому что, во-первых, по законам геометрии люк круглой формы никак не может провалиться в меньший диаметр, как его ни крути. Во-вторых, по законам физики, круглая форма лучше любой другой распределят нагрузку, тогда как квадратный трещит по швам, а точнее по углам. А кроме этого производство круглой формы обходится на треть дешевле той же квадратной, да и перемещать круглый люк легче – его можно перекатить. Люки прямолинейных форм пусть и редко, но все же устанавливают на пешеходных тротуарах, где нагрузка на них небольшая, зато удобнее обкладывать плиткой.
Д ─ дренаж. Фото: Shutterstock.com
К ─ канализация. Фото: Shutterstock.com
ГТС ─ городская телефонная сеть. Фото: Shutterstock.com
В ─ водопровод. Фото: Shutterstock.com
Напоследок раскроем главный секрет – как расшифровывать буквы на люках. Все просто, как в Азбуке: В – это водопровод, К – канализация, Д – дренаж (он же дождевая канализация), ТС – теплосеть, ГС – газоснабжение, Т – телефон, а ГТС – городская телефонная сеть, Г или ПГ — пожарный гидрант. Аббревиатура ТСОД означает «Технические Средства Обеспечения Движения», то есть светофорную кабельную сеть. Интересно, что по правилам буквенный шифр должен обязательно присутствовать на любом, даже на дизайнерском люке, причем, находиться на воображаемой линии, соединяющей ушки крышки.
Ну что же, теперь все тайны люка открыты и, поверьте – отныне мир для вас уже никогда не станет прежним: обладая этими знаниями, вы станете то и дело указывать своим спутникам на обычные и выдающиеся крышки, с видом бывалого инженера-коммунальщика объясняя их истинное предназначение.
Как доехать до дома на Полимерной улице дом 7 общественным транспортом
Ближайшая к дому на Полимерной улице д.7 в Москве станция метро Перово в 950 метрах, до него можно дойти пешком за 14 минут пешком, дойти можно еще до станции метро Новогиреево за 19 минут.
Метро рядом
Перово | 950 м | 14 мин |
Новогиреево | 1300 м | 19 мин |
ЖД станции рядом
Кусково | 850 м | 13 мин |
Наземный транспорт от метро
ПеровоМаршруты | От метро | До остановки | |
---|---|---|---|
620 842 |
Перово Автобус |
Улица Новотетёрки (3 остановки) |
|
7 |
Перово Автобус |
Полимерная улица — Объединение Узоры (35 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
36 |
Новогиреево Автобус |
Полимерная улица — Объединение Узоры (5 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
т53 |
Шоссе Энтузиастов Автобус |
Улица Новотетёрки (11 остановок) |
|
36 |
Шоссе Энтузиастов Автобус |
Улица Новотетёрки (13 остановок) | |
254 |
Шоссе Энтузиастов Автобус |
Улица Новотетёрки (14 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
620 |
Выхино Автобус |
Полимерная улица — Объединение Узоры (13 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
7 |
Щелковская Автобус |
Полимерная улица — Объединение Узоры (14 остановок) |
|
36 |
Щелковская Автобус |
Улица Новотетёрки (36 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
т53 |
Авиамоторная Автобус |
Улица Новотетёрки (15 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
7 |
Измайлово Автобус |
Полимерная улица — Объединение Узоры (16 остановок) |
|
36 |
Измайлово Автобус |
Улица Новотетёрки (38 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
т53 |
Автобус |
Улица Новотетёрки (18 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
т53 |
Римская Автобус |
Улица Новотетёрки (19 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
т53 |
Площадь Ильича Автобус |
Улица Новотетёрки (20 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
т53 |
Таганская Автобус |
Улица Новотетёрки (25 остановок) |
Маршруты | От метро | До остановки | |
---|---|---|---|
254 |
Семеновская Автобус |
Улица Новотетёрки (26 остановок) |
|
36 |
Семеновская Автобус |
Улица Новотетёрки (33 остановки) |
Наземный транспорт от ЖД станций
ВешнякиМаршруты | От станции | До остановки | |
---|---|---|---|
620 |
Вешняки Автобус |
Полимерная улица — Объединение Узоры (10 остановок) |
Маршруты | От станции | До остановки | |
---|---|---|---|
т53 |
Новая Автобус |
Улица Новотетёрки (15 остановок) |
Маршруты | От станции | До остановки | |
---|---|---|---|
т53 |
Серп и Молот Автобус |
Улица Новотетёрки (20 остановок) |
Подробная информация о том как добраться до дома Москва, Полимерная улица дом 7 от станций метро или железной дороги. Какой вид и номер маршрута городского транспорта удобнее использовать. На каком расстоянии находится ближайшая остановка к дому на Полимерной улице д.7. Какое количество остановок нужно проехать на общественном транспорте и на какой остановке выйти, что бы доехать до Полимерная улица дом 7 в Москве.
Полимерная улица дом 7 — на карте
Обзор шаблона проектирования Factory Method | Аршад Сурадж | Geek Culture
Изображение предоставлено — googleЗаводской метод — это шаблон проектирования, который предоставляет интерфейс или абстрактный класс для создания объекта и позволяет его подклассам решать, какой класс должен быть создан. Заводской образец классифицируется как образец создания .
Диаграмма классов шаблона проектирования фабричного методаДля облегчения понимания предположим, что в магазине есть множество товаров, и продавец доставляет нам то, что нам нужно, исходя из наших требований. Точно так же работает заводская выкройка. Обычно мы передаем параметры. Наш фабричный метод определяет, какой класс должен быть создан, на основе переданного нами параметра, а также создает и предоставляет экземпляр этого конкретного класса.
Что следует иметь в виду:
- Все подклассы
Пример: GoldCard, SilverCard и т. Д.
должны расширять один и тот же родительский классПример: CardType
. - Определите фабричный метод внутри класса Factory.Тип возвращаемого значения метода должен быть родительским классом
Пример: CardType
. - Разрешить подклассам создавать объект на основе параметра, переданного фабричному методу.
Давайте реализуем код, чтобы увидеть, как работает шаблон проектирования Factory method.
Предположим, банк предлагает своим клиентам кредитные карты. Они предлагают 3 типа кредитных карт, такие как карты Silver, Gold и Platinum. Каждый тип карты имеет свой кредитный лимит
Например: кредитный лимит серебряной карты составляет 100 000 LKR, кредитный лимит золотой карты составляет 250 000 LKR, а кредитный лимит карты Platinum составляет 250 000 LKR
.Тип карты, которую получает клиент, зависит от его или ее ежемесячной заработной платы. Серебряная карта доступна любому, у кого ежемесячная зарплата менее 50 000 LKR. Он может получить карту Gold, если его ежемесячный доход менее 100 000 LKR. Он имеет право на получение карты Platinum, если его ежемесячная зарплата превышает 100 000 LKR.
Для EX: Если ваша зарплата составляет 85 000 LKR, вы можете получить карту Gold
Теперь давайте закодируем приведенный выше сценарий с помощью шаблона проектирования Factory method.
Фрагмент кода для родительского класса всех подклассовВыше показан родительский абстрактный класс для всех подклассов.
фрагмент кода подкласса 1 фрагмент кода подкласса 2 фрагмент кода подкласса 3Выше показаны подклассы, которые происходят от того же родительского класса.
Класс Factory, который содержит метод Factory, показан ниже,
Фрагмент кода класса FactoryЭтот метод отвечает за определение класса, для которого нужно создать экземпляр . Этот метод создаст и вернет экземпляр соответствующего класса на основе предоставленного ему параметра .
ниже — это основной класс,
Выходы для приведенных выше кодов приведены ниже,
Выход 1 Выход 2 Выход 3В результате мы можем использовать шаблон проектирования Factory для извлечения различных объектов из разных классов на основе нашего параметра. Заводской шаблон решит, какой класс создать, не показывая детали реализации клиенту.
- Шаблон фабричного метода позволяет подклассам выбирать тип создаваемых объектов.
- Реализация взаимодействует только с родительским абстрактным классом или интерфейсом.Следовательно, он может работать с любыми классами, реализующими этот интерфейс или расширяющими этот абстрактный класс.
- Когда класс не знает, какой подкласс должен создать экземпляр.
- Когда класс хочет, чтобы его подклассы указывали создаваемые объекты.
- Когда родительские классы выбирают создание объектов для своих подклассов.
- , если вы хотите предложить пользователям своей библиотеки возможность расширения ее внутренних компонентов.
Этот шаблон проектирования обычно используется в JDK, например: метод java getInstance ().util.Calendar, NumberFormat и ResourceBundle используют шаблон проектирования фабричного метода.
Заводской метод
Также известен как: Virtual Constructor
Намерение
Factory Method — это шаблон проектирования, который предоставляет интерфейс для создания объектов в суперклассе, но позволяет подклассам изменять тип создаваемых объектов.
Проблема Представьте, что вы создаете приложение для управления логистикой. Первая версия вашего приложения может обрабатывать только транспортировку грузовиками, поэтому основная часть вашего кода находится внутри класса Truck
.
Через некоторое время ваше приложение станет довольно популярным. Каждый день вы получаете десятки запросов от морских транспортных компаний о включении морской логистики в приложение.
Добавить новый класс в программу не так просто, если остальной код уже связан с существующими классами.
Отличные новости, правда? А как насчет кода? В настоящее время большая часть вашего кода связана с классом Truck
.Добавление Ships
в приложение потребует внесения изменений во всю кодовую базу. Более того, если позже вы решите добавить в приложение еще один вид транспорта, вам, вероятно, придется снова внести все эти изменения.
В результате вы получите довольно неприятный код, пронизанный условными выражениями, которые переключают поведение приложения в зависимости от класса транспортных объектов.
Решение Шаблон Factory Method предлагает заменить прямые вызовы построения объекта (с использованием оператора new
) вызовами специального метода factory .Не волнуйтесь: объекты по-прежнему создаются с помощью оператора new
, но он вызывается из фабричного метода. Объекты, возвращаемые фабричным методом, часто называются продуктами .
Подклассы могут изменять класс объектов, возвращаемых фабричным методом.
На первый взгляд это изменение может показаться бессмысленным: мы просто переместили вызов конструктора из одной части программы в другую. Однако учтите следующее: теперь вы можете переопределить фабричный метод в подклассе и изменить класс продуктов, создаваемых этим методом.
Есть небольшое ограничение: подклассы могут возвращать разные типы продуктов, только если эти продукты имеют общий базовый класс или интерфейс. Кроме того, тип возвращаемого значения для фабричного метода в базовом классе должен быть объявлен как этот интерфейс.
Все продукты должны иметь один и тот же интерфейс.
Например, классы Truck
и Ship
должны реализовывать интерфейс Transport
, который объявляет метод под названием delivery
.Каждый класс реализует этот метод по-разному: грузовики доставляют грузы по суше, корабли доставляют грузы по морю. Заводской метод в классе RoadLogistics
возвращает объекты грузовика, тогда как заводской метод в классе SeaLogistics
возвращает корабли.
Пока все классы продуктов реализуют общий интерфейс, вы можете передавать их объекты в клиентский код, не нарушая его.
Код, использующий фабричный метод (часто называемый кодом клиента ), не видит разницы между фактическими продуктами, возвращаемыми различными подклассами.Клиент рассматривает все продукты как абстрактные Транспорт
. Клиент знает, что все транспортные объекты должны иметь метод доставки
, но то, как именно он работает, не имеет значения для клиента.
Продукт объявляет интерфейс, который является общим для всех объектов, которые могут быть созданы создателем и его подклассами.
Конкретные изделия — это различные реализации интерфейса продукта.
Класс Creator объявляет фабричный метод, который возвращает новые объекты продукта. Важно, чтобы тип возвращаемого значения этого метода соответствовал интерфейсу продукта.
Вы можете объявить фабричный метод абстрактным, чтобы заставить все подклассы реализовывать свои собственные версии метода. В качестве альтернативы базовый фабричный метод может возвращать некоторый тип продукта по умолчанию.
Примечание, несмотря на название, создание продукта , а не является основной обязанностью создателя.Обычно класс создателя уже имеет некоторую базовую бизнес-логику, связанную с продуктами. Фабричный метод помогает отделить эту логику от конкретных классов продуктов. Приведем аналогию: в крупной компании по разработке программного обеспечения может быть отдел обучения программистов. Однако основной функцией компании в целом по-прежнему является написание кода, а не подготовка программистов.
Concrete Creators переопределяет базовый фабричный метод, поэтому он возвращает другой тип продукта.
Обратите внимание, что заводской метод не должен постоянно создавать новых экземпляра. Он также может возвращать существующие объекты из кеша, пула объектов или другого источника.
В этом примере показано, как можно использовать фабричный метод для создания кроссплатформенных элементов пользовательского интерфейса без привязки клиентского кода к конкретным классам пользовательского интерфейса.
Пример кроссплатформенного диалога.
Базовый класс диалогового окна использует различные элементы пользовательского интерфейса для визуализации своего окна. В разных операционных системах эти элементы могут выглядеть немного по-разному, но они все равно должны вести себя одинаково. Кнопка в Windows по-прежнему остается кнопкой в Linux.
Когда в игру вступает заводской метод, вам не нужно переписывать логику диалогового окна для каждой операционной системы. Если мы объявим фабричный метод, который производит кнопки внутри базового диалогового класса, мы можем позже создать подкласс диалога, который возвращает кнопки в стиле Windows из фабричного метода. Затем подкласс наследует большую часть кода диалогового окна от базового класса, но, благодаря фабричному методу, может отображать на экране кнопки, похожие на Windows.
Чтобы этот шаблон работал, базовый класс диалогового окна должен работать с абстрактными кнопками: базовый класс или интерфейс, которому следуют все конкретные кнопки. Таким образом, код диалога остается функциональным, с каким бы типом кнопок он ни работал.
Конечно, вы можете применить этот подход и к другим элементам пользовательского интерфейса. Однако с каждым новым фабричным методом, который вы добавляете в диалог, вы приближаетесь к паттерну «Абстрактная фабрика». Не бойтесь, мы поговорим об этом позже.
// Класс-создатель объявляет фабричный метод, который должен
// возвращаем объект класса продукта.Подклассы создателя
// обычно предоставляем реализацию этого метода.
класс Dialog - это
// Создатель также может предоставить некоторую реализацию по умолчанию
// фабричного метода.
абстрактный метод createButton (): Button
// Обратите внимание, что, несмотря на название, основной
// ответственность не создает продукты. Обычно это
// содержит некоторую базовую бизнес-логику, которая зависит от продукта
// объекты, возвращаемые фабричным методом. Подклассы могут
// косвенно изменить эту бизнес-логику, переопределив
// фабричный метод и возврат другого типа продукта
// от него.метод render () - это
// Вызов фабричного метода для создания объекта продукта.
Кнопка okButton = createButton ()
// Теперь воспользуемся продуктом.
okButton.onClick (closeDialog)
okButton.render ()
// Конкретные создатели переопределяют фабричный метод, чтобы изменить
// тип результирующего продукта.
класс WindowsDialog расширяет диалог
метод createButton (): кнопка
вернуть новый WindowsButton ()
класс WebDialog расширяет диалог
метод createButton (): кнопка
вернуть новый HTMLButton ()
// Интерфейс продукта объявляет операции, которые все
// бетонные изделия необходимо реализовать.кнопка интерфейса
метод render ()
метод onClick (f)
// Бетонные изделия обеспечивают различные реализации
// интерфейс продукта.
Класс WindowsButton реализует кнопку.
метод render (a, b)
// Отрисовываем кнопку в стиле Windows.
метод onClick (f) - это
// Привязать событие щелчка собственной ОС.
Класс HTMLButton реализует кнопку.
метод render (a, b)
// Возвращаем HTML-представление кнопки.
метод onClick (f) - это
// Привязать событие щелчка в веб-браузере.класс Application - это
диалоговое окно поля: Диалог
// Приложение выбирает тип создателя в зависимости от
// текущая конфигурация или настройки среды.
метод initialize () - это
config = readApplicationConfigFile ()
если (config.OS == "Windows") то
dialog = новый WindowsDialog ()
иначе, если (config.OS == "Интернет"), то
dialog = новый WebDialog ()
еще
выбросить новое исключение («Ошибка! Неизвестная операционная система.»)
// Клиентский код работает с экземпляром конкретного
// создатель, хотя и через его базовый интерфейс.Пока
// клиент продолжает работать с создателем через базу
// интерфейс, вы можете передать ему любой подкласс создателя.
метод main () - это
this.initialize ()
dialog.render ()
ПрименимостьИспользуйте фабричный метод, если вы заранее не знаете точные типы и зависимости объектов, с которыми должен работать ваш код.
Заводской метод отделяет код конструкции продукта от кода, который фактически использует продукт.Поэтому код построения продукта проще расширить независимо от остального кода.
Например, чтобы добавить в приложение новый тип продукта, вам нужно всего лишь создать новый подкласс создателя и переопределить в нем фабричный метод.
Используйте фабричный метод, если вы хотите предоставить пользователям своей библиотеки или платформы способ расширения ее внутренних компонентов.
Наследование, вероятно, самый простой способ расширить поведение библиотеки или фреймворка по умолчанию.Но как фреймворк распознает, что ваш подкласс следует использовать вместо стандартного компонента?
Решение состоит в том, чтобы сократить код, конструирующий компоненты в рамках платформы, до единого фабричного метода и позволить любому переопределить этот метод в дополнение к расширению самого компонента.
Посмотрим, как это будет работать. Представьте, что вы пишете приложение, используя платформу пользовательского интерфейса с открытым исходным кодом. В вашем приложении должны быть круглые кнопки, но в фреймворке есть только квадратные.Вы расширяете стандартный класс Button
великолепным подклассом RoundButton
. Но теперь вам нужно указать основному классу UIFramework
использовать новый подкласс кнопки вместо подкласса по умолчанию.
Для этого вы создаете подкласс UIWithRoundButtons
из базового класса инфраструктуры и переопределяете его метод createButton
. Хотя этот метод возвращает объекты Button
в базовом классе, вы заставляете свой подкласс возвращать объекты RoundButton
.Теперь используйте класс UIWithRoundButtons
вместо UIFramework
. Вот и все!
Используйте заводской метод, если вы хотите сэкономить системные ресурсы, повторно используя существующие объекты, вместо того, чтобы каждый раз перестраивать их.
Вы часто испытываете эту потребность при работе с большими ресурсоемкими объектами, такими как соединения с базами данных, файловые системы и сетевые ресурсы.
Давайте подумаем, что нужно сделать, чтобы повторно использовать существующий объект:
- Во-первых, вам нужно создать хранилище, чтобы отслеживать все созданные объекты.
- Когда кто-то запрашивает объект, программа должна искать свободный объект внутри этого пула.
- … а затем вернуть его в код клиента.
- Если свободных объектов нет, программа должна создать новый (и добавить его в пул).
Это много кода! И все это должно быть собрано в одном месте, чтобы вы не засоряли программу дублированием кода.
Вероятно, наиболее очевидным и удобным местом, где можно было бы разместить этот код, является конструктор класса, объекты которого мы пытаемся повторно использовать.Однако конструктор всегда должен возвращать новых объектов по определению. Он не может вернуть существующие экземпляры.
Следовательно, вам нужен обычный метод, способный создавать новые объекты, а также повторно использовать существующие. Это очень похоже на фабричный метод.
Как реализоватьСделайте так, чтобы все продукты следовали одному интерфейсу. Этот интерфейс должен объявлять методы, которые имеют смысл в каждом продукте.
Добавьте пустой фабричный метод в класс создателя.Тип возвращаемого значения метода должен соответствовать общему интерфейсу продукта.
В коде создателя найдите все ссылки на конструкторы продукта. Один за другим замените их вызовами фабричного метода, извлекая код создания продукта в фабричный метод.
Возможно, вам потребуется добавить временный параметр к фабричному методу для управления типом возвращаемого продукта.
На этом этапе код фабричного метода может выглядеть довольно некрасиво. У него может быть большой оператор
switch
, который выбирает, какой класс продукта создать. Но не волнуйтесь, скоро мы это исправим.Теперь создайте набор подклассов-создателей для каждого типа продукта, указанного в заводском методе. Переопределите фабричный метод в подклассах и извлеките соответствующие биты кода построения из базового метода.
Если существует слишком много типов продуктов и нет смысла создавать подклассы для всех из них, вы можете повторно использовать управляющий параметр из базового класса в подклассах.
Например, представьте, что у вас есть следующая иерархия классов: базовый класс
Mail
с парой подклассов:AirMail
иGroundMail
; классыTransport
— этоPlane
,Truck
иTrain
.В то время как классAirMail
использует только объектыPlane
,GroundMail
может работать как с объектамиTruck
, так и с объектамиTrain
. Вы можете создать новый подкласс (скажем,TrainMail
) для обработки обоих случаев, но есть и другой вариант. Код клиента может передать аргумент фабричному методу классаGroundMail
, чтобы контролировать, какой продукт он хочет получить.Если после всех извлечений базовый метод фабрики стал пустым, вы можете сделать его абстрактным.Если что-то осталось, вы можете сделать это поведением метода по умолчанию.
- Вы избегаете тесной связи между создателем и конкретными изделиями.
- Принцип единственной ответственности . Вы можете переместить код создания продукта в одно место в программе, что упростит поддержку кода.
- Принцип открытия / закрытия . Вы можете вводить в программу новые типы продуктов, не нарушая существующий клиентский код.
- Код может стать более сложным, поскольку вам нужно ввести много новых подклассов для реализации шаблона. В лучшем случае вы вводите шаблон в существующую иерархию классов-создателей.
Многие проекты начинаются с использования фабричного метода (менее сложного и более настраиваемого с помощью подклассов) и развиваются в абстрактную фабрику, прототип или конструктор (более гибкий, но более сложный).
Классы абстрактной фабрики часто основаны на наборе фабричных методов, но вы также можете использовать Prototype для компоновки методов в этих классах.
Вы можете использовать Factory Method вместе с Iterator, чтобы подклассы коллекций возвращали различные типы итераторов, совместимых с коллекциями.
Prototype не основан на наследовании, поэтому у него нет недостатков. С другой стороны, Prototype требует сложной инициализации клонированного объекта.Заводской метод основан на наследовании, но не требует шага инициализации.
Factory Method — это специализация шаблонного метода. В то же время заводской метод может служить шагом в большом методе шаблона .
Шаблон фабричного метода и его реализация на Python — Real Python
В этой статье исследуется шаблон проектирования Factory Method и его реализация в Python.Шаблоны проектирования стали популярной темой в конце 90-х после того, как так называемая «Банда четырех» (GoF: Gamma, Helm, Johson и Vlissides) опубликовала свою книгу «Шаблоны проектирования: элементы многоразового объектно-ориентированного программного обеспечения».
В книге шаблоны проектирования описываются как основное дизайнерское решение для повторяющихся проблем в программном обеспечении, и каждый шаблон проектирования классифицируется по категориям в соответствии с характером проблемы. Каждому шаблону дается имя, описание проблемы, дизайнерское решение и объяснение последствий его использования.
Книга GoF описывает фабричный метод как творческий шаблон проектирования. Шаблоны проектирования создания связаны с созданием объектов, а фабричный метод — это шаблон проектирования, который создает объекты с общим интерфейсом.
Это повторяющаяся проблема, из-за которой фабричный метод является одним из наиболее широко используемых шаблонов проектирования , и очень важно понимать ее и знать, как ее применять.
К концу этой статьи у вас будет :
- Понимание компонентов заводского метода
- Распознать возможности использования заводского метода в ваших приложениях
- Научитесь изменять существующий код и улучшать его дизайн с помощью шаблона
- Научитесь определять возможности, в которых заводской метод является подходящим шаблоном проектирования
- Выберите подходящую реализацию заводского метода
- Знать, как реализовать многоразовое универсальное решение Factory Method
Бесплатный бонус: 5 мыслей о Python Mastery, бесплатный курс для разработчиков Python, который показывает вам план действий и образ мышления, которые вам понадобятся, чтобы вывести свои навыки Python на новый уровень.
Знакомство с заводским методом
Factory Method — это шаблон проектирования, используемый для создания конкретных реализаций общего интерфейса.
Он отделяет процесс создания объекта от кода, который зависит от интерфейса объекта.
Например, приложению требуется объект с определенным интерфейсом для выполнения своих задач. Конкретная реализация интерфейса идентифицируется некоторым параметром.
Вместо использования сложной условной структуры if / elif / else
для определения конкретной реализации приложение делегирует это решение отдельному компоненту, который создает конкретный объект.При таком подходе код приложения упрощается, что делает его более пригодным для повторного использования и упрощением обслуживания.
Представьте себе приложение, которому необходимо преобразовать объект Song
в его строковое представление , используя указанный формат. Преобразование объекта в другое представление часто называют сериализацией. Вы часто увидите, что эти требования реализованы в одной функции или методе, который содержит всю логику и реализацию, как в следующем коде:
# В serializer_demo.ру
импортировать json
импортировать xml.etree.ElementTree как et
класс Песня:
def __init __ (self, song_id, title, artist):
self.song_id = song_id
self.title = title
self.artist = художник
класс SongSerializer:
def сериализовать (self, song, format):
если format == 'JSON':
song_info = {
'id': song.song_id,
'title': song.title,
'исполнитель': song.artist
}
вернуть json.dumps (song_info)
elif format == 'XML':
song_info = et.Элемент ('песня', attrib = {'id': song.song_id})
title = et.SubElement (song_info, 'название')
title.text = song.title
исполнитель = et.SubElement (song_info, 'исполнитель')
artist.text = song.artist
вернуть et. tostring (song_info, encoding = 'unicode')
еще:
поднять ValueError (формат)
В приведенном выше примере у вас есть базовый класс Song
для представления песни и класс SongSerializer
, который может преобразовать объект song
в его строковое представление в соответствии со значением параметра формата .
Метод .serialize ()
поддерживает два разных формата: JSON и XML. Любой другой указанный формат
не поддерживается, поэтому возникает исключение ValueError
.
Давайте воспользуемся интерактивной оболочкой Python, чтобы увидеть, как работает код:
>>> >>> импортировать serializer_demo как sd
>>> song = sd.Song ('1', 'Вода любви', 'Ужасные проливы')
>>> сериализатор = sd.SongSerializer ()
>>> сериализатор.сериализовать (песня, 'JSON')
'{"id": "1", "title": "Вода любви", "artist": "Dire Straits"}'
>>> serializer. serialize (песня, 'XML')
' Вода любви Dire Straits '
>>> serializer.serialize (песня, 'YAML')
Отслеживание (последний вызов последний):
Файл "", строка 1, в
Файл "./serializer_demo.py", строка 30, в сериализации
поднять ValueError (формат)
ValueError: YAML
Вы создаете объект song
и сериализатор
, а затем конвертируете песню в ее строковое представление с помощью объекта .serialize ()
метод. Метод принимает объект song
в качестве параметра, а также строковое значение, представляющее желаемый формат. Последний вызов использует YAML
в качестве формата, который не поддерживается сериализатором
, поэтому возникает исключение ValueError
.
Этот пример короткий и упрощенный, но в нем все еще есть много сложностей. В зависимости от значения параметра формата существует три логических пути или пути выполнения. Это может показаться неважным, и вы, вероятно, видели код более сложный, чем этот, но приведенный выше пример по-прежнему довольно сложно поддерживать.
Проблемы со сложным условным кодом
В приведенном выше примере показаны все проблемы, которые вы обнаружите в сложном логическом коде. Сложный логический код использует структуры if / elif / else
для изменения поведения приложения. Использование условных структур if / elif / else
затрудняет чтение, понимание и сопровождение кода.
Приведенный выше код может показаться несложным для чтения или понимания, но подождите, пока вы не увидите окончательный код в этом разделе!
Тем не менее, приведенный выше код сложно поддерживать, потому что он делает слишком много. Принцип единой ответственности гласит, что модуль, класс или даже метод должны иметь единственную четко определенную ответственность. Он должен делать только одно и иметь только одну причину для изменения.
Метод . serialize ()
в SongSerializer
потребует изменений по разным причинам.Это увеличивает риск появления новых дефектов или нарушения существующей функциональности при внесении изменений. Давайте рассмотрим все ситуации, которые потребуют изменений в реализации:
Когда будет представлен новый формат: Метод необходимо будет изменить, чтобы реализовать сериализацию в этом формате.
При изменении объекта
Song
: Добавление или удаление свойств классаSong
потребует изменения реализации, чтобы приспособиться к новой структуре.При изменении строкового представления для формата (простой JSON против JSON API): Метод
.serialize ()
должен измениться, если желаемое строковое представление для формата изменится, потому что представление жестко закодировано вРеализация метода .serialize ()
.
Идеальная ситуация была бы, если бы любое из этих изменений требований могло быть реализовано без изменения метода . serialize ()
.Давайте посмотрим, как это сделать, в следующих разделах.
В поисках общего интерфейса
Первым шагом, когда вы видите сложный условный код в приложении, является определение общей цели каждого из путей выполнения (или логических путей).
Код, использующий if / elif / else
, обычно имеет общую цель, которая реализуется по-разному на каждом логическом пути. Приведенный выше код преобразует объект песни
в его представление строки
, используя другой формат в каждом логическом пути.
Исходя из поставленной цели, вы ищете общий интерфейс, который можно использовать для замены каждого из путей. В приведенном выше примере требуется интерфейс, который принимает объект песни
и возвращает строку
.
Если у вас есть общий интерфейс, вы предоставляете отдельные реализации для каждого логического пути. В приведенном выше примере вы предоставите реализацию для сериализации в JSON, а другую — для XML.
Затем вы предоставляете отдельный компонент, который определяет конкретную реализацию для использования на основе указанного формата
.Этот компонент оценивает значение формата
и возвращает конкретную реализацию, идентифицированную его значением.
В следующих разделах вы узнаете, как вносить изменения в существующий код без изменения поведения. Это называется рефакторингом кода.
Мартин Фаулер в своей книге «Рефакторинг: улучшение дизайна существующего кода» определяет рефакторинг как «процесс изменения программной системы таким образом, чтобы не изменять внешнее поведение кода, но улучшать его внутреннюю структуру.”
Давайте приступим к рефакторингу кода для достижения желаемой структуры, использующей шаблон проектирования Factory Method.
Реорганизация кода в требуемый интерфейс
Требуемый интерфейс — это объект или функция, которая принимает объект Song
и возвращает строковое представление .
Первым шагом является рефакторинг одного из логических путей в этот интерфейс. Вы делаете это, добавляя новый метод ._serialize_to_json ()
и перемещая в него код сериализации JSON.Затем вы меняете клиента для его вызова вместо реализации в теле оператора if
:
класс SongSerializer:
def сериализовать (self, song, format):
если format == 'JSON':
return self._serialize_to_json (песня)
# Остальной код остается прежним
def _serialize_to_json (я, песня):
payload = {
'id': song.song_id,
'title': song.title,
'исполнитель': song.artist
}
вернуть json.свалки (полезная нагрузка)
После внесения этого изменения вы можете убедиться, что поведение не изменилось. Затем вы делаете то же самое для опции XML, вводя новый метод ._serialize_to_xml ()
, перемещая в него реализацию и изменяя путь elif
для его вызова.
В следующем примере показан код после рефакторинга:
класс SongSerializer:
def сериализовать (self, song, format):
если format == 'JSON':
вернуть себя._serialize_to_json (песня)
elif format == 'XML':
return self._serialize_to_xml (песня)
еще:
поднять ValueError (формат)
def _serialize_to_json (я, песня):
payload = {
'id': song.song_id,
'title': song.title,
'исполнитель': song.artist
}
вернуть json.dumps (полезная нагрузка)
def _serialize_to_xml (сам, песня):
song_element = et.Element ('песня', attrib = {'id': song.song_id})
title = et.SubElement (элемент_песни, 'название')
заглавие.text = song.title
artist = et.SubElement (song_element, 'исполнитель')
artist.text = song.artist
вернуть et.tostring (song_element, encoding = 'unicode')
Новую версию кода легче читать и понимать, но ее можно улучшить с помощью базовой реализации фабричного метода.
Базовая реализация заводского метода
Центральная идея Factory Method — предоставить отдельный компонент, отвечающий за принятие решения о том, какую конкретную реализацию следует использовать на основе определенного параметра.В нашем примере этим параметром является формат
.
Чтобы завершить реализацию фабричного метода, вы добавляете новый метод ._get_serializer ()
, который принимает желаемый формат
. Этот метод оценивает значение формата
и возвращает соответствующую функцию сериализации:
класс SongSerializer:
def _get_serializer (сам, формат):
если format == 'JSON':
вернуть self._serialize_to_json
elif format == 'XML':
вернуть себя._serialize_to_xml
еще:
поднять ValueError (формат)
Примечание: Метод ._get_serializer ()
не вызывает конкретную реализацию, а просто возвращает сам объект функции.
Теперь вы можете изменить метод .serialize ()
объекта SongSerializer
на использование ._get_serializer ()
для завершения реализации заводского метода. В следующем примере показан полный код:
класс SongSerializer:
def сериализовать (self, song, format):
сериализатор = сам._get_serializer (формат)
вернуть сериализатор (песня)
def _get_serializer (сам, формат):
если format == 'JSON':
вернуть self._serialize_to_json
elif format == 'XML':
вернуть self._serialize_to_xml
еще:
поднять ValueError (формат)
def _serialize_to_json (я, песня):
payload = {
'id': song.song_id,
'title': song.title,
'исполнитель': song.artist
}
вернуть json.dumps (полезная нагрузка)
def _serialize_to_xml (сам, песня):
song_element = et.Элемент ('песня', attrib = {'id': song.song_id})
title = et. SubElement (элемент_песни, 'название')
title.text = song.title
artist = et.SubElement (song_element, 'исполнитель')
artist.text = song.artist
вернуть et.tostring (song_element, encoding = 'unicode')
Окончательная реализация показывает различные компоненты фабричного метода. Метод .serialize ()
— это код приложения, выполнение задачи которого зависит от интерфейса.
Это называется клиентский компонент шаблона.Определенный интерфейс упоминается как компонент продукта . В нашем случае продукт — это функция, которая принимает Song
и возвращает строковое представление.
Методы ._serialize_to_json ()
и ._serialize_to_xml ()
являются конкретными реализациями продукта. Наконец, метод ._get_serializer ()
— это компонент creator . Создатель решает, какую конкретную реализацию использовать.
Поскольку вы начали с некоторого существующего кода, все компоненты Factory Method являются членами одного класса SongSerializer
.
Обычно это не так, и, как видите, ни один из добавленных методов не использует параметр self
. Это хороший признак того, что они не должны быть методами класса SongSerializer
и могут стать внешними функциями:
класс SongSerializer:
def сериализовать (self, song, format):
сериализатор = get_serializer (формат)
вернуть сериализатор (песня)
def get_serializer (формат):
если format == 'JSON':
вернуть _serialize_to_json
elif format == 'XML':
вернуть _serialize_to_xml
еще:
поднять ValueError (формат)
def _serialize_to_json (песня):
payload = {
'id': песня.song_id,
'title': song.title,
'исполнитель': song.artist
}
вернуть json.dumps (полезная нагрузка)
def _serialize_to_xml (песня):
song_element = et.Element ('песня', attrib = {'id': song.song_id})
title = et.SubElement (элемент_песни, 'название')
title.text = song.title
artist = et. SubElement (song_element, 'исполнитель')
artist.text = song.artist
вернуть et.tostring (song_element, encoding = 'unicode')
Примечание: Метод .serialize ()
в SongSerializer
не использует параметр self
.
Приведенное выше правило говорит нам, что он не должен быть частью класса. Это правильно, но вы имеете дело с существующим кодом.
Если вы удалите SongSerializer
и измените метод .serialize ()
на функцию, вам придется изменить все места в приложении, которые используют SongSerializer
, и заменить вызовы новой функции.
Если у вас не очень высокий процент покрытия кода модульными тестами, это не то изменение, которое вам следует делать.
Механика Factory Method всегда одинакова. Клиент ( SongSerializer.serialize ()
) зависит от конкретной реализации интерфейса. Он запрашивает реализацию у компонента-создателя ( get_serializer ()
), используя какой-то идентификатор ( формат
).
Создатель возвращает конкретную реализацию в соответствии со значением параметра клиенту, а клиент использует предоставленный объект для выполнения своей задачи.
Вы можете выполнить тот же набор инструкций в интерактивном интерпретаторе Python, чтобы убедиться, что поведение приложения не изменилось:
>>> >>> импортировать serializer_demo как sd
>>> песня = сд.Песня ('1', 'Water of Love', 'Dire Straits')
>>> сериализатор = sd.SongSerializer ()
>>> serializer.serialize (песня, 'JSON')
'{"id": "1", "title": "Вода любви", "artist": "Dire Straits"}'
>>> serializer.serialize (песня, 'XML')
' Вода любви Dire Straits '
>>> serializer.serialize (песня, 'YAML')
Отслеживание (последний вызов последний):
Файл "", строка 1, в
Файл "./serializer_demo.py ", строка 13, в сериализации
сериализатор = get_serializer (формат)
Файл "./serializer_demo.py", строка 23, в get_serializer
поднять ValueError (формат)
ValueError: YAML
Вы создаете песню
и сериализатор
и используете сериализатор
для преобразования песни в ее строковое представление с указанием формата
. Поскольку YAML
не является поддерживаемым форматом, возникает ValueError
.
Признание возможностей использования заводского метода
Factory Method следует использовать в каждой ситуации, когда приложение (клиент) зависит от интерфейса (продукта) для выполнения задачи, и существует несколько конкретных реализаций этого интерфейса.Вам необходимо предоставить параметр, который может идентифицировать конкретную реализацию и использовать его в создателе для определения конкретной реализации.
Существует множество проблем, которые подходят под это описание, поэтому давайте рассмотрим несколько конкретных примеров.
Замена сложного логического кода: Сложные логические структуры в формате if / elif / else
трудно поддерживать, потому что по мере изменения требований требуются новые логические пути.
Factory Method - хорошая замена, потому что вы можете поместить тело каждого логического пути в отдельные функции или классы с общим интерфейсом, а создатель может предоставить конкретную реализацию.
Параметр, оцениваемый в условиях, становится параметром для идентификации конкретной реализации. Пример выше представляет эту ситуацию.
Создание связанных объектов из внешних данных: Представьте себе приложение, которому необходимо получить информацию о сотрудниках из базы данных или другого внешнего источника.
Записи представляют сотрудников с разными ролями или типами: менеджеры, офисные клерки, торговые партнеры и так далее. Приложение может хранить идентификатор, представляющий тип сотрудника в записи, а затем использовать фабричный метод для создания каждого конкретного объекта Employee
из остальной информации в записи.
Поддержка нескольких реализаций одной и той же функции: Приложению обработки изображений необходимо преобразовать спутниковое изображение из одной системы координат в другую, но существует несколько алгоритмов с разными уровнями точности для выполнения преобразования.
Приложение может позволить пользователю выбрать вариант, который идентифицирует конкретный алгоритм. Factory Method может предоставить конкретную реализацию алгоритма, основанного на этой опции.
Объединение аналогичных функций в общем интерфейсе: Следуя примеру обработки изображения, приложению необходимо применить фильтр к изображению.Конкретный используемый фильтр может быть определен пользователем, и Factory Method может предоставить конкретную реализацию фильтра.
Интеграция связанных внешних служб: Приложение музыкального проигрывателя хочет интегрироваться с несколькими внешними службами и позволить пользователям выбирать, откуда поступает их музыка. Приложение может определить общий интерфейс для музыкального сервиса и использовать заводской метод для создания правильной интеграции на основе предпочтений пользователя.
Все эти ситуации похожи.Все они определяют клиента, который зависит от общего интерфейса, известного как продукт. Все они предоставляют средства для определения конкретной реализации продукта, поэтому все они могут использовать фабричный метод в своем дизайне.
Теперь вы можете взглянуть на проблему сериализации из предыдущих примеров и предложить лучший дизайн, приняв во внимание шаблон проектирования Factory Method.
Пример сериализации объекта
Основные требования для приведенного выше примера заключаются в том, что вы хотите сериализовать объекты Song
в их представление string
.Кажется, что приложение предоставляет функции, связанные с музыкой, поэтому вполне вероятно, что приложению потребуется сериализовать другие типы объектов, такие как Playlist
или Album
.
В идеале проект должен поддерживать добавление сериализации для новых объектов путем реализации новых классов без необходимости внесения изменений в существующую реализацию. Приложение требует, чтобы объекты были сериализованы в несколько форматов, таких как JSON и XML, поэтому кажется естественным определить интерфейс Serializer
, который может иметь несколько реализаций, по одной для каждого формата.
Реализация интерфейса может выглядеть примерно так:
# в serializers.py
импортировать json
импортировать xml.etree.ElementTree как et
класс JsonSerializer:
def __init __ (сам):
self._current_object = Нет
def start_object (self, object_name, object_id):
self._current_object = {
'id': object_id
}
def add_property (self, name, value):
self._current_object [name] = значение
def to_str (сам):
вернуть json.dumps (self._current_object)
класс XmlSerializer:
def __init __ (сам):
self._element = Нет
def start_object (self, object_name, object_id):
self._element = et.Element (имя_объекта, attrib = {'id': object_id})
def add_property (self, name, value):
prop = et.SubElement (self._element, имя)
prop.text = значение
def to_str (сам):
вернуть et.tostring (self._element, encoding = 'unicode')
Примечание: В приведенном выше примере не реализован полный интерфейс Serializer
, но он должен быть достаточно хорош для наших целей и для демонстрации фабричного метода.
Интерфейс Serializer
- абстрактное понятие из-за динамической природы языка Python. Статические языки, такие как Java или C #, требуют явного определения интерфейсов. В Python говорят, что любой объект, который предоставляет желаемые методы или функции, реализует интерфейс. В примере интерфейс Serializer
определяется как объект, реализующий следующие методы или функции:
-
.start_object (имя_объекта, идентификатор_объекта)
-
.add_property (имя, значение)
-
.to_str ()
Этот интерфейс реализован конкретными классами JsonSerializer
и XmlSerializer
.
В исходном примере использовался класс SongSerializer
. Для нового приложения вы реализуете что-то более общее, например ObjectSerializer
:
# в serializers.py
класс ObjectSerializer:
def сериализовать (самостоятельно, сериализуемый, формат):
сериализатор = фабрика. get_serializer (формат)
serializable.serialize (сериализатор)
вернуть serializer.to_str ()
Реализация ObjectSerializer
является полностью универсальной и в качестве параметров упоминает только сериализуемый
и формат
.
Формат
используется для идентификации конкретной реализации сериализатора
и разрешается объектом factory
. Параметр serializable
относится к другому абстрактному интерфейсу, который должен быть реализован для любого типа объекта, который вы хотите сериализовать.
Давайте посмотрим на конкретную реализацию сериализуемого интерфейса в классе
Song
:
# В songs.py
класс Песня:
def __init __ (self, song_id, title, artist):
self.song_id = song_id
self.title = title
self.artist = художник
def serialize (self, сериализатор):
serializer.start_object ('песня', self. song_id)
serializer.add_property ('название', self.title)
serializer.add_property ('исполнитель', сам.художник)
Класс Song
реализует интерфейс Serializable
, предоставляя метод .serialize (сериализатор)
. В этом методе класс Song
использует объект сериализатора для записи собственной информации без какого-либо знания формата.
Фактически, класс Song
даже не знает, что цель состоит в том, чтобы преобразовать данные в строку. Это важно, потому что вы можете использовать этот интерфейс для предоставления другого типа сериализатора
, который при необходимости преобразует информацию Song
в совершенно другое представление.Например, вашему приложению в будущем может потребоваться преобразовать объект Song
в двоичный формат.
До сих пор мы видели реализацию клиента ( ObjectSerializer
) и продукта (сериализатор
). Пришло время завершить реализацию Factory Method и предоставить создателя. Создателем в примере является переменная factory
в ObjectSerializer.serialize ()
.
Фабричный метод как объектная фабрика
В исходном примере вы реализовали создателя как функцию.Функции подходят для очень простых примеров, но они не обеспечивают особой гибкости при изменении требований.
Классымогут предоставлять дополнительные интерфейсы для добавления функциональности, и они могут быть производными для настройки поведения. Если у вас нет очень простого создателя, который никогда не изменится в будущем, вы хотите реализовать его как класс, а не функцию. Эти классы называются фабриками объектов.
Вы можете увидеть базовый интерфейс SerializerFactory
в реализации ObjectSerializer.сериализовать ()
. Метод использует factory.get_serializer (format)
для извлечения сериализатора
из фабрики объектов.
Теперь вы реализуете SerializerFactory
для соответствия этому интерфейсу:
# в serializers.py
класс SerializerFactory:
def get_serializer (self, формат):
если format == 'JSON':
вернуть JsonSerializer ()
elif format == 'XML':
вернуть XmlSerializer ()
еще:
поднять ValueError (формат)
factory = SerializerFactory ()
Текущая реализация .get_serializer ()
— это то же самое, что вы использовали в исходном примере. Метод оценивает значение в формате
и решает, какую конкретную реализацию создать и вернуть. Это относительно простое решение, которое позволяет нам проверить функциональность всех компонентов фабричного метода.
Давайте перейдем к интерактивному интерпретатору Python и посмотрим, как он работает:
>>> >>> импорт песен
>>> импортировать сериализаторы
>>> песня = песни.Песня ('1', 'Water of Love', 'Dire Straits')
>>> сериализатор = сериализаторы. ObjectSerializer ()
>>> serializer.serialize (песня, 'JSON')
'{"id": "1", "title": "Вода любви", "artist": "Dire Straits"}'
>>> serializer.serialize (песня, 'XML')
' Вода любви Dire Straits '
>>> serializer.serialize (песня, 'YAML')
Отслеживание (последний вызов последний):
Файл "", строка 1, в
Файл "./serializers.py ", строка 39, в сериализации
сериализатор = factory.get_serializer (формат)
Файл "./serializers.py", строка 52, в get_serializer
поднять ValueError (формат)
ValueError: YAML
Новый дизайн Factory Method позволяет приложению вводить новые функции путем добавления новых классов вместо изменения существующих. Вы можете сериализовать другие объекты, реализовав на них интерфейс Serializable
. Вы можете поддерживать новые форматы, реализуя интерфейс Serializer
в другом классе.
Отсутствует то, что SerializerFactory
необходимо изменить, чтобы включить поддержку новых форматов. Эта проблема легко решается с новым дизайном, потому что SerializerFactory
— это класс.
Поддержка дополнительных форматов
Текущая реализация SerializerFactory
должна быть изменена при введении нового формата. Возможно, вашему приложению никогда не понадобится поддерживать какие-либо дополнительные форматы, но вы никогда не знаете.
Вы хотите, чтобы ваши проекты были гибкими, и, как вы увидите, поддержка дополнительных форматов без изменения SerializerFactory
относительно проста.
Идея состоит в том, чтобы предоставить метод в SerializerFactory
, который регистрирует новую реализацию Serializer
для формата, который мы хотим поддерживать:
# в serializers.py
класс SerializerFactory:
def __init __ (сам):
self._creators = {}
def register_format (сам, формат, создатель):
self._creators [format] = создатель
def get_serializer (self, формат):
creator = self. _creators.get (формат)
если не создатель:
поднять ValueError (формат)
вернуть создателя ()
factory = SerializerFactory ()
фабрика.register_format ('JSON', JsonSerializer)
factory.register_format ('XML', XmlSerializer)
Метод .register_format (формат, создатель)
позволяет регистрировать новые форматы путем указания значения формата , используемого для идентификации формата и объекта создателя
. Объект-создатель является именем класса конкретного сериализатора
. Это возможно, потому что все классы Serializer
предоставляют значение по умолчанию .__ init __ ()
для инициализации экземпляров.
Регистрационная информация хранится в словаре _creators
. Метод .get_serializer ()
получает зарегистрированного создателя и создает желаемый объект. Если запрошенный формат
не был зарегистрирован, возникает ValueError
.
Теперь вы можете проверить гибкость дизайна, реализовав YamlSerializer
и избавившись от надоедливой ValueError
, которую вы видели ранее:
# В yaml_serializer.ру
импортировать ямл
импортные сериализаторы
класс YamlSerializer (сериализаторы.JsonSerializer):
def to_str (сам):
вернуть yaml.dump (self._current_object)
serializers.factory.register_format ('YAML', YamlSerializer)
Примечание: Чтобы реализовать пример, вам необходимо установить PyYAML
в своей среде, используя pip install PyYAML
.
JSON и YAML — очень похожие форматы, поэтому вы можете повторно использовать большую часть реализации JsonSerializer
и перезаписать .to_str ()
для завершения реализации. Затем формат регистрируется в объекте factory
, чтобы сделать его доступным.
Давайте воспользуемся интерактивным интерпретатором Python, чтобы увидеть результаты:
>>> >>> импортные сериализаторы
>>> импорт песен
>>> импортировать yaml_serializer
>>> song = songs. Song ('1', 'Water of Love', 'Dire Straits').
>>> сериализатор = сериализаторы.ObjectSerializer ()
>>> print (сериализатор.сериализовать (песня, 'JSON'))
{"id": "1", "title": "Вода любви", "artist": "Dire Straits"}
>>> print (serializer.serialize (песня, 'XML'))
Вода любви Ужасные проливы
>>> print (serializer.serialize (песня, 'YAML'))
{художник: Dire Straits, id: '1', название: Water of Love}
Реализуя фабричный метод с использованием фабрики объектов и предоставляя интерфейс регистрации, вы можете поддерживать новые форматы без изменения какого-либо существующего кода приложения.Это сводит к минимуму риск нарушения существующих функций или внесения незначительных ошибок.
Фабрика объектов общего назначения
Реализация SerializerFactory
— огромное улучшение по сравнению с исходным примером. Он обеспечивает большую гибкость для поддержки новых форматов и позволяет избежать изменения существующего кода.
Тем не менее, текущая реализация специально нацелена на проблему сериализации, описанную выше, и не может повторно использоваться в других контекстах.
Заводской методможно использовать для решения широкого круга задач.Фабрика объектов дает дополнительную гибкость дизайну при изменении требований. В идеале вам понадобится реализация Object Factory, которую можно будет повторно использовать в любой ситуации без дублирования реализации.
Есть некоторые проблемы, связанные с предоставлением универсальной реализации Object Factory, и в следующих разделах вы рассмотрите эти проблемы и реализуете решение, которое можно повторно использовать в любой ситуации.
Не все объекты могут быть созданы равными
Самая большая проблема при реализации фабрики объектов общего назначения состоит в том, что не все объекты создаются одинаково.
Не все ситуации позволяют нам использовать значение по умолчанию .__ init __ ()
для создания и инициализации объектов. Важно, чтобы создатель, в данном случае Object Factory, возвращал полностью инициализированные объекты.
Это важно, потому что в противном случае клиенту придется завершить инициализацию и использовать сложный условный код для полной инициализации предоставленных объектов. Это противоречит цели шаблона проектирования Factory Method.
Чтобы понять сложность универсального решения, давайте взглянем на другую проблему.Допустим, приложение хочет интегрироваться с разными музыкальными сервисами. Эти службы могут быть внешними по отношению к приложению или внутренними для поддержки локальной музыкальной коллекции. У каждой службы свой набор требований.
Примечание: Требования, которые я определяю для примера, приведены в целях иллюстрации и не отражают реальных требований, которые вам придется реализовать для интеграции с такими службами, как Pandora или Spotify.
Цель состоит в том, чтобы предоставить другой набор требований, который показывает проблемы реализации фабрики объектов общего назначения.
Представьте, что приложение хочет интегрироваться со службой Spotify. Эта услуга требует процесса авторизации, в котором для авторизации предоставляются ключ клиента и секрет.
Служба возвращает код доступа, который следует использовать при дальнейшем общении. Этот процесс авторизации очень медленный, и его следует выполнять только один раз, поэтому приложение хочет сохранить инициализированный объект службы и использовать его каждый раз, когда ему нужно общаться со Spotify.
В то же время другие пользователи хотят интегрироваться с Pandora. Pandora может использовать совершенно другой процесс авторизации. Он также требует клиентского ключа и секрета, но возвращает потребительский ключ и секрет, которые следует использовать для других коммуникаций. Как и в случае со Spotify, процесс авторизации медленный, и его следует выполнять только один раз.
Наконец, приложение реализует концепцию локальной музыкальной службы, в которой музыкальная коллекция хранится локально. Служба требует, чтобы было указано местоположение музыкальной коллекции в локальной системе.Создание нового экземпляра службы выполняется очень быстро, поэтому новый экземпляр можно создавать каждый раз, когда пользователь хочет получить доступ к музыкальной коллекции.
Этот пример представляет несколько проблем. Каждая служба инициализируется с различным набором параметров. Кроме того, Spotify и Pandora требуют процесса авторизации перед созданием экземпляра службы.
Они также хотят повторно использовать этот экземпляр, чтобы избежать многократной авторизации приложения. Локальный сервис проще, но он не соответствует интерфейсу инициализации других.
В следующих разделах вы решите эти проблемы, обобщив интерфейс создания и реализуя фабрику объектов общего назначения.
Создание отдельного объекта для обеспечения общего интерфейса
Создание каждого конкретного музыкального сервиса имеет свой набор требований. Это означает, что общий интерфейс инициализации для каждой реализации службы невозможен или не рекомендуется.
Лучший подход — определить новый тип объекта, который предоставляет общий интерфейс и отвечает за создание конкретной службы.Этот новый тип объекта будет называться Builder
. Объект Builder
имеет всю логику для создания и инициализации экземпляра службы. Вы реализуете объект Builder
для каждой из поддерживаемых служб.
Начнем с конфигурации приложения:
# В program.py
config = {
'spotify_client_key': 'THE_SPOTIFY_CLIENT_KEY',
'spotify_client_secret': 'THE_SPOTIFY_CLIENT_SECRET',
'pandora_client_key': 'THE_PANDORA_CLIENT_KEY',
'pandora_client_secret': 'THE_PANDORA_CLIENT_SECRET',
'local_music_location': '/ usr / data / music'
}
Словарь config
содержит все значения, необходимые для инициализации каждой из служб.Следующим шагом является определение интерфейса, который
будет использовать эти значения для создания конкретной реализации музыкального сервиса. Этот интерфейс будет реализован в Builder
.
Давайте посмотрим на реализацию SpotifyService
и SpotifyServiceBuilder
:
# В music.py
класс SpotifyService:
def __init __ (self, access_code):
self._access_code = код_доступа
def test_connection (сам):
print (f'Доступ к Spotify с помощью {self._код доступа}')
класс SpotifyServiceBuilder:
def __init __ (сам):
self._instance = Нет
def __call __ (self, spotify_client_key, spotify_client_secret, ** _ игнорируется):
если не self._instance:
access_code = self.authorize (
spotify_client_key, spotify_client_secret)
self._instance = SpotifyService (код_доступа)
вернуть self._instance
def authorize (self, key, secret):
вернуть 'SPOTIFY_ACCESS_CODE'
Примечание: Интерфейс музыкальной службы определяет .test_connection ()
, которого должно хватить для демонстрационных целей.
Пример показывает SpotifyServiceBuilder
, который реализует .__ call __ (spotify_client_key, spotify_client_secret, ** _ игнорируется)
.
Этот метод используется для создания и инициализации конкретного SpotifyService
. Он указывает обязательные параметры и игнорирует любые дополнительные параметры, предоставленные через ** _ игнорируется
. После получения access_code
он создает и возвращает экземпляр SpotifyService
.
Обратите внимание, что SpotifyServiceBuilder
хранит экземпляр службы и создает новый только при первом запросе службы. Это позволяет избежать многократного прохождения процесса авторизации, как указано в требованиях.
Давайте сделаем то же самое для Pandora:
# В music.py
класс PandoraService:
def __init __ (self, consumer_key, consumer_secret):
self._key = consumer_key
self._secret = consumer_secret
def test_connection (сам):
print (f'Доступ к Pandora с помощью {self. _key} и {self._secret} ')
класс PandoraServiceBuilder:
def __init __ (сам):
self._instance = Нет
def __call __ (self, pandora_client_key, pandora_client_secret, ** _ игнорируется):
если не self._instance:
consumer_key, consumer_secret = self.authorize (
pandora_client_key, pandora_client_secret)
self._instance = Служба PandoraService (ключ_потребителя, секрет_потребителя)
вернуть self._instance
def authorize (self, key, secret):
вернуть 'PANDORA_CONSUMER_KEY', 'PANDORA_CONSUMER_SECRET'
PandoraServiceBuilder
реализует тот же интерфейс, но использует другие параметры и процессы для создания и инициализации PandoraService
.Он также поддерживает экземпляр службы, поэтому авторизация происходит только один раз.
Наконец, давайте посмотрим на реализацию местной службы:
# В music.py
класс LocalService:
def __init __ (я, местоположение):
self. _location = место
def test_connection (сам):
print (f'Доступ к местной музыке в {self._location} ')
def create_local_music_service (local_music_location, ** _ игнорируется):
вернуть LocalService (local_music_location)
LocalService
просто требует места, где хранится коллекция, для инициализации LocalService
.
Новый экземпляр создается каждый раз, когда запрашивается услуга, поскольку нет медленного процесса авторизации. Требования проще, поэтому вам не нужен класс Builder
. Вместо этого используется функция, возвращающая инициализированный LocalService
. Эта функция соответствует интерфейсу методов .__ call __ ()
, реализованных в классах построителя.
Общий интерфейс для фабрики объектов
Фабрика объектов общего назначения ( ObjectFactory
) может использовать общий интерфейс Builder
для создания всех видов объектов.Он предоставляет метод регистрации Builder
на основе значения ключа
и метод создания экземпляров конкретных объектов на основе ключа
.
Давайте посмотрим на реализацию нашего общего ObjectFactory
:
# в object_factory.py
класс ObjectFactory:
def __init __ (сам):
self._builders = {}
def register_builder (сам, ключ, строитель):
self._builders [key] = строитель
def create (self, key, ** kwargs):
строитель = сам._builders.get (ключ)
если не строитель:
поднять ValueError (ключ)
возвратный строитель (** kwargs)
Структура реализации ObjectFactory
такая же, как и в SerializerFactory
.
Разница в интерфейсе, который поддерживает создание любого типа объекта. Параметром построителя может быть любой объект, реализующий вызываемый интерфейс. Это означает, что Builder
может быть функцией, классом или объектом, реализующим .__call __ ()
.
Метод .create ()
требует, чтобы дополнительные аргументы были указаны как аргументы ключевого слова. Это позволяет объектам Builder
указывать необходимые им параметры и игнорировать остальные в произвольном порядке. Например, вы можете видеть, что create_local_music_service ()
указывает параметр local_music_location
и игнорирует остальные.
Давайте создадим экземпляр фабрики и зарегистрируем конструкторы для сервисов, которые вы хотите поддерживать:
# В музыке.ру
import object_factory
# Пропуск других классов реализации, показанных выше
factory = object_factory.ObjectFactory ()
factory.register_builder ('SPOTIFY', SpotifyServiceBuilder ())
factory.register_builder ('PANDORA', PandoraServiceBuilder ())
factory.register_builder ('МЕСТНЫЙ', create_local_music_service)
Модуль music
предоставляет экземпляр ObjectFactory
через атрибут factory
. Затем строители регистрируются в экземпляре.Для Spotify и Pandora вы регистрируете экземпляр соответствующего конструктора, но для локальной службы вы просто передаете функцию.
Напишем небольшую программу, демонстрирующую функциональность:
# В program.py
импортировать музыку
config = {
'spotify_client_key': 'THE_SPOTIFY_CLIENT_KEY',
'spotify_client_secret': 'THE_SPOTIFY_CLIENT_SECRET',
'pandora_client_key': 'THE_PANDORA_CLIENT_KEY',
'pandora_client_secret': 'THE_PANDORA_CLIENT_SECRET',
'local_music_location': '/ usr / data / music'
}
пандора = музыка.factory.create ('PANDORA', ** конфигурация)
pandora.test_connection ()
spotify = music.factory.create ('SPOTIFY', ** конфигурация)
spotify.test_connection ()
local = music.factory.create ('МЕСТНОЕ', ** конфигурация)
local.test_connection ()
pandora2 = music.services.get ('PANDORA', ** конфигурация)
print (f'id (pandora) == id (pandora2): {id (pandora) == id (pandora2)} ')
spotify2 = music.services.get ('SPOTIFY', ** конфигурация)
print (f'id (spotify) == id (spotify2): {id (spotify) == id (spotify2)} ')
Приложение определяет config словарь
, представляющий конфигурацию приложения. Конфигурация используется в качестве аргументов ключевого слова для фабрики независимо от службы, к которой вы хотите получить доступ. Фабрика создает конкретную реализацию музыкального сервиса на основе указанного параметра key
.
Теперь вы можете запустить нашу программу и посмотреть, как она работает:
$ python program.py
Доступ к Pandora с помощью PANDORA_CONSUMER_KEY и PANDORA_CONSUMER_SECRET
Доступ к Spotify с помощью SPOTIFY_ACCESS_CODE
Доступ к локальной музыке в / usr / data / music
id (pandora) == id (pandora2): Верно
id (spotify) == id (spotify2): Верно
Вы можете видеть, что правильный экземпляр создается в зависимости от указанного типа службы.Вы также можете видеть, что запрос службы Pandora или Spotify всегда возвращает один и тот же экземпляр.
Специализированная фабрика объектов для улучшения читаемости кода
Общие решения допускают многократное использование и исключают дублирование кода. К сожалению, они также могут скрыть код и сделать его менее читабельным.
В приведенном выше примере показано, что для доступа к музыкальной службе вызывается music.factory.create ()
. Это может привести к путанице. Другие разработчики могут подумать, что каждый раз создается новый экземпляр, и решить, что им следует оставить экземпляр службы, чтобы избежать медленного процесса инициализации.
Вы знаете, что это не то, что происходит, потому что класс Builder
сохраняет инициализированный экземпляр и возвращает его для последующих вызовов, но это не ясно из простого чтения кода.
Хорошее решение — это специализированная реализация общего назначения для предоставления интерфейса, конкретного для контекста приложения. В этом разделе вы специализируете ObjectFactory
в контексте наших музыкальных сервисов, чтобы код приложения лучше передавал намерение и становился более читабельным.
В следующем примере показано, как специализировать ObjectFactory
, предоставляя явный интерфейс для контекста приложения:
# В music. py
класс MusicServiceProvider (object_factory.ObjectFactory):
def get (self, service_id, ** kwargs):
вернуть self.create (service_id, ** kwargs)
services = MusicServiceProvider ()
services.register_builder ('SPOTIFY', SpotifyServiceBuilder ())
services.register_builder ('PANDORA', PandoraServiceBuilder ())
Сервисы.register_builder ('МЕСТНЫЙ', create_local_music_service)
Вы получаете MusicServiceProvider
из ObjectFactory
и предоставляете новый метод .get (service_id, ** kwargs)
.
Этот метод вызывает общий .create (key, ** kwargs)
, поэтому поведение остается прежним, но код лучше читается в контексте нашего приложения. Вы также переименовали предыдущую переменную factory
в services
и инициализировали ее как MusicServiceProvider
.
Как видите, обновленный код приложения теперь читается намного лучше:
импорт музыки
config = {
'spotify_client_key': 'THE_SPOTIFY_CLIENT_KEY',
'spotify_client_secret': 'THE_SPOTIFY_CLIENT_SECRET',
'pandora_client_key': 'THE_PANDORA_CLIENT_KEY',
'pandora_client_secret': 'THE_PANDORA_CLIENT_SECRET',
'local_music_location': '/ usr / data / music'
}
pandora = music. services.get ('ПАНДОРА', ** конфигурация)
pandora.test_connection ()
spotify = music.services.get ('SPOTIFY', ** конфигурация)
спотифицируйте.test_connection ()
local = music.services.get ('МЕСТНОЕ', ** конфигурация)
local.test_connection ()
pandora2 = music.services.get ('PANDORA', ** конфигурация)
print (f'id (pandora) == id (pandora2): {id (pandora) == id (pandora2)} ')
spotify2 = music.services.get ('SPOTIFY', ** конфигурация)
print (f'id (spotify) == id (spotify2): {id (spotify) == id (spotify2)} ')
Запуск программы показывает, что поведение не изменилось:
$ python program.py
Доступ к Pandora с помощью PANDORA_CONSUMER_KEY и PANDORA_CONSUMER_SECRET
Доступ к Spotify с помощью SPOTIFY_ACCESS_CODE
Доступ к локальной музыке в / usr / data / music
id (pandora) == id (pandora2): Верно
id (spotify) == id (spotify2): Верно
Заключение
Factory Method — это широко используемый шаблон проектирования, который можно использовать во многих ситуациях, когда существует несколько конкретных реализаций интерфейса.
Шаблон удаляет сложный логический код, который трудно поддерживать, и заменяет его дизайном, который можно многократно использовать и расширять. Шаблон позволяет избежать изменения существующего кода для поддержки новых требований.
Это важно, потому что изменение существующего кода может привести к изменениям в поведении или незначительным ошибкам.
Из этой статьи вы узнали:
- Что такое шаблон проектирования Factory Method и каковы его компоненты
- Как провести рефакторинг существующего кода для использования заводского метода
- Ситуации, в которых следует использовать заводской метод
- Как фабрики объектов обеспечивают большую гибкость для реализации фабричного метода
- Как реализовать объектную фабрику общего назначения и ее проблемы
- Как специализировать общее решение, чтобы обеспечить лучший контекст
Дополнительная литература
Если вы хотите узнать больше о фабричном методе и других шаблонах проектирования, я рекомендую Design Patterns: Elements of Reusable Object-Oriented Software от GoF, который является отличным справочником по широко распространенным шаблонам проектирования.