Патерни (потрібна безкоштовна консультація)

Є в мене декілька тасок, і щоб краще їх вписати, та й покращити наявний код що я писав під конкретні АПІ і формати, вирішив я нарешті повчити патерни і підшукати якесь готове рішення. Я опишу коротко в загальному вигляді що я хочу зробити, і патерни які я як розумію підходять під це, а ви поправите, окей?

1. Є сервіс, який звертаєтсья до АПІ GMAIL, і бере звідти повідомлення витягаючи з них інформацію для подальшої обробки. Починалося все з того, що звідти треба було тягти PDF файли, і парсити їх регулярками. Цей процесинг я відокремив в два сервіси: перший основний робить запит по GMAIL API зі специфічним фільтруванням(повідомлення з прикріпленими атачмент, які є формату PDF), і другий сервіс являє собою парсер PDF файлів(цей сервіс я теж хочу переробити під патерн, опишу в другому пункті). Перший сервіс використовує другий, після того як витяг пдф файл з повідомлення, і використовує 3 методи для зчитування вже конкретної інфи, яку потім сам і повертає зформовану у вигляді об’єкту.

Давайте я інтерфейс кину, щоб зрозуміліше:

public interface IGMAILAPI
    {
        List<PDFDataObject> PullNewEmailsAndParsePDFData(string dateAfter = null, IEnumerable<string> emailIdsToSkip = null);

    }
public interface IPDFParser
    {
        string ReadTextFromBase64(string data);
        string FindTrackingNumberOrNull(string text);
        string FindDeliveryAddressOrNull(string text);
        string FindPostalCodeOrNull(string text);
    }

Все працює, все добре. Але тут є 2 проблеми:

Проблема 1: тепер потрібно, щоб IGMAILAPI працював як з повідомленнями, що мають атачмент PDF, так і з HTML шаблонами(вкладеними прямо в боді меседжа), які по суті як і пдф холдять необхідну інформацію.
Проблема 2: PDF файли є різних шаблонів, від поставника 1, і 2. Поки я просто прописав в PDFParser приватні методи, і вони викликаються по порядку в інтерфейсних допоки значення не буде успішно зпарсене відповідною регуляркою. До речі всі ці публічні методи викликаються в IGMAILAPI методі.

Я одразу зрозумів, що треба пошукати готові патерни, замість того щоб фантазії натягувати. І ось до чого прийшов:

Проблему 1 вирішую, використовуючи "Template Method"(refactoring.guru/...​-patterns/template-method), тобто розбиваю цей один метод на групу steps, і наслідую в 2 сабкласи: PDFGMAILAPI, HTMLGMAILAPI. Хах, тільки но дійшло, що така ж проблема 1 в 1 показана в цьому UML: refactoring.guru/...​te-method/solution-en.png

Наче все сходиться?

Проблему 2 вирішу. використовуючи "Strategy"(refactoring.guru/design-patterns/strategy) в результаті імплементація інтерфейсу IPDFParser буде грати роль Context, як показано на цьому скріні: refactoring.guru/...​ms/strategy/structure.png а PDF формати, тобто приватні методи які тепер стоять там, підуть в окремі класи Strategies.

Там у мене ще є проблем повирішувати, але хочеться дізнатися чи я в правильну сторону дивлю. Дякую.

LinkedIn
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Допустимые теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter

I’ve seen many over-engineered software, but haven’t seen under-engineered.

Воно вмирає ще до реліза

Коротше, думаю робити парсер он як:

1. Сервіс, який приймає інпут дані у вигляді байтів. Є декілька імплементацій: PdfService, HtmlService
2. Кожна з імплементацій приймає стратегію, тобто тип парсингу:

PdfService — Pdf!Strategy, Pdf2Strategy, so on
HtmlService — Html1Strategy, Html2Strategy, so on

Як вам? Тип парсингу то тип(темплейт) документу. Документи бувають від різних компаній. Але може бути що одна компанія має декілька різних. В такому разі буде декілька стратегій для однієї компанії з назвою: КомпаніянеймТемплейт.

Та норм. Головне, щоб тобі цікаво було)

Цікаво! Аби ще міг по 10 годин в день працювати, а не по 3-4)) Ще перші дні тижня з застудою якоюсь був, і через силу йшло. Курва коли вже я буду нормальним.

Я, щойно карантин зняли, теж щось підчепив. Може — те саме. Більше тижня невелика температура, морозило — спав в халаті під ковдрою. Горло не боліло, кашлю не було. Саме прийшло, саме пройшло.

Я часто якісь застуди ловлю. дрібні вірусняки. Але мене так плющить що від стресу що від вірусів, що я не одразу розумію. Температуру не міряю, сенс якщо не відчуваєш велику. Це ж не тиск.

Я теж не міряв, але за кілька днів мама так дістала дзвінками, що для неї поміряв, щоб заспокоїлась.

Краще мамам не казати про те що хворієш. У них уява дуже розвинута)

может лучше начать с solid, так будет просто понятнее мотивация отдельных паттернов и их компонентов ?

не знаю. какие-то эти SOLID, DI и прочие модности непонятные, мне по крайней мере. Зачем оно, и вообще обозначает ли оно что-то, или просто о поговорить. GoF, напротив, написано для тупых с картинками и примерами.

Наверное это индивидуально и сильно зависит от потребности. Мне напротив солид, инверсия, гексагон и с4 (если последнее вообще востребовано) дал очень много

какие-то эти SOLID, DI и прочие модности непонятные, мне по крайней мере.

Я думаю ці штуки більше для формошльопства де код бейз великий. В ембедед програмуванні воно зайве напевно. У вас там все по лібам розкидується ж? І вже ліби один одну юзають.

І як виживаєте? Знайшли власні підходи?

Я думаю ці штуки більше для формошльопства

Очень неправильно так думать. Одно дело, что оно в камнях кому-то не нужно. Другое дело верить в то, что на арх. принципах живут только ынтерпрайз и формошлёпы. Хочешь, на называй их своими именами, и даже не читай о них. Назови это «здравым смыслом», придумай свой подход. Вопрос только в том, будет ли это лучше, чем просто освоить онные?

если проект это формошлепство то откуда у него большой код бейз?

Так для формочек не надо код бейз большой, это 2 противоречивые вещи.

Скажіть це Ангуляру з архітектурою цілою.

у приложений на ангуляре большой код бейз?

у меня было похожее чтото, первый сервис тащил с гогла имейлы и складывал в блобы, второй сервис сторожил сохраненные блобы и если у них был тип емейл то он запускал парсер емейла. Парсер парсил весь емейл и сохранял в блобы атачменты и бади. Опятьже сервис который сторожил блобы подключал парсер для пдф или для боди если оно соответсвовало критериям.

Я трохи заплутався:

1. Пєніє виступає за те, щоб забити на формалізовані патерни, і писати свої.
2. Денис Полторак все таки розписав мені про один корисний патерн(Strategy)
3. Maxim Deyneka додав ще один корисний патерн для мого випадку(Builder)
4. Vadim Kopanev виступив здається за те, щоб патерни використовувати, лише після детального їх вивчення, і, як я зрозумів, опробовування на пет проектах?
6. Viktor Chyzhdzenka написав, що забив на патерни, і пише дизайнячи класами і їх зв’язками.

Тобто патерни потрібні? Як в C# так і в Asp.net Core імплементовані патерни. І я бачу як прикольно виходить їх використовувати. То чому б і в свожму коді не додати, там де воно корисне? Не хочу починати заново великі дискусії, просто підвести висновки.

Тут ще казали за Chain of Responsibility для фільтрів мейлів та для парсерів pdf.
І якщо можливі різні типи пейлоаду, то воно усе сильно сповзає в бік Pipes and Filters.

Я вже сильно втомлений був, щоб прочитати про цей патерн. Завтра все ще раз перечитаю і почну писати код надіюся. Тема дуже допомогла, брейншторм серйозний.

Але от:

«IoC(DI) не потрібен». У мене підгоріло. Ото сидиш читаєш великі доки по фреймворку, вчиш всі його нові фічі круті, юзаєш там Options pattern цього фреймворку(Бо реально крута штука яка уберігає від magic strings!), читаєш про те, що депенденсі мають бути явними і без new, а потім тобі в коментах пишуть що DI непотрібен. Піду нап’юсь.

Ось навіть в Laravel вже ввели DI!

laravel.com/docs/7.x/container

А мені задвигають що це непотрібно. Гофери кляті. Ваш Го непотрібен!

Я работал на проектах и с IOC и без. Сейчас у меня огромнейший проект без IOC, написанный в процедурном стиле. Если мне в методе сервиса нужен другой сервис, я просто беру и инстанцирую этот сервис. Отлично себя чувствую, за IOC не скучаю совершенно.

Писати можна без жодних новомодних фіч. Але стартувати новий проект без них — злочин(для себе в першу чергу, бо випадаєш з тренду).

Якщо тягнути фічі заради модності — то втрачаєш розуміння взагалі нащо вони існують. В результаті проект буде великий, складний, тормозний, заплутаний, ще й з новомодними багами.

Не всі ж фічі тягнути бо тягнути. У мене наприклад нема докеру в апі. Нема CI/CD. Бо мені за налаштування їх не заплатять, та й сенс це інтергровувати коли там одна невелика апа. Я так, залию зкомпільований проект на лінукс сервер, перезапущу кестрел(сервер) і готово.

sudo systemctl restart kestrel-appname.service

Кто бы что не писал, но di обязательно нужен, бенефиты от его использования в 90% перекрывают все недостатки, под остальное попадают всякие хеллоуворлды

Ще не так давно були прихильники невикористання фреймворків, а писати все з нуля. З DI напевно подібна ситуація. Людям важко змінювати звички.

так DI можно без фрейворка как бы — Guice

“Google Guice is an open-source software framework for the Java platform released by Google under the Apache License. ”

))

Допустим на проекте нет мок-тестов, что почти всегда.
Какие бенефиты от di, кроме как свернуть _foo = new Foo(new Bar(new Boo()), new Goo());
?
Зачем все это конструировать, если эти объекты даже не понадобяться внутри?

упростить инициализацию, аоп, тесты, слабая связность и уменьшение кодовой базы

слабая связность

95% проектов имеют что-то вроде ClientService, ProductService, MailService. И в 95% случаев все эти классы будут иметь одну единственную реализацию. И в качестве рудимента будет валяться папка Interfaces с горой бесполезного хлама вроде IClientService, IProductService, IMailService в которые придется постоянно лезть и поправлять то названия, то сигнатуры. Добавим сюда постоянный головняк вроде «Go to implementation ...». Но зато мы можем с религиозным придыханием воскликнуть «О чудо! Все loosely coupled! Все по интерфейсу заинжекшено в конструктор».

две реализации*
обычная + мок для тестов

так это же автоматизируется прям с коробки, ничего особо не надо делать чтобы оно само, даже комменты подправляются автоматически, если в них название....

если ты пишешь очередную формочку с рестом, то можешь забить, там это и вправду не нужно.

Не обязательно классами. Сущностями — иногда классы, иногда либы. Выбираешь уровень детализации в зависимости от уровня глубины представляение.
И я выше посоветовал почитать и понять классиков с паттернами, чтобы знать, какие решения и паттерны люди уже использовали для разынх задач.
Например паттерн «визитор» прекрасен но только тогда, когда применен в правильном для него месте. Если его присобачили не подумав — выходит безумное уродство.

Но любая твоя задачка редко идеально ляжет на разработанный классиками паттерн. Правильнее сделать свои под задачу, основываясь на паттернах классиков, их идеях.

1. Пєніє виступає за те, щоб забити на формалізовані патерни, і писати свої.

В принципі, все вірно. Дуже рідко патерни лягають на задачу в такому вигляді як вони описані в книжці.

6. Viktor Chyzhdzenka написав, що забив на патерни, і пише дизайнячи класами і їх зв’язками.

... І в кінці-кінців, таким чином може виявитися що патерн виходить сам собою (пам’ятаємо що патерн не обовєязково має збігатся тютілька в тютільку як в кнжці).

Додам лише що набагато швидше переписати 2 рази (другий може настане колись), ніж морочити собі яйця і з першого разу писати так ніби Всесвіт створюєш.

Часть, которую затонуло )
Что-то типа:
Первый раз делаешь что бы работало
Второй раз переделываешь, выносишь общие моменты
Третий раз и далее — наследуешься, имплементишь необходимые интерфейсы, остальное было сделано раньше..

В чому перевага над робити одразу більш менш пристойне? Враховуючи що в процесі це додасть знань, як потім краще переписати.

Сложнее выделить общее. Уйдет больше времени, а потом, на втором этапе окажется, что нужных обобщений нету, а ненужные есть.
Это мое мнение, мне так проще. Возможно кто-то может сразу предусмотреть, более опытный или более умный. А вообще, так меня учили. Не программируй «вдруг потом понадобится», обычно не надобится )

Тем ни менее возможности для небольших расширений и добавлений нужно закладывать еще на этапе проектирования.

Закон Мэрфи утверждает, что именно туда оно расширяться и не будет. Зато добавлят хотелок, начисто рвущих абстрактную модель.

Ты не понял меня. Я о том, что на этапе построения архитектуры ее желательно делать так, чтобы достаточно легко было ее расширять в любую достаточно разумную сторону. А дальше как повезет. Иногда везет, и необходимо расширить в ту сторону, в которую позволяет. Иногда не везет и мы вспоминаем Мерфи.
Один из путей достичь этого делать архитерктуру максимально модульной и так, чтобы модули слабо зависели друг от друга.

Опыт доказывает, что эти решения нужно применять после 2-го изменения за пределами текущей серии. То есть пока первичные хотелки идут — делаем х*як-х*як, когда через полгодика захотелось чего-т ещё, уже можем оценивать стоимость изменений и соответственно принимать решение о её снижении.

Есть разница, пишешь ты основную логику, или паразитную разовую задачу или кучку разовых. Не веришь — посмотри что у тебя в кронтабе лежит, и насколько оно «модульно» и «архитектурно».

С первого раза пишется «правильно» если ты видишь что проект большой или сложный.

Даже на первой стадии можно подумать, а не сразу

х*як-х*як

Но да, на старте проекта архитектуру категорически нельзя делать сложной. Она должна быть тупая и простая, как валенок.
Лучше пусть при реализации допишут отсебеятины, лучше потом через какое-то время отрефакторить или вообще переписать (выдирая клоки из написанного).

Нельзя на старте делать слишком простой. Иначе потом ниасилишь изменение модели многопоточности.

Есть архитектурные паттерны (layers, pipes and filters, microkernel, blackboard, mvc, actors). Они отвечают на вопрос «что делать». И вот оно нужно с самого начала. Потом фиг всю систему перепишешь.

Если дизайн паттерны. Отвечают на вопрос «как делать». Вот их часто можно добавлять и убирать в процессе.

Нельзя на старте делать слишком простой. Иначе потом ниасилишь изменение модели многопоточности.

Наоборот. На простое всегда можно навернуть сложность, а вот на сложное уже не получится часто.

Есть архитектурные паттерны (layers, pipes and filters, microkernel, blackboard, mvc, actors)

Видал я их в белых тапочках.
Но для молодого их знать и понимать нужно.

Наоборот. На простое всегда можно навернуть сложность, а вот на сложное уже не получится часто.

Из процедурной консольной нормально не сделать оконную программу, если на старте не заложить полиморфизм между блокирующим вызовом чтения из консоли и обработкой событий из окошек.
Моноблок будет очень тяжело распилить на фильтры, когда нужно будет дать пользователю комбинировать этапы и алгоритмы обработки потока данных.
Сделать из однопоточного блокирующего кода реал-тайм обработку событий — вообще не получится.
Вспомни, как легко досовские программы переходили под Виндовс. И сколько из них не пережили этот этап.

Из процедурной консольной нормально не сделать оконную программу

Запросто, а вот наоборот часто сложно.
Написал код для отображения и дернул в нужных местах функции, что были в консольной.

Моноблок будет очень тяжело распилить на фильтры

Из простыни на 100500 строк в main вообще что-то сложно сделать.

Вспомни, как легко досовские программы переходили под Виндовс.

Помню. Без проблем. Этим и занимался много времени. А еще 16 бит в 32 — это сложнее было.

Помню чисто С-шный код мат библиотеки Рейтера в С++ вообще генератором переносили. Да, написали генератор и парсер за пару месяцев, для парсинга специфический вывод gcc юзали — были там такие возможности уже давно.

Написал код для отображения и дернул в нужных местах функции, что были в консольной.

Фиг. В консольной у тебя дерево выполнения функций, а в оконной надо все время слушать события, иначе прога зависнет. Ты не можешь что-либо считать больше сотни миллисекунд.

Ты не можешь что-либо считать больше сотни миллисекунд.

Да ладно. Не позорься.
И во всех либах для гуев давно отдельный поток гуями занимается. И да в него долгие вычисления совать не нужно.

О! И теперь вместо чтения команды из консоли появляется очередь событий. Или ты через файловый дескриптор будешь между потоками данные передавать в виде консольных команд?

Зачем так извращаться?
Из консоли тоже может читать отдельный поток, если тебе так нужно. А все остальное делаться в большом стаде других.

Вся сложность бывает только в одном — не всякий последовательный алгоритм можно сделать параллельным. У меня один сотрудник на эту тему целый диссер защищал и защитил.

Из консоли тоже может читать отдельный поток, если тебе так нужно. А все остальное делаться в большом стаде других.

О! появляется структура! Резать на несколько потоков готовый код просто?

Вся сложность бывает только в одном — не всякий последовательный алгоритм можно сделать параллельным. У меня один сотрудник на эту тему целый диссер защищал и защитил.

Скайп или Телеграм — это алгоритм?
Представляешь себе однопоточный Скайп или Телеграм?

В контексте задачи, да ) UI который натягивается делается в отдельном потоке.. все.

Про голос и видео забыл?

Из процедурной консольной нормально не сделать оконную программу

При чем тут голос и видео? Или у нас изначано консольный однопоточный скайп? Зачем сову натягивать на глобус?

Сорри, не посмотрел дерево ответов.
У нас тут вопрос, нужна ли вообще архитектура изначально, или можно писать как подряд. То есть, да, изначально однопоточный скайп писать, а затем — доводить до ума.

Это уже споры ради споров. Уже через пол года работы все понимают, что архитектура нужна в рамках задачи + предполагаемое расширение. Не более, но может быть менее, если скажут.

Ніхто не каже одноразову таску робити правильно по всім канонам. Чи щось мале. Але от подиви: мені щоб навчитися робити правильно — потрібно старатися робити правильно. Воно вийде все ще не зовсім правильно, але з часом набиваю руку і вже можу обирати як писати.

Той мій метод «PullNewEmailsAndParsePDFData» явно не правильний, і я це знав, але не мав часу подумати як зробити правильно, а з ходу не йшло в голову.

Іншими словами — сіньйор має права обирати писати куяк і куяк(бо він розуміє всі наслідки). А от джун а то і мідл — ні!

«PullNewEmailsAndParsePDFData»

Ты засунул кучу разных задач сразу в одну.
Но и такое годиться для разовой работы, но не для последующего расширения. Но в таком случае вот так по дурацки и разумно называть функцию. Сразу понятно, что в нее засунуто все и при необходимости расширения она удаляется и пишется вся эта объединенная функциональность с 0. Более того эта функция не должна влиять больше ни на что в системе — должна сделать свою работу и сообщить об успехе или не успехе и отдать конкретную ошибку в случае неуспеха.

Ты засунул кучу разных задач сразу в одну.

Лише дві. При тому парсер йде окремим сервісом.

var text = _pdfParser.ReadTextFromBase64(attachment.data);
                    var number = _pdfParser.FindTrackingNumberOrNull(text);
                    var address = _pdfParser.FindDeliveryAddressOrNull(text);
                    var postalCode = _pdfParser.FindPostalCodeOrNull(text);

                    if (number != null && address != null && postalCode != null)
                    {
                        resultData.Add(new PDFDataObject
                        {
                            TrackingNumber = number,
                            DeliveryAddress = address,
                            PostalCode = postalCode,
                            EmailId = messageId,
                            EmailInternalDate = messageDetail.internalDate / 1000
                        });
                    }

Ось — денкілька рядків коду це вся друга задача що тут робиться.

А так ти абсолютно правий.

Я бы только назвал ReadPdfFromBase64. Так сразу понятно, что эта функция делает только это и ничего другого. Никаких разных text, а именно pdf.
А вот если понадобятся другие форматы, то там уже рефакторить. Если один, то добавить еще функцию, а если много, то уже интерфейсы с фабриками добавить, а функцию удалить нафиг. Да чуть заденет зависимости от этой функции, но это не так страшно, когда наворочена гигантская непонятная архитектура.
Но можно использовать ReadTextFromBase64, но тогда в код добавить проверку, а pdf-ли там и кидать исключение подобное «NOT IMPLEMENTED YET!!!!!!!».

Я бы только назвал ReadPdfFromBase64. Так сразу понятно, что эта функция делает только это и ничего другого.

Ну я думав що так як клас зветься PDFParser, то і так зрозуміло буде, що base64 родом із PDF файлу має бути. І до речі та функція текст повертає. Треба б тоді зазначати в назві що текст, а не пдф обєкт.

Да откуда я знаю, как у тебя класс зовется. Дублировать слова не нужно.

Найчастіше — нічого. Нема нічого більш постійного аніж тимчасове.

А раз воно постійне, то треба старатися зробити його гуд енаф хоча б.

If you can’t make it good, make it looks good
©Билл Ворота

If you can’t make it good, make it looks good
©Билл Ворота

Х: спочатку ми написали код дуже погано, а потім зробили красиво
У: вийшло дуже погано і красиво?
© з розмови на кухні

Х: спочатку ми написали код дуже погано, а потім зробили доповідь яка ми крута команда, і який крутий продукт накували, купуйте всі. Точніше не ми, а наш піар менеджер за нас написав, щоб ви купили, то ж купуйте!

Можно часть, можно весь, зависит от многого.

ніж морочити собі яйця і з першого разу писати так ніби Всесвіт створюєш.

Я стараюся з першого разу максимально добре зробити. Це корисне і для проекту може бути, але саме головне мені для практики добре, коли думаю що роблю. Але як час обмежений, то за безкоштовно робити трушно — ні, вибачайте.

Всьо тлєн. Йдемо процедурщиною займатись.

Ладно побуду бородатым дедом-учителем. Вот твои шаги:
1. Внимательно читаешь и изучаешь классиков (ссылок на их книжки тут привели). Понимаешь почему и для какой задачи какой получился.
2. Затем берешь много листов бумаги и карандаш.
3. Начинаешь на листах бумаги рисовать прямоугольнички со стрелками и надписями, которые отражают архитектуру будущего приложения (системы) для решения твоей задачи.
4. Смотришь на нарисованное и максимально упрощаешь, но не теряя полноты решения задачи.
5. Итерируешься так, пока не получишь что-то достаточно простое.
6. Пытаешься реализовать нарисованное на уровне пустышек, без реализаций. На этом этапе придется архитектуру еще пару раз перерисовать.
7. Как всё сошлось, начинаешь писать реализации и тесты.

При наработке опыта эти шаги будешь делать в уме и почти без бумажек.

Но разве этим заработаешь? Х*як, х*як — и в продакшен!

Будь мужиком — не прогинайся під «Х*як, х*як — и в продакшен!»!

Лише після «Х*як, х*як — і доплата!»

На погодинці чого хвилюватись? Зміг вибити собі в естімейтах — візьме і зроби ©. На фікседах то інша справа. Але я так не працюю, ну його нафіг.

А я працюю. І те що я зробив працює. І навпаки, на фікседах і роблю рефакторинг — коли все вже зібрано до купи, воно значно швидше.

Є підхід, емпірично показаний, що на початку проекту намагаєшся закласти якомога більше layers of indirection. Потім, в пізньому продакшні, зайві можна видалити, щоб не заважали. А в середині розробки — вони дуже знадобляться, коли почнуться хотілки користувачів.

Это не правильный подход. Сделав сразу сложную архитектуру ты сразу ограничиваешь себя ею с точки зрения расширения. А предугадать будущее ты не можешь.

Обычно сильно не ограничиваешь, архитектура растет итеративно. Но нужно общее понятие и общая форма, чтобы оно не превратилось в грибы.

Оно нужно когда видишь что они растут. А если это не грибы, а просто деревянная лавочка по хотелке старенького заказчика — то никуда она расти не будет, максимум покрасить попросит.

Мало того, очередная хотелка потребует дать степень свободы, которая будет противоречить модели и потребует связи «многие ко многим», читай запутанности. И огромен шанс что заказчик незаслуженно получит отказ от реализации — просто потому что он «требует невозможного».

А «невозможно» — это зарезать священную корову тщательно построенной модели. Согласись, это куда проще, когда у тебя тщательно построено х*як-х*як и в продакшен. Ты просто говоришь «любой каприз за ваши деньги». И да, по мере роста это будет всё дороже, ЕСЛИ НЕ отрефакторить.

Но сколь огромная доля проектиков и задачек (особенно разовых) вообще никогда не получат существенного роста? Не говоря уж о том что потребность может тупо отвалиться, с мат.ожиданием 99% для 3 лет.

Проблема в тому, що саме на початку багато чого не зв′язано і працює тільки в голові. А там пам′ять обмежена, а надійність зв′язків «не бий лежачого». Саме тому ці layers of indirection треба нещадно випалювати ще в студентів на першому курсі.

Цінністю є ЛОКАЛІЗАЦІЯ імен, так щоб код легко ділився на прості частини. Так його простіше розуміти. Навіть якщо це призведе до repeat yourself. Останнє потім досить просто прибрати, якщо виявиться що там точна копіпаста без особливостей. Якщо з особливостями — дешевше продублювати 90% коду, аніж 10% перевести в параметри ініціалізації та налаштування.

Що менше треба пам′ятати — то краще. Оце і є власне KISS.

То працює доки не чіпаєш багатопоточність. А як треба влізти — то без структури й обмежень сам знаєш що буде.

Всё там будет хорошо, если соблюдешь то, что Пение выше написал, хоть и написал он сильно филосовски.

Левовій долі завдань багатопоточність ніколи не знадобиться. І що швидше працюватиме залізо, то далі на три літери йтимуть адепти багатопоточності.

Висновок : єдино правильного рішення немає. І як би ти не написав свій код, завжди знайдеться хтось хто назве його гівнокодом.

Ну да, только есть подходящие и неподходящие решения, неподходящие назовут говнокодом, остальное — ок

Когда подходящее переживет годик изменений требований, как его только не назовут.

Вот потому простота и есть самоцель. Даже если ценой нарушения 100500 корпоративных стандартов Оракла и Майкрософта.

Паттерн — это как течение в искусстве. Тебе дают картину (проект) и ты в нёй видишь импрессионизм (strategy).
Но стили не чёткие, смешиваются.
И вот тебе дают рисовать самому, ты же не перерисовываешь чью-то картину, ты лишь берешь идеи из примеров, так что другие люди тоже смотря на неё, угадывали в ней этот стиль.

О, супер описано. Коротко і ясно.

Я виступаю не щоб забити на формалізовані патерни, а щоб зрозуміти що ті патерни мають специфічну сферу застосування. Коли треба закрутити гвинт, ти береш хрестову викрутку — це патерн. Коли тобі треба закрутити кран — ти не питаєш на DOU, яку викрутку взяти. І не намагаєся натягнути на нього хрестову.

Ти просто не застав епохи коли все це творилося, і відповідно не знаєш ЖИТТЄВОГО ЦИКЛУ патернів. А життєвий цикл дуже простий: їх творять за потреби. А конкретну невелику купку із двох десятків з тисяч патернів тупо ПОПУЛЯРИЗУВАЛИ модні на той час автори модної на той час книги. Все. Більшість тих патернів не відноситься до прикладного програмування взагалі.

ТИ МОЖЕШ спочатку написати свої, а потім частково формалізувати. Лише в тій частині, де ти будеш ДІЛИТИ КОД на незалежні складники або документувати як окремі компоненти. Тобто, коли він розрастеться. Власне єдина мета патернів — формалізація API. Якщо цю мету прибрати, залишиться 2-3 патерни, якими ти користувався завжди навіть коли про них не знав.

Я ВИСТУПАЮ за економію часу. Не натягуй сову на глобус, особливо там де в тебе нема ані глобусу, ані сови. Натягуй рябчика на вертел та не витрачай літо на всяку дурню.

Вставлю і я своє «фі», прошу не ображатися на критику — така в мене манера.
По-перше, інтерфейси — це абстракція, а тому, вони не мають відображати деталей. Почнемо з інтерфейсу IGMAILAPI.
public interface IGMAILAPI
Все, приїхали. Гвіздками прибили наш інтерфейс до Gmail. Втрачається сенс самого поняття інтерфейсу. Як я зрозумів інтерфейс буде тільки з однією реалізацією? — якщо так, то навіщо взагалі він такий треба?
Треба робити інтерфейс який незалежить від сервісу, та який не буде своїми публічними методами видавати себе для якого сервісу він призначений. Щось типу такого:
public interface IMailService
А конкретна імплементація для гугла:
public class GMailSevice implements IMailService.
Це дасть простір для маневру в мабутньому, наприклад, якщо треба буде сервіс який отримує імейли через, скажімо, yahoo.

List<PDFDataObject> PullNewEmailsAndParsePDFData(string dateAfter = null, IEnumerable<string> emailIdsToSkip = null);
Уффффф. Серйозно? Повертаємо список об’єктів PDFDataObject? — Знову розкриття деталей. І взагалі, чому цим займається сервіс який працює з поштою? Треба повертати список об’єктів щось типу «EmailMessage», можливо з методами типу bool hasAttachment() та IFile getAttachment();
Назва методу, знову таки, не відповідає обов’язкам класу. Парсити файл вже треба в іншому класі.
Параметри для цього методу — шлак. Краще заюзати патерн білдер, скажімо створит клас та назвати його Query і використовувати його як параметр. Виглядати метод буде приблизно так:
List<EmailMessage> getMessages(Query query);
Та викликатиметься десь так:
Query query = Query.newBuilder().dateAfter("2020-01-01").dateBefore("2020-01-02"); ArrayList<EmailMessage> messages =  getMessages(query);

Проблему 1 вирішую, використовуючи «Template Method»

Ще як варіант створити інтерфейс парсера та використати для нього паттерн фабричний метод.

прошу не ображатися на критику

Якщо критика конструктивна і об’єктивна — я тільки за.

Все, приїхали. Гвіздками прибили наш інтерфейс до Gmail. Втрачається сенс самого поняття інтерфейсу. Як я зрозумів інтерфейс буде тільки з однією реалізацією? — якщо так, то навіщо взагалі він такий треба?

Так, з однією. В шарпі читав що заведено на кожен клас робити інтерфейс. Потім, коли реєструєш сервіс в IoC, то там потрібен інтерфейс. Хоча наче можна і без нього.

Це дасть простір для маневру в мабутньому, наприклад, якщо треба буде сервіс який отримує імейли через, скажімо, yahoo.

Супер!

Треба повертати список об’єктів щось типу «EmailMessage», можливо з методами типу bool hasAttachment() та IFile getAttachment();

Абсолютно. Мені вже писали. А як пропонуєте назвати клас, який буде зєднувати MailService і PDFParser, використовуючи їх послідовно, і повертаючи вже PDFDataObject, хоча насправді більш універсальну Модель поставлю в ретурні, бо потрібно вже додавати новий тип парсеру — html.

Параметри для цього методу — шлак. Краще заюзати патерн білдер, скажімо створит клас та назвати його Query і використовувати його як параметр. Виглядати метод буде приблизно так:
List getMessages(Query query);
Та викликатиметься десь так:
Query query = Query.newBuilder().dateAfter("2020-01-01").dateBefore("2020-01-02″);
ArrayList messages = getMessages(query);

Там параметр використовується лише один — dateAfter. dateBefore ніколи не буде. Точно треба патерн Builder в даному випадку? Бо ж у мене спеціалізований MailService. Робити з нього щось що б було для чогось ще, окрім як тягнути імейли для подальшого парсингу, мені б не хотілось. Розясніть детальніше в чому проблема мати специфічні параметри?

Ще як варіант створити інтерфейс парсера та використати для нього паттерн фабричний метод.

Ок, почитаю завтра. І про патерн Білдер теж.

як пропонуєте назвати клас, який буде зєднувати MailService і PDFParser, використовуючи їх послідовно

 А навіщо там клас? Там просто цикл, можна навіть в main():

const MailData* mail;
while(mail = reader.Next()) {
    for(auto p = processors.begin(); p != processors.end(); ++p) {
        PdfData* const result = p.Parse(mail);
        if(result) {
            writer.Save(result);
            break;
        }
    }
}

Можна й не створювати. Тоді код буде прямо в GMAILPullService — бекграунд сервісі який юзає оці сервіси і пише в базу.

Абсолютно. Мені вже писали. А як пропонуєте назвати клас, який буде зєднувати MailService і PDFParser, використовуючи їх послідовно, і повертаючи вже PDFDataObject, хоча насправді більш універсальну Модель поставлю в ретурні, бо потрібно вже додавати новий тип парсеру — html.

Як я зрозумів, то нас цікавить тільки TextFromBase64, TrackingNumber, DeliveryAddress, PostalCode незалежно від формату? Іншими словами, це може бути pdf чи html який містить в собі вищезгадані поля?

Іншими словами, це може бути pdf чи html який містить в собі вищезгадані поля?

Так, але в хтмл є ще одне поле — link. І потім пост процесинг буде відрізнятися для двох типів зтягнутих з мейлів. Хоча в фіналі дані отримаються однакові.

Хоча в фіналі дані отримаються однакові.

Якщо коротко, та не прив’язуючися до конкретної мови:

Enum eTypes
{
    PDF,
    HTML_TYPE1,
    HTML_TYPE2
}

public class FileHelper
{
    static eTypes getTypeOfFile(IFile file);
}

public class EmailMessage
{
    bool hasAttachment();
    IFile getAttachment();
   datetime getDateTime(); //Опціонально
   ...... // Якщо цікаві їхні атрибути то повертаємо і їх
}

public class Entry
{
    string TextFromBase64;
    string TrackingNumber;
    string DeliveryAddress;
    string PostalCode;
}

public interface IParser
{
    Entry parseFile(IFile parse);
}

public class implements PdfParser
{
    Entry parseFile(IFile parse){...};
}

public class implements HtmlType1Parser
{
    Entry parseFile(IFile parse){...};
}

public class implements HtmlType1Parser
{
    Entry parseFile(IFile parse){...};
}

public interface IMailService
{
    bool connect(string address, EType type_of_connection);
    List<EmailMessage> getMessages(Query query);
}

public class GMailSevice implements IMailService;

void main()
{
  List<Entry> entries = new ArrayList<Entry>();
  IParser pdfParser = new PdfParser();
  IParser htmlType1Parser = new HtmlType1Parser();
  IParser htmlType2Parser = new HtmlType2Parser();
  
  map<eTypes, IParser> parserMapper = {
    {PDF, pdfParser },
    {HTML_TYPE1, htmlType1Parser },
    {HTML_TYPE2, htmlType2Parser }
  }

    IMailService googleService = new GMailSevice();
    if(!googleService.connect("Гугол", IMAP/POP3)) {
        log("ашипка!");
    }

   Query query = Query.newBuilder().dateAfter("2020-01-01").dateBefore("2020-01-02"); // для Query юзаємо паттерн Builder
   ArrayList<EmailMessage> messages =  googleService.getMessages(query);

  for_each(message: messages)
  {
    if(message.hasAttachment())
        IFile file = message.getAttachment();
        entries.add(parserMapper[FileHelper::getTypeOfFile(file)].parseFile(file) )
  }
}

Або дійсно, можна ще використати «Цепочку обязаностей» замість мого варіанту з мапою (яка між іншим, зокрема в С++ може мати проблеми зі списком ініціалізації).

Замініть тег на pre, а ще краще через сервіс шарингу коду.


Enum eTypes
{
    PDF,
    HTML_TYPE1,
    HTML_TYPE2
}

public class FileHelper
{
    static eTypes getTypeOfFile(IFile file);
}

public class EmailMessage
{
    bool hasAttachment();
    IFile getAttachment();
   datetime getDateTime(); //Опціонально
   ...... // Якщо цікаві їхні атрибути то повертаємо і їх
}

public class Entry
{
    string TextFromBase64;
    string TrackingNumber;
    string DeliveryAddress;
    string PostalCode;
}

public interface IParser
{
    Entry parseFile(IFile parse);
}

public class PdfParser implements IParser
{
    Entry parseFile(IFile parse){...};
}

public class HtmlType1Parser implements IParser
{
    Entry parseFile(IFile parse){...};
}

public class HtmlType2Parser implements IParser
{
    Entry parseFile(IFile parse){...};
}

public interface IMailService
{
    bool connect(string address, EType type_of_connection);
    List<EmailMessage> getMessages(Query query);
}

public class GMailSevice implements IMailService;

void main()
{
  List<Entry> entries = new ArrayList<Entry>();
  IParser pdfParser = new PdfParser();
  IParser htmlType1Parser = new HtmlType1Parser();
  IParser htmlType2Parser = new HtmlType2Parser();
  
  map<eTypes, IParser> parserMapper = {
    {PDF, pdfParser },
    {HTML_TYPE1, htmlType1Parser },
    {HTML_TYPE2, htmlType2Parser }
  }

    IMailService googleService = new GMailSevice();
    if(!googleService.connect("Гугол", IMAP/POP3)) {
        log("ашипка!");
    }

   Query query = Query.newBuilder().dateAfter("2020-01-01").dateBefore("2020-01-02");
   ArrayList<EmailMessage> messages =  googleService.getMessages(query);

  for_each(message: messages)
  {
    if(message.hasAttachment()) {
        IFile file = message.getAttachment();
        entries.add(parserMapper[FileHelper::getTypeOfFile(file)].parseFile(file) )
   }
  }
}

Дякую гарно. Завтра перегляну і відпишусь.

Ага, добре, ідеї звідси візьму. З мапером цікаво, я так ще не робив, щоб мапер підкладав змінну з обєктом і потім над тим метод викликався.

Все майже вірно, я лише порекомендую скомпактити код: позбавитись від зайвих сутностей, а класи зробити внутрішніми анонімними, народивши їх прямо в main(). Назовні нехай будуть лише типи даних та інтерфейси.

Чому так: при усій цілісності коду логіка буде ЛІНІЙНО та КОМПАКТНО лежати в одному місці. Коли задача масштабуватиметься — це легко буде зробити, і лише коли переросте сама себе — робити розкидання по різних файлах.
Що це дасть: суттєве зниження кваліфікації для керування кодом. Ти можеш навіть віддати це студенту, який не володіє фреймворками на яких то писано, він бачитиме логіку.

Чому пропоную саме краткість: задача виглядає такою, що не виросте в монстра. Проте потребуватиме частих змін свістєлок та пердєлок. Відповідно за півроку автор сам забуде що там робив. А щоб внести дрібні правки, достатньо почитиати 1 сторіночку коду та зробити кілька копіпаст.

Левова доля задач — дрібні деталі. Для них найціннішим є швидкість читання коду та мінімальна залежність від автора. Навіть якщо вирішать переписати все «з нуля», в тому нулі буде ну дууууже багато копіпасти. А от коли все розкидано по 100500 класах та іменах, копіпаста не працює.

Там параметр використовується лише один — dateAfter. dateBefore ніколи не буде.

Це сьогодні лише один, а завтра їх буде 10. Як приклад це може бути фільтр по авторові листа, по темі, по об’єму листа, помічені як «flagged», etc.
Інший варіант, Якщо тобі треба буде обробити декілька листів за, скажімо, 2001 рік. То в твоєму випадку тобі також доведеться обровляти всі листи включаючи вчорашні. А таке швидше станеться аніж ні.

Точно треба патерн Builder в даному випадку?

Можливо замінити і іншим паттерном/паттернами, просто в даний момент білдер мені здався набільш доречним. Але в такому вигляді як в тебе краще не залишати, бо рано ч пізно це буде виглядати приблизно так:
value method(parameter1); value method(parameter1, parameter2); ..... value method(parameter1, parameter2, ... parameterN);

Бо ж у мене спеціалізований MailService. Робити з нього щось що б було для чогось ще, окрім як тягнути імейли для подальшого парсингу, мені б не хотілось.

Все вірно, він і має відповідати тільки за коннект до поштовика та повернення імейлів.

Але в такому вигляді як в тебе краще не залишати, бо рано ч пізно це буде виглядати приблизно так:
value method(parameter1); value method(parameter1, parameter2); ..... value method(parameter1, parameter2, ... parameterN);

Траплялося зі мною таке, і не один раз. Але я не знав як вирішити це. В C# є фіча, коли можна задавати аругменти вказуючи їх назву. Це дещо спрощує використання такого методу: object.MethodDo(parameter3=true, parameter10=false).

Я робив явний список фільтрів в парсері команди, передавав цей список до телефонної книги, а там — кожний запис прогоняв через весь список фільтрів.
Себто, для кожного можливого аргумента дивишся, чи він є в команді, якщо так — додаєш відповідний фільтр до списку.

Це ти вже про консольні утиліти. У мене клієнтом коду виступає інший код. І він вирішує які фільтри задіяти, беручи звісно до уваги джейсон файл конфігурації.

Так, з однією. В шарпі читав що заведено на кожен клас робити інтерфейс. Потім, коли реєструєш сервіс в IoC, то там потрібен інтерфейс. Хоча наче можна і без нього.

0_o
Не заведено, вроде как.. Как и написано выше, интерфейс дает общую абстракцию а IoC «сам» выберет необходимую реализацию. Иначе теряется смысл интерфейса.

На стьоковерфлов вичитав, що норм кожному класу по інтерфейсу.

Иначе теряется смысл интерфейса.

Легше звязки читати. Заглянув в інтерфейс і побачив специфікацію, з якою працюєш. Ще читав що добре використовувати інтерфейс iEnumerable, або ж IList, якщо нема реальної потреби в конкретному типу колекції.

На стьоковерфлов вичитав, що норм кожному класу по інтерфейсу.

Если очень хочется, то конечно. Но не для IoC. Можно вообще навернуть
IMessageService -> IEmailMessageService -> IGmailMessageService
а то вдруг понадобится обрабатывать не только почтовые, а например забирать по REST, или читать с папки.

Ще читав що добре використовувати інтерфейс iEnumerable, або ж IList, якщо нема реальної потреби в конкретному типу колекції.

Это стандартное правило. Работать с наиболее обобщенным интерфейсом из доступных.

Я давно забил на эти патерны проектирования. Если ООП в основе, то просто на бумажке рисую квадратики для объектов, как я вижу систему, и как они связываются друг с другом. Упрощенно объекты — существительные, связи — глаголы.
Затем пытаюсь эту картинку перерисовать наиболее просто (где-то что объединяешь, где-то что-то удаляешь или вешаешь интерефейс и от него наследование (аггрегации)).
Как вижу, что картинка мне нравиться, пытаюсь ее написать в виде интерфейсов и пустышек имплементаций. Не всё на картинке кладется на язык программирования, изменяю картинку.
Как всё легло на код в виде пустышек и картинка системы выглядит простой и понятной, начинаю писать собственно код.
Более того в таком варианте легко подключать еще людей для реализации системы и почти все могут писать код параллельно.

Для рисования можешь юзать UML, а можешь свои обозначения.

Цікаво. Ще до цього можна TDD. Має додати розуміння що ти хочеш від того методу. Я ще не пробував правда.

Да. Обычно так и делаю. Пишу код и тесты почти параллельно, потиху наполняя реализацию. Они удобны и для отладки.

Напевно небагато девів малюють UML на папері. В голові створили картину і йдуть писати код. Не обов’язково реалізацію одразу лупити. Теж інтерфейсами.

Я малюю на папері квадратиками структурні діаграми. Діаграми послідовностей можуть буть необхідними для складних багатопоточних сценаріїв. А от від діаграм наслідування вже відійшли навіть старі патерністи — вони заплутані й майже нічого не додають до розуміння, як прога працює.
Приклад структурної діаграми www.chromium.org/...​ulti-process-architecture
Приклад діаграм послідовностей www.chromium.org/...​ing-architecture-diagrams

А от від діаграм наслідування вже відійшли навіть старі патерністи

Это неправильно. Но их глупо делать сильно детальными. Но у них есть большой плюс — они показывают иерархии классов (объектов), слои.

Да, для обзора какой-то отдельной огромной иерархии может быть полезно. Но не в таком виде www.codeproject.com/...​/ProactorPattern/Main.jpg

для обзора какой-то отдельной огромной иерархии

Хіба тепер це ще актуально будувати великі іерархії? Чи мова не про наслідування, а про групи класів по функціоналу?

Наприклад, в мене на проекті ієрархія меседжів до різних методів купи об’єктів.
s.dou.ua/...​-files/image2_simkS4v.png
Ніколи не кажи ніколи. Те саме може бути з ієрархією CLI команд, якихось варіантів алгоритмів — хто зна, коли де таке вилізе.

Блін що з цими картинками(з сайту chromium) всі темні як дупа слона. Пробую 2 браузери оба нічого не видно.

Може, в тебе якийсь плагін до PNG прив’язаний?

Та ні. Ось як я бачу коли дірект лінк: ibb.co/CQvtS51

З інших сайтів норм.

То це лінк на ДОУ

Ага, він теж. То в тебе норм показує ці лінки?

так, файрфокс, лінух.

Він ПРОЗОРИЙ. Тобто колір фону не заданий.

Догадуюсь. То зкуя браузер не підставив свій білий фон? Зкачав і переглядаю в Photos апі, то білий фон є, але букви розмиті. Особливості вінди?

Може, картинка низького розрішення, й воно розтягнуло?

На сторінках колір білий. Коли окремо переглядаєш файл — темний. Фотошоп показує прозорий фон як клітинки.

Тому не роби із того проблему. Але якщо дуже хочеш, ти МОЖЕШ поправити стиль в браузері. Це питання редагування теми. Особисто мені здається тобі просто робити нема чого. Тупо пофарбуй фон тим картинкам та й усе.

Вот по такой многое сразу понятно. Но я бы не в виде «таблицы» рисовал, а тупо явно видного дерева. Благо мониторы большие нынче (текст можно и подрезать до нескольких первых букв и трех точек, главное, чтобы человек понимал, что там за функциональность).
Можно просто примечанием внизу дать расшифровку сокращений.

Та ну его рисовать, если там пару сот этих сообщений в иерархии. Пускай по коду смотрят. Я и сам по коду смотрю). Это так — для примера картинка.

И как ты это вычитал? Я тебе только посоветовал рисовать подобную структуру только чуть по другому, чтобы дерево было явно видно и тесктовку сокращениями делать в этом случае, если она тебе необходима там. Видя эту древовидную структуру код уже будет легко и просто понимать.

Зависит от конкретной задачи задачи. Иногда действительно полезна цепочка наследований в виде разворачивающегося дерева. Часто не нужна. Но связи не только наследование, еще агрегации, ссылки и т.п. — просто некие зависимости.
Если есть картинка на верхнем уровне о таком, то потом проект легче понимать и разрабатывать. Видно, что если что-то меняешь в этом месте, то оно потянет еще и в тех вот местах.
Очень полезно в этой картинке рисовать интерфейс отдельно, а имплементации по агрегации. наследованию стрелками показать.
В реализации иногда удобнее «фабрика», а иногда «pimpl».

О да. Это классика разбивания верующими лба об пол. Вот такое рисовать точно не нужно. Но если у тебя 7-9 классов в иерархии (не обязательно дерево с 1 корнем), то нарисовать их квадратиками и обозначить связи между ними разумно.

Ну я за время работы архитектуру не одного проекта сделал, а многих и с разными подходами, причем в зависимости и от квалификации тех, кто имплементировать будет.
Почему и написал, что отказался от паттернов проектирования.
Но для новичка очень важно прочитать и понять классиков и после уже выбирать самому нужное или перерабатывать под проект. По сути изучить основной набор шаблонов и под какие задачи они придуманы.

Все що робить TDD — ускладнює роботу через необхідність писати заглушки для тих ділянок, які ще не реалізовані. Шанси забути таку заглушку ну дууууже величезні, зважаючи на сильно зв′язаний між собою код.

Тому TDD виправдано для модульного програмування. І лише там.

Нет никакого смысла использовать паттерны таким образом, если ты не абсолютно уверен что он подходит для той или иной ситуации, то лучше обойтись

Я їх ще особливо і не застосовував, тому просто розглядаю варіанти. І по Strategy pattern мені вже Денис підсказав, що таки воно мені підійде в одній частині. Ну а далі вже не буду шукати ще якихось, раз така негативна їх оцінка. Хотілось просто вивчити, може пригодяться потім. І тут рефакторинг підвернувся. Якось так.

І по Strategy pattern мені вже Денис підсказав, що таки воно мені підійде в одній частині

с твоего описания проблемы номер 2 невооруженным глазом видно что там явно цепочка обязанностей, а не стратегия, причем ты ее и сделал только кустарным способом

Ну а далі вже не буду шукати ще якихось, раз така негативна їх оцінка. Хотілось просто вивчити, може пригодяться потім. І тут рефакторинг підвернувся. Якось так.

ты так ничего не выучишь, практически любой паттерн можно впереть практически везде, только он будет как 5я нога в большинстве случаев, нужно точно понимать какой куда сувать чтобы не сделать хуже.

можно, конечно, методом проб и ошибок разобраться, но так все шансы получить по шапке на ревью и больше не получить задание делать рефакторинг никогда

Фріланс, здається. Нема ревью.

Так. Ще не працював в команді. Особливо в професійній. Тому намагаюся зрозуміти сам як краще. І в принципі в мене не самий гівняний код я думаю. Я не стереотипний фрілансер.

Не думай, що там щось хороше

Де? В команді? От що до речі навчило мене професійності, це нетівський фреймворк. Так як там купа патернів вже імплементована і ти читаєш доки, працюєш з тим і бачиш правильні підходи.

рефакторинг на фрилансе? внезапно...

Ну я собі вибив час на рефакторинг, додавши його в час на фічі. В чому проблема? Клієнту норм, хоч він і посередник. Писати одноразовий код не для мене, особливо якщо мені його потім розширяти.

Да нет никакой проблемы, мне обычно заказчики на фрилансе бедные попадались и там не то что рефакторинг, не тестировалось ничего никем — сразу в прод

Цей мій теж не хотів оплачувати окремо таску «рефакторинг», розказуючи що бюджет із тайт. А потім я йому написав що розкидаю час рефакторингу по таскам, і він непрямо погодився. Дав йому тотал естімейт, сказав ок. Вчуся ставити естімейти і працювати з клієнтами, які не сильно розбираються в розробці. В продукті легше пояснити необхідність рефакторингу. Ще є категорія замовників які самі шукають хто б їм переписав. Так один зі мною зписався, щоб я йому туристичний сайт переписав з Кодігнітер на Ларавель. Я відповів, що на Asp.Net Core краще буде, привів аргументи, і він погодився, але сказав спочатку редизайн в програмі замовить і потім звернеться до мене. Боявся що .Net фреймворк буде оверхедом, але я йому поснив що новий не уповільнює розробку, а навпаки допомагає писати код. Поки тихо, може через коронавірус економить гроші.

Якщо я на фрілансі працював в стартапі, і неодноразово рефакторив, то що? Одна річ коли ти на фікседі і клієнту подавай пошвидше подешевше, а інша коли хаурлі, і ти працюєш над проектом довгий час. А коли ти над ним працюєш, то хоча б для своєї зручності добре б підтримувати код бейз в хорошому стані. Мені потім додавати фічі, і я не хочу страждати.

string ReadTextFromBase64(string data);
string FindTrackingNumberOrNull(string text);
string FindDeliveryAddressOrNull(string text);
string FindPostalCodeOrNull(string text);
Що заважає одним махом отримати повний набор даних і назвати метод Parse?

Різні регулярки не хочеться в купу збивати. Але якщо ти про публічний метод, то погоджуюсь. Зроблю їх протектед. Правда тоді мені треба перетворити інтерфейс на абюстрактний клас, щоб гарантувати виконання конкретних цих методів, і було зрозуміліше що кожен наслідувач повинен зробити.

Думай как гофер. У них нет интерфейсов, абстрактных классов, наследования, «протектед». Нет в языке мусорного булшита. Единственно чего им не хватает это эксепшонов. Вот и пиши на сидидиезе как на го с эксепшенами.

За що тоді платити $5к? Чим код сіньйорного гофера відрізняється від коду го джуна?

Так то я звик писати все використовуючи «булшит». Pursuit of небутигівнокодером.

Ти можеш передати в метод Properties чи якусь іншу Map, де ти ВКАЖЕШ, які саме дані тобі потрібні, або скористуєся вже існуючими заданими наборами (константами), наприклад FULL, BASIC...

А вже в коді напишеш:

if(data==null) ret=DEFAULT.clone();
else if(data==DEFAULT) ret=DEFAULT.clone();
else if(data==BASIC) ret=BASIC.clone(); 
else if(data==FULL) ret=FULL.clone(); 
else ret=data; // отут важливо, що ти працюєш саме із тим об′єктом, який тобі надали.
А потім аналізуєш на наявність кожного конкретного поля в карті та заповнюєш. Такий код дуже легко читати, бо він поділений на елементарні операції. І що важливо, все подібне лежить в одному місці, тобі не треба ніяких зайвих імен, які стирчать поза кодом. І досить легко ДОДАТИ потім інших даних, не змінюючи при цьому аж нічого. Це важливо, коли твій код викликається віддалено, через проксі-об′єкти і тому подібні речі, які ти не здатен контролювати на боці клієнта.

Хочеться вже як випала нагода переробити свій код(за гроші клієнта), то максимально багато нового вивчити. Буду писати юніт тести(викроїв час і на це), додам мапер якийсь, логування допишу в багатьох місцях щоб потім зрозуміліше було як все працює. Що до коду можна ще цікавого і плюс мінус корисного додати? А то клієнт весь час додає фічей, не кажучи які плануються ще, і якось би легко розширюваним код зробити. Краще я потрачу трохи часу на конструкцію, зате потім все не буде валитися і не потребуватиме підпорок.

От з яких архітектурних компонентів ви починаєте будувати аплікейшин? Коли ще не відомо наскільки великим він буде. Але точно не більше мідл сайз.

З багатопоточності. Модель потоків та процесів. Хто з ким як взаємодіє. Якщо це закласти на початку неправильно — буде дуже велика халепа.

Модель потоків залежить від задачі — що має робитись швидко, де доступ до заліза чи мережі чи ОС. Які дані шаряться між задачами, які — локальні. Туди ж збереження даних (persistance). Кажуть, туди ж авторизацію та аутентифікацію.

Для твоєї задачі, ніби, багатопоточність не потрібна. Якщо буде потрібна — задача схожа на пайплайн (pipes and filters) — дуже проста схема. Кожен шматок живе в своєму потоці чи процесі, щось отримує на вхід, обробляє, результат — випльовує наступній задачі. Виглядатиме:
ReadAndFilterMail -> ParsePdf -> WriteResults в 3 потоки. Парсерів можна напхать кілька різних типів в паралель, щоб вони одночасно пробували распарсить різними алгоритмами. Читачів також можна напхать в паралель (з різних ящиків чи серверів), якщо поштові сервера тормозні.

Ні, я не використовую багатопоточність в цьому апі. Потреби 0. Хоча міг би робити дещо асинхронним(в шарпі це через багатопоточність). І з async await не дуже важко писати. Ну але далі там починаються нюанси, яких я не знаю. В асп.корі є реалізована реєстрація сервісів(класів) з використанням різних Service Life Times, де сервіс може жити весь час доки ап крутиться, або при кожному реквесті, або при кожному інжекшині. Якось я був попав на проблему коли пошарилося між тредами, але забув як її вирішив і що за проблема то була. Я так по фреймворку читаю і там все вже зроблено максимально аби без глибоких знань писати асінк код.

Вірніше, є ще один перший крок — аналіз задачі. Що ми робимо: обробку даних чи модель для управління якоюсь системою. Якщо модель — з яких частин складається система? Якщо обробка даних — з який кроків складається ця обробка.

Мета: розрізати предметну область за найменшою зв’язністю. Як ти ніби маєш купу сплутаних ниток. Тобі цю купу треба розділити на кілька шматків. Що ти робиш? Розтягаєш руками в боки, шукаєш, де великі клубки зв’язує 1-2 нитки, і там ріжеш. Маєш кілька менших клубків. Це і є метод divide and conquer — ділимо велику задачу на менші підзадачі так, щоб отримані підзадачі (бажано) не перетинались. Тоді в main() створюєш усі шматки, зв’язуєш їх (хто кому передає дані) і запускаєш.

Що ми робимо: обробку даних чи модель для управління якоюсь системою. Якщо модель — з яких частин складається система? Якщо обробка даних — з який кроків складається ця обробка.

Ці сервіси що я тут описав використовуються для створення моделі, яка потім зберігається в базі, і зчитується третім сервісом, що далі надсилає запит використовуючи ці дані для отримання повної інформації. Я ж правильно розумію значення «Модель» — це клас з даними? Якщо так то я все правильно описав.

Ні, не правильно. Я мав на увазі цифрову репрезентацію реального світу. Наприклад, в модулі, що керує розумним будинком, має бути закешований поточний стан будинку, як він його розуміє. В автомобільному комп’ютері — закешовані дані автомобіля — швидкість, температура двигуна, стан кондиціонера, чи застібнуті ремені...
Себто, дані системи програмно моделюють об’єкт(и) реального світу, поновлюють цю модель, коли сенсори надають нові дані, й керують реальним світом, покладаючись на власну модель. Дещо схожим чином працює й людська свідомість.

В результаті задача системи зводиться не до алгоритмічної обробки потоку данних, а до управління й підтримки «віртуальної реальності». Відповідно, розбивка задачі йде відповідно модулям цієї моделі — для машини то двигун, кондиціонер, тормоза, ремені безпеки. Для розумного будинку — то присторї.
А якщо ти пишеш алгоритм — в ньому нема об’єктів реального світу, і ти розбиваєш задачу відповідно крокам алгоритму.

Дві абсолютно різних задачі.
При цьому віконні системи (складні інтерфейси) будуть відноситись до моделей, а не до алгоритмів.

Тоді у мене напевно обробка даних. Хоча після цих двох сервісів в базі створюється незавершена модель даних(там реально є стан, з булеан isLoaded), яка потім витягується третім сервісом і вже доповнюється. І ці сервіси незалежно одне від одного працюють, а стейт зберігається в базі.

PDF файли є різних шаблонів, від поставника 1, і 2

До начала парсинга можно узнать от какого поставщика, т.е. какой шаблон файла?

В назві імейла може бути вказано постачальника. Але де гарантія, що усі вхідні будуть приходити з правильним сабжектом. Такої гарантії нема. І думаю мій замовник буде незадоволеним якщо я прив’яжу такий байндинг.

Напиши окремий клас на детект. Ба більше, ти можеш зробити JSON, XML чи JAML файли, в які будеш задавати темплейти, щоб не правити щоразу код. Потрібен новий темплейт — докидуєш в конкретну папку новий файл, можеш навіть старі не видаляти.

Ба більше, ти можеш зробити JSON, XML чи JAML файли, в які будеш задавати темплейти, щоб не правити щоразу код. Потрібен новий темплейт — докидуєш в конкретну папку новий файл, можеш навіть старі не видаляти.

І потім з папки читати усі файли підряд? Але не можу, бо в одному з методів що парсить темплейт є нюанс як він це робить. Хоча можна стандартизувати і дійсно просто винести регулярки в JSON file.Але ці темплейти додаватися будуть лише девом, і в чому проблема перекомпілити ап. Його ж і так треба буде тестувати, я хочу юніти написати під кожен темплейт.

Так, читати всі файли підряд на етапі ініціалізації.
Проблема зазвичай не перекомпилити апп, а апдейтити його щоразу скриптом інсталяції. Зокрема, це дає тобі можливість не апдейтити там, куди не дотягнеся, і стара версія буде працювати як годиться — тобі не буде потреби аналізувати версію.

Що до тестів — тобі ніщо не заважає підкладати потрібні дані під тест, АБО в окремих темплейтах (виключно для тесту) вказати розпізнавати конкретний файл конкретним парсером.

Взагалі то дуже гнила відмазка робити як треба бо бачите йому невдобно то тестити — саме через те що воно автоматом працює. Відкрию секрет Полішинеля — те що працює можна взагалі не покривати тестами в деталях, аналізуючи виключно вхід та вихід. Інакше кажучи, надсилаєш ємейл, що має секретне поле «НАВЧАЛЬНА ТРИВОГА» в тексті письма чи в файлі, яке відповідно опрацьовується та результат отримуєш на своє мило. Так ти зможеш тестити прямо на бойовому коді.

Взагалі то дуже гнила відмазка робити як треба бо бачите йому невдобно то тестити — саме через те що воно автоматом працює.

Я можу зробити щоб класи автоматом працювали, і це буде легше і краще інтегроване в девелопмент. Повторюсь: додаванням темплейтів займатиметься дев. І нема проблеми перезаливати ап на проді.

Зокрема, це дає тобі можливість не апдейтити там, куди не дотягнеся, і стара версія буде працювати як годиться — тобі не буде потреби аналізувати версію.

Не зрозумів. Вона і так буде працювати як годиться, бо не буде змінена, лише кокнретний клас в конкретний фолдер(неймспейс) залитий. Крім того основний юзер там це крон.

Відкрию секрет Полішинеля — те що працює можна взагалі не покривати тестами в деталях, аналізуючи виключно вхід та вихід.

От до речі це питання ще не вирішив був — чи покривати тестами вже існуючий код, який не буде зачеплений рефакторингом. Залишу його на останнє, як буде вільно часу в естімейті то додам тестів.

Інакше кажучи, надсилаєш ємейл, що має секретне поле «НАВЧАЛЬНА ТРИВОГА» в тексті письма чи в файлі, яке відповідно опрацьовується та результат отримуєш на своє мило. Так ти зможеш тестити прямо на бойовому коді.

Та там не проблема і весь прод знести, воно потім з початку все потягне і занесе в базу. Або можна поставити з якої дати заносити, якщо багато старого і непотрібного в історії. Поки що нема проблеми))

Якщо нема потреби заливати кудись на чужу сторону апп — то роби як знаєш в найпростіший спосіб та забудь. Рефакторинг зробиш коли тобі дадуть задачу ще раз вже під новий тип даних.

Разові завдання краще робити що простіше, то краще. Нехай там буде копіпаста коду в 5 місцях, біс із ним, аби працювала. Тести — та ну їх в дупу.

Якщо нема потреби заливати кудись на чужу сторону апп — то роби як знаєш в найпростіший спосіб та забудь.

True!

Рефакторинг зробиш коли тобі дадуть задачу ще раз вже під новий тип даних.

Так вже дали. І нові формати пдфок додати, і хтмл файли обробляти, і сервіс дозаповнення даних роздвоїти, щоб один як і раніше брав дані з пдфок і звертався до стороннього сервісу Х, а дані взяті з хтмл файлу, дозаповнювалися звертаючись до сервісу У. Саме тому назріло питання рефакторингу, щоб я не наробив там бардаку додаючи недодаване.

Разові завдання краще робити що простіше, то краще. Нехай там буде копіпаста коду в 5 місцях, біс із ним, аби працювала. Тести — та ну їх в дупу.

Чи можна назвати разовим завданням, по якому мені вже в пятій ітерації приносять робити нових фіч? І це ще не кінець.

Можна. Якщо носять часто — то нехай носять, бо закон Мерфі стверджує — щойно ти все автоматизуєш, від тебе забажають фічу яка не вписується в модель.

Читати PDF в будь-якому випадку прийдеться(якщо це вже імейл з PDF). А далі просто перебираються шаблон парсери, допоки не видається правильний результат. Або ж можна визначати по загловку всередині PDF, і обирати відповідний парсер(такий підхід збереже деякий час на регулярках).

ты делаешь проверку заголовка и пытаешься прочесть выбранным парсером, если парсер фелится или не получает всех полей — ты начинаешь перебирать парсеры по порядку пока не сработает какой то из них

Так. По суті просто зміна порядку виклику парсерів.

Тут есть подводный камешек. Если парсеры медленные, то будет медленно перебирать. Может быть решение читать заголовок и по нему уже дергать конкретный парсер.

IGMAILAPI

ничего не должно парсить. название ужас. и почему интерфейс?
создай класс GmailReader c единственной ответственностью связываться с gmail и вытаскивать все оттуда в сыром виде в аггрегированном виде в классе GmailMessage.
Итого
class GmailReader  { List<GmailMessage> Read(...) } class GmailMessage { string Body List<File> PdfAttachments }

почему интерфейс?

Правило — створюєш сервіс, роби його з інтерфейсом.

ничего не должно парсить

Напевно так і є.

создай класс GmailReader c единственной ответственностью связываться с gmail и вытаскивать все оттуда в сыром виде в аггрегированном виде в классе GmailMessage.

Дякую, так і думав що треба щось робити. Але тепер я не знаю як називати клас, який буде розбирати цей список повідомлень, і вже викликати або PDF парсер, або HTML парсер. І як зробити, щоб додавання нових форматів не змушувало додавати новий if в цьому класі-клієнті GmailReader? Якщо наприклад прийдеться зчитувати plain text, в додачу до існуючих. Тут точно має підходити один з існуючих патернів, бо все типове. Ну ось для класів-парсерів застосовується патерн Strategy, і в клієнті цих парсерів, який перед ними викликає GmailReader, прописується рефлексія з витягом усіх наявних в неймспейсі класів-парсерів, і проганяється по ним. Я так бачу.

Дивись. Які ти маєш задачі?
1) Вичитати мейли.
1а) Пофільтрувати мейли під час читання за якимось параметром.
2) Розпарсити якісь дані з вичитаних мейлів, алгоритм задається ззовні.

Маємо 3 задачі. Відповідно, під кожну з них робимо клас чи функцію (divide & conquer).

Вичитати мейли — робимо інтерфейс читалки мейлів, він отримує в конструктор стратегію фільтрування. Робимо конкретні читалки, що наслідуються від цього інтерфейса. Для gmail — одна читалка, для дампу з аутлука — інша читалка.

Стратегія фільтрування — функція чи клас, що на вхід отримує параметри мейла, й каже, чи треба цей мейл вичитувати. Читалки юзають цю стратегію. Патерн Strategy.

Парсер — так само, інтерфейс парсера, що отримує на вхід мейл, а на вихід дає розпаршені дані. Від нього наслідуються конкретні парсери конкретних типів пейлоада.

Мейн — треба створити потрібні класи (залежно конфігу чи параметрів командного рядку), зібрати усе докупи (з’єднати класи в тулчейн) та запустити.

Для збоченців-багатопоточників та лінуксоїдів: читаємо архітектурний патерн Pipes and Filters. Але навряд чи воно тут треба, хіба що будуть дуже великі об’єми даних.

Але є одне але: потрібно аналізувати не всі мейли, а реагувати на нове. Від так вигоди не буде майже ніякої.

Взагалі-то абстрактна модель в тебе вірна. Але реалізовувати її простіше не одразу, а як рефакторинг вже працюючого процесу. І швидше, і тестити легше.

Патерн Strategy тут не сильно допоможе. Логіка має ризик втопитися в смітті коду абстракцій. Тут ти вірно помітив, є дуже виражений PayLoad залежно від того, що знайдено, тому не здивуюся, коли доведеться розбити всю аплікуху на дві. Одна буде відповідати за PayLoad, інша — за парсинг та пошук. Інша може й не знадобиться, якщо код виявиться простим, кілька кілобайт, тобто зайвий раз його проапдейтити не проблема при зміні PayLoad.

Питання от у чому — різним людям може знадобитися різний PayLoad, навіть різним співробітникам тієї ж організації.

Питання от у чому — різним людям може знадобитися різний PayLoad, навіть різним співробітникам тієї ж організації.

Ну тоді linux-style pipes and filters. І ціна проекту х10. Отримаємо дяді Вітін любимий gstreamer. І кожен буде його збочувати в командному рядку своїм незабутнім чином.

Ну ви даєте тут. Мені стільки не заплатять.

Типу такого ps aux | grep -v grep | grep firefox
Один напише собі „vasya.pupkin” > gmail | readallnew | findpdf | getnames | dumptofile > vasya.names
Другий напише „ruhab” > mailru | readwithattach | findjpeg | resize | dumptofolder > prn
Перший розпарсить pdfки, другий — збереже картинки. Користувачі самі збирають тулчейн в команд лайні.

і ще х10 різних інсталяційних проектів із 100500 залежностей

Хочеш в цей проект ще затягнути ієрархію PayLoad?

Нащо ієрархію? Копіпасту :)
Коли їх набереться мішок — тоді й робити ієрархію.

Правило — створюєш сервіс, роби його з інтерфейсом.

Правило: якщо тобі нема вигоди із правила, не виконуй його. Зазвичай, єдина мета творити таке — лише генерація автотестів. А загальне правило — що далі ти передаєш можливість користувати твій код, то більше абстракцій потрібно робити заради мінімізації змін коду на іншій стороні.

Створити інтерфейс не важко. І з ним легше читати потім морду класу, не дивлячись в реалізацію.

Интерфейс с одной реализацией не нужен. Только мучаться потом каждый раз «Go to implementation ...»

Реєструвати класи в DI без інтерфейсів напряму? Які мінуси при написанні тестів під класи, а не інтерфейси(я ще тести не писав лише теорію знаю трохи)?

Які мінуси при написанні тестів під класи, а не інтерфейси(я ще тести не писав лише теорію знаю трохи)?

Тесты в которых нужны интерфейсы, те в которых мокают — бесполезная трата времени за которую сейчас мало какой заказчик отважится платить. Я по крайней мере уже больше 10 лет не видел, чтобы кто-то на проекте такой херней страдал.

Реєструвати класи в DI без інтерфейсів напряму?

У тебя врядли там проект такого масштаба, чтобы там нужен был IOC контейнер. Да и вообще британскими учеными не доказано нужен ли вообще IOC контейнер.

У тебя врядли там проект такого масштаба, чтобы там нужен был IOC контейнер. Да и вообще британскими учеными не доказано нужен ли вообще IOC контейнер.

Смішно. Нічого не потрібно. Тільки от це все йде по дефолту в фреймворку. І... ну я не знаю. Тут почитаєш, так всі правила написання хорошого коду, всі підходи — нах нах, і треба в процедурному з функціями писати. Може мені не треба більше англомовний інет читати про програмування. І доку фреймворку теж не буду читати. І фреймворк не буд використовувати. І C#.

Може мені не треба більше англомовний інет читати про програмування.

Теорія перевіряється практикою. Те, що пишуть — треба пробувати й дивитись, чи поліпшилася якість життя.

Тільки от це все йде по дефолту в фреймворку.

По дефолту по ДНК ти мавпа. Але еволюція чомусь мавп не дуже любить

Нема ніякого сенсу не використвувати DI, коли він вже готовий для використання.

Приведемо діаграми наслідування та послідовностей для hello world:
upload.wikimedia.org/...​on_Design_Pattern_UML.jpg

У мене не hello world. Приведу кроки для реєстрації класу в дефолтній DI Asp.Net Core:

1. Створюю клас
2. Додаю його одним рядком в Startup.cs
3. Інжектю куди потрібно через параметри конструктору, і присвоюю приватному проперті цього класу
4. Done

Але тоді в тебе в редакторі 2 вкладки замість 1 на кожен клас — з майже однаковими заголовками. Тому спочатку створи клас, та назви його інтерфейс :)

PS. Так кажеш, нібито тобі воно треба, дивитися в реалізацію. Бо якщо так, то взагалі не варто було виокремлювати клас, лише інтерфейс, а клас створюй внутрішнім анонімним за потреби, а то й через лямбда-вираз.

Але тоді в тебе в редакторі 2 вкладки замість 1 на кожен клас

Правдиво лише в процесі розробки. Як клас дороблений, у мене лише 1 файл — інтерфейс, в якому в декілька рядків опис усіх методів класу. Не реба скролити, як у випадку з класом.

Я так і кажу. Якщо світиш інтерфейс — не світи клас без зайвої необхідності, роби анонімні внутрішні класи.

Наприклад, в окремому файлі:
Interface Speak{public void default saySomething(){...};}

В коді:
task.promises.onFail.speak = new Speak(){...}

А це що за мова? В шарпі в інтерфейсі не можна наскільки я знаю додавати приватний клас.

название ужас

Занадто широке значення має, вірно? Я допускав, що можливо буде необхідно додати також метод видалення повідомлень. І в такому разі я б додав до цього класу метод Delete. Але з вірогідність 99% такого функціоналу не потребуватиметься.

Base64.Decode

Там всередині методу ще PDF бібліотека використовується, щоб з base64 отримати текст.

Ось:

public string ReadTextFromBase64(string data)
        {
            // Converting from RFC 4648 base64 to base64url encoding
            // see http://en.wikipedia.org/wiki/Base64#Implementations_and_history
            data = data.Replace('-', '+').Replace('_', '/');
            var bytes = Convert.FromBase64String(data);

            using (var pdf = new PdfDocument(bytes))
            {
                return pdf.Pages[0].ExtractText();
            }
        }

І ці люди забороняють колупатися в носі щось розповідають про шаблони проектування.

return new PdfDocument(Convert.FromBase64String(data.Replace('-', '+').Replace('_', '/'))).Pages[0].ExtractText();
А то й взагалі викинути метод та вставити цю строку коду де треба.

Чому так: якщо код влазить в 1 строчку, плодити зайві імена моветон. Вони займають оперативну пам′ять програміста, якої в нього ну дуууже мало.
Навряд чи комусь знадобиться вникати у деталі коду за межами цієї строки. Важливо що буде на виході, чи воно зрозуміле що там ОЧІКУЄТЬСЯ. Якщо очікування не виправдовуються, лише тоді цю строчку взагалі читатимуть, тобто майже ніколи.

Код має бути зрозумілим, коли читаєш його «по діагоналі». А це означає позбутися від зайвих імен.

Акцент: ти заховав у код Pages[0] — зовсім не очевидну магію. Якщо це строчка в загальному потоці, це побачать у разі дебагу. Якщо десь в надрах функції, з її імені не очевидно що там будуть якісь фокуси.

А то й взагалі викинути метод та вставити цю строку коду де треба.

Почитай код уважніше. Там використовується бібліотека ще.

Чому так: якщо код влазить в 1 строчку, плодити зайві імена моветон. Вони займають оперативну пам′ять програміста, якої в нього ну дуууже мало.

Там одна додаткова змінна, яка на мою думку лише покращує читання. А от цей код прийдеться ще розпарсювати:

return new PdfDocument(Convert.FromBase64String(data.Replace(’-’, ’+’).Replace(’_’, ’/’))).Pages[0].ExtractText();
Акцент: ти заховав у код Pages[0] — зовсім не очевидну магію. Якщо це строчка в загальному потоці, це побачать у разі дебагу. Якщо десь в надрах функції, з її імені не очевидно що там будуть якісь фокуси.

Розумію. Хоча це специфіка всього апу — потрібна лише перша пейджа пдфу. Вважаєш варто винести індекс в аргументи? Чи можливо пройтися по всім доступним пейджам і зібрати текст в одне ціле?

Не виносити. Взагалі записати в 1 строчку. Чому так вже пояснив — це досить стабільна частина коду, яку 1 раз відтестиш, і НІКОЛИ не доведеться міняти. А від так — і читати потреби не буде.

В цьому і принцип гарного коду — логіка читається легко, в деталі лізти не потрібно щоб зрозуміти, а коли знадобиться — чітко видно ДЕ САМЕ все відбувається. Не забувай, що синтаксис підсвічується, що межі дужок показує редактор при наведенні миші.

Розібратися в 1 строчці досить легко, якщо знати, що все що потрібно лежить прямо в ній. Коли ж розмазати код по кільком десяткам місць, то буде бюрократична обфускація.

Не виносити. Взагалі записати в 1 строчку. Чому так вже пояснив — це досить стабільна частина коду, яку 1 раз відтестиш, і НІКОЛИ не доведеться міняти. А від так — і читати потреби не буде.

О, круто підмічено. Тру, той код змінюватися взагалі не має.

В цьому і принцип гарного коду — логіка читається легко, в деталі лізти не потрібно щоб зрозуміти, а коли знадобиться — чітко видно ДЕ САМЕ все відбувається.

Є інтерфейси для цього. З ними взагалі в реалізацію не лізеш, лише як припече. То для чого скорочувати тоді?

Розібратися в 1 строчці досить легко, якщо знати, що все що потрібно лежить прямо в ній. Коли ж розмазати код по кільком десяткам місць, то буде бюрократична обфускація.

У мене нічого не розмазано, все там стоїть одне біля одного, і легко читається. Бачу одну проблему — мутування вхідної string. Але щось забув вже чи string передається Reference чи Value.

Проблема в тому, що від розділення код стає «турбулентним». Додаючи зайву функцію, ти створюєш їй ім′я, сутність, місце розташування... і все це програміст має запом′ятовувати просто щоб прочитати код.

Наскільки простіше воно читається в ламінарному потоці, коли ти не намагаєся розкидати все по 100500 абстракцій, а тупо пишеш що ти власне робиш. Ото й весь патерн KISS.

Навіть якщо для цього прийдеться пожертвувати «don’t repeat yourself». Взагалі-то цінність цього принципу ніким не доведена, практика показує що він більше шкодить. Нема нічого поганого в тому, що ти скопіпастиш 1-10 строчок коду в кілька 2 місця замість робити з того стратегію.

PS. Є код логіки, він має змінюватися. Є код який забезпечує рутинну обробку — він зміниться навряд чи колись. А коли зміниться — треба щоб було зрозуміло, ДЕ САМЕ обмежується його дія. І чим ця кількість коду менша — тим краще.

Зайві імена вимагають дуже багато розумових зусиль. Їхня відсутність гарантує розуміння, що все що ти бачиш на екрані — тобі достатньо, що нічого іншого не треба розуміти. А особливо треба розуміти, коли воно тобі НЕ ТРЕБА, коли ти прочитав назву змінної — і все зрозуміло що там МАЄ БУТИ.

Дивно таке читати від джависта. Особисто мені приємніше працювати з кодом, який розділений по категоріям.

Я дещо старший за Жабу :)
Розділення має бути там, де треба думати. Де думати не треба, краще писати компактно та коментувати що саме робиться, бо через скільки років то прийдеться читати невідомо.

Варто хоча б комент написати, що тут нам треба лише перша сторінка. Можна — номер сторінки зробити іменованою константою.

А чому саме GMAIL, чим тобі IMAP не дає щастя? Чи ти пишеш додаток саме до GMAIL для інтеграції його туди? IMAP наскільки пам′ятаю дозволяє робити пошук на боці сервера.

Чи ти пишеш додаток саме до GMAIL для інтеграції його туди?

True

IMAP наскільки пам′ятаю дозволяє робити пошук на боці сервера.

Не вникав, але стрьомнувато мені лізти в такий лов левел. А так завдання не вимагає роботи з різними email серверами, тому гугл апі цілком достатньо. По суті там усе йде навіть на одну адресу.

Істина занадто проста, щоб бюрократу її зрозуміти: Не лише «правильні» патерни є патернами. Патернами є взагалі все. Все що ти можеш уявити, все що можеш описати, є патерном. Тому пиши так, щоб було зрозуміло підлітку на інгліші, навіть якщо він індус.

Можу коротше: KISS

Та я ж не намагаюся ускладнити все аби ускладнити. Мені реально необхідні абюстракції, які дозволять зробити легку розширюваність наявних структур. Бо потім прийдеться знову додавати нові формати зчитування імейлів. Dmytry Lehenkiy вже підсказав, що потрібно випилити парсинг з GMAIlAPI і повертати все одним обєктом незалежно від очікуваного формату. Хоча тоді мені прийдеться взагалі прибрати фільтрацію імейлів по типу, хоча це і не має принести проблем, якщо ліві меседжі не пройдуть парсинг в подальшому і будуть проігноровані.

Все що ти можеш уявити, все що можеш описати, є патерном.

Наскільки хорошим? Наскільки провіреним? Наскільки надійним? То чому не використати підходи, які вже показали себе в бою?

Бо потім ніхто це не зможе прочитати. Патерни — це хаки, коли не виходить зробить нормально — просто та очевидно. Коли купу патернів навертають просто так — проект ні читати неможливо, ні підтримувати. Й тормозний буде страшенно.

Розумію. В моєму ж випадку ти й сам застосував патерн Стратегію. Значить він корисний тут. Та я влдасне й шукав щось подібне. А загалом, то в самому фреймворці Asp.Net Core багато патернів використовується, і вони реально допомагають. Так, крива навчання пряміша. Ну але на пхп я вже накодився з Laravel. Ба навіть в Laravel є патерни! То я не розумію чому все так проти них виступають. Може й фреймворіки непотрібні?

Проти патернів виступають або функціональщики, в котрих нема ООП, або ті, хто їх не знають.
Більшість виступає проти непродуманого використання, котре змінює життя на гірше.

В такому я теж проти патернів. Але ж, повторюсь вже не знаю який раз — я шукав патерн під завдання, з ціллю застосувати якийсь стандартний підхід як основу для свого алгоритму.

Ті патерни були корисні в ті роки, коли їх писали. Зараз то лайно мамонта.

Сказав Пєніє перед використанням чергового ітератора

І що з того? Ітератор — то вічне, а от що таке патерн Strategy? Ймовірність що буде зрозуміло читачу менша за 10%, а з них — менше 1% що зрозуміло вірно. А все зайве має відмирати, і досить швидко.

А от коли треба передати дані сокетом, а тип сокету, тип шифрування, та формат даних в конфізі записані — хутко стратегія згадається. Бо наслідуванням таке не зробиш.

Я не кажу що то поганий патерн. Погане в ньому — це намагання його формалізувати, тим самим роблячи спробу замінити його значення з англійської мови.

Цей патерн є абстрактним, тому має бути інтерфейс, який і пояснюватиме, ЩО САМЕ мається на увазі цього разу. І документація не завадить. Я вже не кажу, що у більшості випадків це лише... строкова константа. Інакше кажучи, «стратегія» це і є синонім терміну «патерн».

Може у вас усе вже зашили в бібліотеки. В нас ще руками треба коннектить TCP сокет чи не коннетить UDP. Шифрування теж якимось лібами, котрі можуть буть різні для різних типів шифрування. Запаковка в JSON чи в XML — також різні функції. А ще зверху можна зіпать. От і виде 3-4 стратегії як поліморфні функції, по одній на кожен параметр.

В С взагалі вказівники на функції — те, на чому мова досі жива, і з С++ бореться. По суті вказівник на функцію і є стратегія.

От сам бачиш: нічого конкретного. Кожного разу стратегія означає щось своє. Це порожній патерн. Але він несе на собі спадок мови та традицій використання в конкретній області.

Подивись на патерн «політика», і побачиш те саме.

Але ж він має значення! В даному місці треба зробити додатковий поліморфізм поведінки, що не визначається іерархією класа.
Конкретна семантика, незалежно метода імплементації. Як з усіма патернами.

додатковий поліморфізм поведінки, що не визначається іерархією класа.

Типу композиція замість наслідування?

Композиція з передачею частини логіки роботи основного класу тому, кого він має.
Основний клас щось робить, але для кількох конкретних шагів викликає методи додаткового класу-стратегії (чи функцію через вказівник, чи ще якось).
Таким чином, ці конкретні шаги змінюються без додавання наслідників головному класу. Дуже важливо, коли є купа параметрів зміни поведінки (ступенів свободи) — ти просто не зможеш написати стільки субкласів руками, а підставивши в один клас 5 стратегій — отримуєш 5 незалежно змінюваних шматків алгоритму, по одному ступеню свободи на стратегію.

Еге ж. Але найчастіше вся та логіка прописана в одному місці, а стратегія є лише КОНСТАНТОЮ, яку треба передати для визначення яким із прописаних алгоритмів користуватися.

Саме тому стратегія є лише мовним шаблоном, і не лише до програмування відноситься. І таких шаблонів ще туєва хуча. І вони створюються за потреби, і зникають за її відсутності.

Не знаю. В мене весь час трапляються саме функції чи набори функцій як стратегії. Щоб константа була стратегією — не бачив.

Якщо вони компактні, то нащо під них писати окремі класи, якщо можна все запхати в один, і щоб назовні стирчали лише імена констант? Дешево та сердито.

Звичайно, якщо тобі не платять за кількість строчок.

Швидше працює й легше читати.
Чому перейшли від switch() до наслідування? Можна ж усі методи один раз написати, а в кожному — switch() з усіма вариантами поведінки сабкласів.

Можна по всякому. Мета саме в тому, щоб не плодити сутності. Якби в людей було більше оперативи, все це було б зайвим. Але проблема в тому, що при читанні коду задіюється довготривала пам′ять, і «розвидіти» зайве ти не можеш. Як результат, погано написаний код може зайняти в 20 разів більше часу на його підтримку та подальшу розробку.

Тому навіть жертвуючи ефективністю нерідко абстрактну модель обирають заме за принципом легкості читання. А жертвують дуууже сильно. Особисто я вважаю що цих священих корів треба різати, доки вони маленькі, і замість того щоб плодити абстракції — писати інлайн все, що можна писати, аж допоки не назріє рефакторінг і не буде рішення якісь ділянки зрілого коду ефективно відокремити в окремі сутності.

З таким підходом код стає коротшим втроє. Але може мати хвости з назрілого, але не виконаного за браком часу (та натхнення) рефакторингу. Зазвичай сам код встигає устаріти раніше, припиняє часто змінюватися, і зайві рухи ніхто не оплатить.

Можлива проблема — коли код потрібно передати, і чомусь вважається що оскільки він старий, то можна передати вчетверо дешевшому спеціалісту. Якби коду було в 3-5 разів більше, то і спеців би дали більше або краще. Але коли код маленький — жаба давить, і це добиває проект. особливо коли джун приходить з ефектом Данінга-Крюгера та лізе переписувати те, чого не розуміє, а вже потім скаржиться, що код лайно [вже криво недопереписана непрацююча версія].

В тебе в інший бік відхилення. Код кожної функції роздувається, і на екран влазить менше функцій.
Приклад: якщо я дебажу, в мене підключена конкретна стратегія. Відкриваю файлик і бачу усі її методи з кодом на 1-2 екранах. Якщо робить одну стратегію з світчами про всі випадки — коду буде вже 10 екранів, і я не зможу одночасно бачить усе, бо воно розбавлене тим, що наразі (в даній конфігурації) зайве.
Той самий аргумент, що й для наслідування — код компактніший, бо в ньому нема гілок, котрі не використовуються.

Для цього ООП і задумовувалось — щоб можна ізолювати код по призначенню і функціональності. А Пєніє каже складати все в купу...

Думаю, не для цього, а як зручний метод моделювання об’єктів реального світу. А потім сподобалось, і абстрагували до усіх видів задач.

Не все. А лише невеликі купки, особливо коли там в 1 строчку той код можна записати. А ділити — вже коли є що ділити.

Так. Код функції роздувається, але це не проблема. Коли коду буде 10 екранів — то я піду собі пива наллю заміть рефактору, краще почекаю ще років з 5 поки там буде 20 екранів.
А от коли треба відкрити 70 вкладок лише щоб зрозуміти що воно робить...

Якби в тебе була книжка на 20 сторінок, ти б побоявся її читати — за умови що вона не посилається щоразу на інші? Бо я знаю безліч таких людей, що не здатні осилити Закон про захист прав споживачів — там 30 сторінок :)

А от коли треба відкрити 70 вкладок лише щоб зрозуміти що воно робить...

Крайнощі. І тут йде порушення правила тримати код незвязаним, і функціонально повним.

Найбільшим обмеженням є твоя власна пам′ять. Якщо для її оптимізації треба порушити всі правила — роби це не вагаючись, зекономиш багато часу.

Код можна робити зв′язаним. Функціональне програмування взагалі страждає циклічними зв′язками. Питання лише в тому, чи не занадто зайвих зв′язків, які виникли паразитно, яких нема в логіці коду.

Тому практичне правило звучить так — що коротша зона видимості, що компактніший код, то більше та насичініше можуть бути зв′язки. Саме це є основною вигодую від ООП. До ООП те саме робили через... особливості іменування змінних.

Переход от switch к наследованию не всегда удобен. Помню был проект с общением с роботом и по протоколу там было 256×256 функций возможно.
Программисты во франции заюзали гигансткий switch. Была жесть, но ей 10 лет пользовались копи-коле у них рулило (копипаста по ангельски). Мне осточертело в этой простыне копаться и я тупо сделал таблицу 256×256 указателей на функции и никакого наследования. Причем реализовывалась только малая часть от всей этой таблицы. Соответсвенно там, где не нужна была реализация я ставил NULL.
Код упростился очень сильно. Вызов функции был элементарен.

А как такой монстр у них получился? Очень просто. Сеньор набросал первый вариант с 3 функциями. А дальше дешевые французские вайтишники (это у них было в 90-х до кризиса доткомов) тупо накопиколили. Один из них пережил тот кризис и был моим начальником (художник по образованию), но не дурак и полностью отдал разбираться с кодом и фиксами и рулить програмерами в мои руки. Он взял на себя общение с вышестояшими начальниками и выдавал мне список фич и фиксов на ближайшую неделю-две, что нужно добавить. Я ему говорил, когда и что сможем сделать и в таком режиме мы успешно проработали 5 лет.

Так ты же сам сделал виртуальный метод, и говоришь, что облегчило жизнь

Нет. Не делал. Зачем. Конечно натянуть на это решение можно виртуальность, но тогда и асм можно назвать языком поддерживающим ООП.
Я сделал тупо таблицу указателей на функции, забил NULL и помещал в нужные клетки указатели на реализованные функции.
Это вообще С подход и даже не С++.
А вот если бы присобачивал туда виртуальность с стиле С++, то ой как много кода писать пришлось бы лишнего.

Так виртуальность — это и есть вызов функции по указателю из таблицы. Ты сделал виртуальный обработчик сообщений в зависимости от типа сообщения.

Ну вот и зачем тебе эта игра слов? Ты же прекрасно понимаешь в чем разница между виртуальностью в С++ и тем, что я описал.

А ни в чем. Она примерно так и реализована. И в ядре линуха — тоже.

А ты таки почитай про виртуальность в С++.
То что курица птица, не говорит о том, что она умеет плавать, как утка.

Вызов функции по указателю из таблицы по указателю, равному адресу объекта.

Для чого тоді Стратегія? Enum передав і все. Якщо в тебе код в одному місці, то там лімітована кількість варіантів, та ще й треба весь час чіпати той клас де основний код в купі забитий. А з Стратегія ти можеш безліч робити.

Саме це я і сказав. Стратегія є мовним патерном, а не патерном програмування. Намагання замістити цей шаблон чимось іншим можуть робити тільки дибільні бюрократи, які нічого путного створити самі не здатні, а найчастіше взагалі вважають працю чимось недостойним себе (дартаньянів).

Улюбленими патернами подібного толку є «вид» і «тип» як класифікаторів даних. Дуже обурюються, коли їх називаєш мудаками за використання цих антипатернів. На що я зазвичай відповідаю, що мудаки бувають двох видів і чотирьох типів. Якщо назвуть всі — вони не мудаки.

Стратегія по суті є таким самим антипатерном, щойно ти забудеш її задокументувати. Цей патерн порожній. І саме тому при реалізації стратегій так важливо мати їх ОБМЕЖЕНУ кількість — той самий ENUM. І для легкості розуміння означення стратегії в окремо взятому контексті, реалізацію краще за все робити саме в цьому контексті.

Що менша зона видимості, то дешевше обходиться розуміння коду. Щойно твоя Стратегія стає чимось невідомим — ти зарив в код джерело багів та відвертого нерозуміння, ЩО САМЕ очікується від реалізації цього шаблону.

По суті Інтерфейс є повним аналогом Стратегії в теорії. Але на практиці Інтерфейс є суттєво обмеженим в мовах програмування (і це вже не виправити), а Клас — занадто загальним — і це вже теж не змінити в теорії. Хоча на практиці ти розумієш що воно таке, щойно захочеш написати свій ClassLoader або прочитати чужий. Чим не стратегія?

По суті все зводиться як зазвичай до одного: використовуй річ там де їй місце, і не тули куди попало.

Ото ж бо й воно. В теорії саме так. А на практиці навпаки — тули все що можна тулити, все що вважаєш за потрібне, а вже потім оптимізуй та викидай зайве.

Навіть письменники так пишуть — спочатку все що в голову лізе, вже потім редактура.

Чому так? Бо обмеження найчастіше в дрібних деталях. Тому починати писати потрібно з дрібних речей, а вже потім робити композицію. Щоб не доводилося переписувати.

Чижденко он робить одразу щось пристойне через діаграми. Але підхід твій розумію. Наскільки пропонуєш робити все що вважаєш за потрібне? А якщо з самого початку вважаєш за потрібне поділити на декілька класів?

А потів те, що ти понаписував, не тулиться до купи. І половину треба переписать.

І не факт що виділять час на переписування. Або виділять, коли все вже буде триматися на божому слові. Щось мене тролять сіньйори в цій темі.

Не звертай уваги. Читай Pipes and Filters в першому томі POSA. Маєш досить схожу задачу.
Сам знаєш, що єдиного вірного варіанту нема. Тут кожен розповідає, як йому самому зручніше. А якщо усе те ліпити докупи — буде C++11.

Pipes and Filters

Це про можливість комбінувати обробники? В моєму завданні важливий лише довершений стан обробки, тобто усі обробники мають спрацювати. Хоча стан напівобробки теж відображатиметься на окремій сторінці.

А загалом, то я б хотів вже пошвидше почати писати код, не влізаючи в додаткові архітектурні речі) Напевно chain of responsibility ще ознайомлюся.

Це про можливість комбінувати обробники?

Ні, це — про те, щоб твій користувач сам обирав, які конкретні етапи обробки даних робити. Ти йому надаєш коструктор, а він — робить з ним що схоче в своєму коді.

Я це і мав на увазі. ні, потреби в такому не маю.

Як без патернів писати великі апи, щоб потім інші деви, що не брали участь в розробці, могли швидко входити в архітектуру апу? Можливо принципи застосовуються? Вони точно ж. А каркас коду якийсь?

Ніяк. Я ж так і пояснив: патернами є все. Проблема в тому, що якісь патерни стали релігією — і тепер люди, що не вміють писати код, розказують що саме ото є патерни, а все інше то єресь.

БЮРОКРАТІЯ — от що воно таке. Смертельна хвороба будь-якого бізнес-процесу.

Схоже на те, для чого робили Java. Щоб люди які не вміли програмувати, могли щось корисне зробити.

Навпаки. Щоб люди, які гадали що вміють програмувати, не наробили дурниць. Особисто я вважаю, що зробили дуже погано. Треба було лише УСКЛАДНИТИ написання речей, де треба 2 рази подумати. І спростити все інше.

Найбільшою проблемою Java зараз є Object. Наскільки б усе було простіше, якби він майже перестав існувати, якби компілятор не наслідував би від нього типи даних без зайвої потреби. Або навпаки, залишив пустим, все зайве спочатку об′явивши Deprecated, а вже тоді випилили. З можливістю НАДАТИ логіку Object через композицію деяким об′єктам через аннотації.

Закохавця в цей гіпотетичний підхід. Пєніє в архітекти Джави!

З можливістю НАДАТИ логіку Object через композицію деяким об′єктам через аннотації.

Метапрограмування. Я так зрозумів, що метапрограмування, це наступна гілка еволюції ООП. Спочатку було наслідування, потім композиція, а тепер метапрограмування?

Нема еволюції ООП. Є зміна моди, що може дещо коригувати зі зміною поширених задач в індустрії.

JavaScript довів можливість еволюції ООП, і що заборона відокремлювати методи від даних — лише «священа корова». ООП є по суті синтаксичним цукром, а іноді хочеться без нього.

От чому в ООП нема нативних методів DuckCast, які б здебільшого працювали на етапі копіляції.

Нічого не зрозумів. Не знаю JS.

Не буде патерн Стратегія, буде свій велосипед. Чим краще? Велосипед більше зрозуміють?

Бо стратегія — то не патерн. То лише СЛОВО, яким означають по суті патерн. А що саме робить стратегія — в кожному випадку мається на увазі своє, властиве лише конкретній предметній області.

Патерн «стратегія» використовується у випадках, коли є декілька способів робити щось. Він саме для того і потрібен, щоб ПОЯСНИТИ читачу, що в конкретному місці коду має місце вибір способу. Тому саме в найменуванні змінних чи параметрів має бути СЛОВО «Strategy» — оце власне й весь патерн. Він взагалі не до мови програмування відноситься, а до англійської.

Все ж там є певний алгоритм, як реалізовувати її в ООП(композиція класів).

Та ні. Може буть вказівник на функцію. Може ще якась невідома хрєнь, лямбда там. Неважливо. Важлива семантика. Нема «правильного» методу. Час та випадок для усіх їх.

Коли є список класів з певною логікою, що належать одному інтерфейсу, і потрібно обходити їх всі автоматично, викликаючи певний метод, чи правильно застосовувати рефлексію на стороні клієнту коду? Щоб при додаванні нового класу, той атачився автоматично без необхідності модифікації класу клієнту. Наче такий підхід реалізує OCP:

deviq.com/open-closed-principle

«The Open-Closed Principle (OCP) states that software entities (classes, modules, methods, etc.) OpenClosedPrincipleshould be open for extension, but closed for modification»

Чим тобі ітератор не ітератор? Складаєш усі класи до колекції, потім робиш foreach() чи for(it = begin(); it < end(); ++it);
Чи я щось не зрозумів в умові

Я хочу щоб в подальшому достатньо було просто створити ще один клас в папці і той клас кодом клієнтом автоматично брався, без додавання його до колекції. По суті це динамічна генерація колекції. Я вже так робив, просто питаю чи правильно воно.

Не знаю — в С++ так не можна. Я б пострьомався. Але в кожної технології — власні правила. Тобі видніше, як у вас таке роблять.

То альтернатива моєму підходу лише одна — едітити клас клієнта додаючи в колекцію новостворений клас?

В С++ пишеш код, компілиш, і надсилаєш клієнту нову програму. У вас якось інакше прийнято, мабуть. Тут я нічого не скажу.

Так. Я про намагання не порушувати принцип Open-closed.

Забий на нього. KISS.
Принципи потрібні для зручності. Якщо вони незручні — позбався. Твій проект і тобі вирішувати, як простіше. «Правильного» варіанту для усіх випадків нема. Часто треба обрати найменше зло. І котре з них найменше — залежить від обставин. Тому одну ситуацію розрулюють різними патернами — залежно того, що тобі цінніше — підтримуваність коду, швидкість роботи, час відклику, використання пам’яті, швидкість старту приклади, надійність... Усе зразу добре не буває — щось стане краще, а щось — гірше. Усе має ціну. Питання, чим ти готовий жертвувати заради чого.

Тому одну ситуацію розрулюють різними патернами — залежно того, що тобі цінніше

Я коли читав патерн Декоратор, то здалося що це про інтегрування нового, коли ти не хочеш чи не можеш чіпати старе.

Я ще не вїхав в розділення патернів на Creational patterns, Structural patterns, Behavioral patterns. Як би не було, вчити їх буде корисним для мого росту як розробника? Типу як дивити на чужий досвід, при чому формалізований, а не те коли хтось нагівнокодив а ти вивчаєш те і віриш що то ок.

Я ще не вїхав в розділення патернів на Creational patterns, Structural patterns, Behavioral patterns.

Нафіг. Нема такого розділення. Помилка авторів, котрі були першопроходцями.

Як би не було, вчити їх буде корисним для мого росту як розробника? Типу як дивити на чужий досвід, при чому формалізований, а не те коли хтось нагівнокодив а ти вивчаєш те і віриш що то ок.

Читати корисно й потрібно, але не щоб вчити, а щоб зрозуміти.

Після Банди Чотирьох бажано підтягнути:
POSA dou.ua/forums/topic/28077
взаємодію в організації dou.ua/forums/topic/28756
Та книжки й статті Фаулєра martinfowler.com

Хм. Я GoF не читав. Всю інфу збираю по інету, плюс з досвіду. Напевно самий час взятися за книгу. Ще маю пробіли в C# і самому фреймврці Asp.Net Core. Думаю може вже раз виходить то обрати спеціалізацію в .Net Core стьоці, і там все добре вивчити. Потім можна чи через Апворк, як контрактор, чи на галеру. А то бути Software Engineer я не потягну, коли декілька мов і стьоків. Та й кому воно тут треба в Україні? Це фішка Долини лише, так?

Хм. Я GoF не читав.

И зря. полезная книжка для прочтения и понимания, но не для прямого юзания.

Не обязательно. Раскидай по либам и сделай отложенную загрузку или явную. Так писали проги с толпами плагинов еще 20 лет назад и они прекрасно работали.
С явной загрузкой пробегаешься по определенным путям, находишь либы там с интерфейсом плагинов и явно загружаешь. В результате клиенту достаточно отправить либу и он ее положит у нужное место на диске и она работает. Более того это элементарно сделать даже без выгрузки всего приложения. Просто или отслеживать изменение файлов по некоторому пути или периодически сканировать изменения файлов по этому пути.
В выгрузкой налету чуть сложнее, тут уже не помню, нужно RTFM.

Ну это уже слишком сложно. Не для тестового проекта.

Нет, подгружать либы это очень просто. Просканить парой функции пути и dlopen (линух) или LoadLibrary (винда). Понятно, что у всех плагинов одинаковый интерфейс и лучше extern «C».

Ну нафига? Потом добавят в папочку левый файл, и система навернется.

В Вордпресі хіба не так встановлюються плагіни?

Кучи разных видео и аудио плагинов именно так и делались. Но это разумно, если предполагается, что у тебя будет много разных реализаций одного интерфейса и более того их хочется подгружать по желанию (или по мере покупки).
Нужно-ли такое в твоем случае, я не знаю. И тем более на жабе.
Но это, говоря по современному один из самых массовых и отлаженных паттернов проектирования для определенного круга задач. Раньше так не называли, называли просто: организация системы (структуры) плагинов. Причем никто не мешал делать разные наборы плагинов с разными интерфейсами.
Такие плагины еще и тестировать легко.

Нужно-ли такое в твоем случае, я не знаю.

Ні. В мене лише одна ціль — щоб не чіпати клас в якому проходиться по цим обєктам, кожного разу додаючи новий обробник в колекцію. І я так вже робив декілька разів через рефлексію. Виходить прикольно. Ти написав новий клас по тому ж інтерфейсу, і все вже заатачилося до класу. І це не про рантайм оновлення, а після перезапуску заново зкомпільованого апу. Тобто профіт лише в тому що ти не чіпаєш один клас при розробці.

Нет, не навернется. Ты же не все подряд подгружать будешь, а потом интерфейс должен быть правильный, да и лоадер не всю муть подгрузит, а скажет, что муть ты пытаешься подгрузить.

Поки прочитав цей список патернів: refactoring.guru/design-patterns/catalog але ще не дуже дійшло до мене їх значення і використання. Думаю таке лише з реальним написанням продакшин коду засвоюється)

І до речі, власне, як часто ви особисто застосовуєте патерни? Можливо частіше власні винаходите і застосовуєте в проектах, базуючись на власному досвіді?

Читати варто оригінальну літературу (GoF, POSA) і вечорами. Коли хтось викладає патерни на сайті — найчастіше їх настільки скоротили, що фіг зрозумієш, про що воно.

Патерни не застосовуються по-одному. Чесно, вони взагалі не застосовуються. Патерни — то слова, котрими позначають певні підходи. От заходить хтось в ліс, бачить дерево. Але він не знає, що то зветься — дерево. Він його буде називати «купа палок». Та ж воно деревом від того не перестане буть.

Так само з патернами. Коли програмування розвинулось, чуваки почали помічати в коді різних проекті схожі шматки, і якось ці шматки поназивали. Знання цих термінів — патернів — концентрує те, до чого дійшло програмування за свою історію. Але ти не можеш використати один патерн — щоб перейти річку — треба «місток» або «лодка». Щоб зробить місток треба «дерево» та «мотузка». І коли усе це разом зліпити — перейдеш річку. А хтось не знає, як воно усе зветься, і все одно місток зробить.

Себто, коли маєш проблему, і знаєш патерни — більше шанс пригадати якесь більш-менш перевірене іншими рішення. Якщо не знаєш патернів — подумай, і щось придумаєш. Єдиного правильного рішення нема. Є 1-2 рішення на 5, кілька рішень на 4, багато на 3, а на 2 взагалі не проблема.

Ще — патерни не треба тулить усюди. Де воно треба буде — саме з’явиться. Кожен патерн — це ускладнення, певний хак щоб виплутатись з кепської ситуації. Якщо в тебе ситуація не кепська — не треба собі ускладнювати життя патернами.

Круто написав. дякую!

Я так і уявляв собі патерни. Причина чому я шукаю з готових — аби побачити якесь хороше рішення. А так я без проблем би й сам щось придумав. Тільки вирішив в процесі роботи чомусь навчитися правильному. То підскажи, я приблизно попав там в темі з патернами під моє завдання, чи ні? Якщо в темі незрозуміло описано, я можу спробувати перефразувати ще раз тут в коментарі.

Ніби, попав, але спочатку ООП, потім — патерни. Вичитування мейла й парсінг — незалежні задачі, мають робитись незалежними класами. Тому один шаблонний метод на двох — трохи не те.
А головне — щоб тобі самому було зручно.

Ще одна штука патернів — вони часто вводять just another layer of indirection. Це зручно, коли проект збирається жити роками — матимеш зайву ступінь гнучкості, коли замовник щось дивне викине, а тобі — це пилять. Часто десь між патернами можна код розрізать, втулить новий шматок, і зібрати назад. Якщо проект одноразовий — нема сенсу його робить надто правильно, хіба що — для себе.

Якщо проект одноразовий — нема сенсу його робить надто правильно, хіба що — для себе.

От для себе було б непогано. Щоб потім піти на +5 рейт)) Попередній мій клієнт, там був великий проект переписування цілої соціалочки тематичної. При чому переписування свого ж коду попереднього(ну і плюс індуського там було і ще трохи хтось писав). Ось там я погрався непогано, і здається доволі багато вивчив. Принаймні код став писати точно краще))

Вичитування мейла й парсінг — незалежні задачі, мають робитись незалежними класами.

Я про цю проблему знав, але не мав часу подумати як розділити. Бо естімейти занижені подавав.

Подписаться на комментарии