Интерфейсы и модульность
Запись от CoderHuligan размещена 30.01.2024 в 17:15
Показов 5017
Комментарии 62
|
Слово "интерфейс" происходит от двух слов - inter (между) и face (лицо), т. е. то что находится между лицами. Как взаимодействуют между собой к примеру люди? Посредством языка, т.е. того, что является интерфейсом между двумя и более, лицами. Без интерфейса взаимодействие невозможно. Даже язык жестов является таким же интерфейсом, как и любой другой язык. Сразу отмечу, что любой язык как интерфейс, является чисто внешней сущностью, которая вполне независима от лиц её использующих. Причем одно лицо может пользоваться разными интерфейсами (языками). Итак, любой интерфейс это язык (если он интерфейс). Причем не может быть так, что одно лицо имеет свой уникальный интерфейс, а другое свой. Тогда взаимообщение лиц попросту невозможно или было бы затруднено. Гораздо выгоднее иметь универсальный в рамках какой-то общности интерфейс. Во всяком случае к этому стоит стремиться. Если речь идет о программировании, тот тут мы часто имеем ввиду взаимодействие модулей/классов. Модули в языке Паскаль, по моему, есть чисто искусственное явление. Язык Дельфи это унаследовал. Язык Оберон, потомок Модулы, имеет загружаемые по требованию модули, и это уже большой шаг вперед. Но мы о другом. Классы в ООП, как и модули Паскаля, предоставляют свой интерфейс во внешний мир, делая доступными для него свои паблик поля, или через геттеры и сеттеры, что в общем одно и тоже: мы приоткрываем внутренности модуля/класса внешнему миру. Казалось бы: а как иначе? Как иначе строить взаимодействие между модулями/классами? Проблема тут одна: каждый отдельный модуль/класс предоставляет во внешний мир свой собственный интерфейс, а значит и язык, который обязаны знать все остальные. А я уже писал, что лучше иметь универсальный язык. Проблема возникает, когда мы хотим изменить модуль/класс. Допустим хотим добавить в паблик функцию еще один параметр. Если это сделать, то мы должны будем изменить все модули/классы, которые пользуются интерфейсом этого модуля/класса. Предоставляя внешнему миру даже свои паблик поля, мы связываем этот внешний мир с конкретным классом. Это всегда проблемы. Выходом из положения может быть создание особого языка (именно языка!), который бы разделяли все модули системы. Когда мы вызываем публичную функцию какого-то класса или пользуемся переменной, мы по сути становимся заложниками имен этих сущностей, их типов и их сигнатур. Если бы каждый модуль ничего не знал о других модулях, но просто посылал сообщение и параметры через некий общий для всех интерфейсный модуль, посредством особого универсального языка, то отпала бы необходимость в глобальных данных, что всегда проблемы, а также изменение одного модуля не влияло бы на другие, так как ни один модуль ничего не знает о других, кроме универсального языка, который все модули должны поддерживать. Тогда не потребуются и области имен. Повторяю: один язык лучше, чем много уникальных языков. Ну а реализацию данного механизма лучше поручить особому языку программирования, который бы скрывал детали реализации межмодульного взаимодействия, предоставляя универсальные механизмы по передаче/получении сообщений. Вот это и будет подлинное ООП.. |
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 62
Комментарии
-
хорошие, правильные мысли, добро пожаловать в Эрланг (ну или молодежный Эликсир), там взамодействие между потоками исполнения (это не потоки ОС) происходит через отправку сообщения. А что там за методы, есть ли там вообще кто на том конце и умеет ли обработать сообщение -не важно. Это позволяет добиться многих интересных вещей, в том числе можно менять любые сущности, все равно другие сущности о них не в курсе, можно перезапускать часть системы или обновлять не останавливая всей программы.Запись от Welemir1 размещена 30.01.2024 в 18:26
-
Запись от CoderHuligan размещена 30.01.2024 в 18:39
-
Это совсем не проблема. А наоборот. Это публичный контракт с окружающим миром. Который изменяется только при наличии весомых на то причин. И это правильно, так и должно быть. Давайте представим что это не так. Что из этого последует:
Вы сделали библиотеку/класс/функцию, а я ее использовал в своем проекте, вдруг вы изменили ее интерфейс, причем рассматриваем случай, как вы описали когда придется менять всю программу. Так мне и как сейчас и как у вас придется менять программу. Это ведь тогда когда, например метод имел два обязательных параметра, а стал принимать три обязательных параметра.
И тут вы какую универсальность не придумайте - ни чего толком не получится. Т.е. моя программа об этом не узнает, если я этого не сделаю.
При этом, чтобы обеспечить надежность своей программы, я должен буду абсолютно все методы вашей библиотеки проверять, обертывать дополнительными проверками и т.д и т.п. т.е., по сути, я все равно опишу "контракт" взаимодействия с вашим классом, только это будет многословно, замусоривать код.
В какой то мере можно сделать так и сейчас. На примере php делаете условно один публичный метод у класса, с определенным названием и.... а что дальше массив в качестве параметра, в котором будет складироваться все что нужно для внутренней работы? При этом я, как пользователь вашего класса, как узнаю чего он хочет? Т.е. тут же по бороде пойдут все удобства соверменных ИДЕ, которые подсказывают, что лажу передал в параметры. Да и код уже не прочитаешь, надо будет смотреть в документацию по вашему классу конкретной версии, и сверять все ли так.
Если вы добавляете, не изменяя прочую логику, то и не нужно ни чего менять в других классах, это какая то новая фишка вашего класса, которая в моем проекте не нужна была. Если она появилась и она нужна, то я в любом случае, должен реализовать ее использование. При чем тут очень серьезный вопрос к вам, как к разработчику класса: а правильно ли вы сделали, что добавили в этот класс. Может нужно было создать новый?
Ну, а если с добавлением нового метода вы изменили поведение других публичных методов то:
1. Если это библиотека, вы должны сменить мажорную версию, как сигнал, что обратная совместимость может быть нарушена
2. Если это внутри проекта, надо которым работает большая команда - вас лучше санной тряпкой гнать из этого проекта
(если, конечно, это не плановое изменение)
[QUOTE]Если бы каждый модуль ничего не знал о других модулях, но просто посылал сообщение и параметры через некий общий для всех интерфейсный модуль, посредством особого универсального языка, то отпала бы необходимость в глобальных данных, что всегда проблемы, а также изменение одного модуля не влияло бы на другие, так как ни один модуль ничего не знает о других, кроме универсального языка, который все модули должны поддерживать. Тогда не потребуются и области имен.[QUOTE]
Ну такое делают, просто этот универсальный язык, он универсален в рамках конкретного проекта, модуля и т.п. иначе ни как. И это тоже правильно. Представьте мы делаем систему управления атомным реактором , а какой то идиот подсунет, 100500 модулей из разных сфер... Мы должны будем в слушателях реализовывать эвристический анализ, парсинг и еще 100500 телодвижений. При этом мы один фиг условиями и проверками опишем "контракт".
Это будет лютый треш. Который читая код не поймешь. Надо будет обкладываться тонной документации.
Схематично поясните как вам видится как я могу указать какие сообщения я жду не описывая так или иначе нужных мне данных?Запись от voral размещена 30.01.2024 в 23:26
-
Но все равно же не каким то магическим способом обработчик выбирает какое сообщение обрабатывать, а какое нет? Опять же, допустим договорились "что я жду сообщение А"... Вася отправляет в этом сообщении один набор данных, Петя вообще какой то упакованный в хеш, совсем другой набор данных.... И как быть? Будем с ними созваниваться?
Сообщение от Welemir1
Шина данных это круто, но она не отменяет интерфейсов.Запись от voral размещена 30.01.2024 в 23:29
-
ага, типа пароход это ходячий пар)
а раз пар водяной то он может ходить по воде))
CoderHuligan,
ты не с того начал)
нуно начинать с того, а зачем тя, нафик, понадобились сии интерфейсы и модули?
тогда все эти гетеры, сетеры и т.п. будут шулупень, про которые и упоминать - время тратить.
например, раз ты любишь Си, интерфейсом у тя мог бы быть отдельный хедер,
а всё остальное, для пущей немозолистости глаз, можно было бы упаковать в бинарный либ.
вот, вот.
Сообщение от Welemir1
непонимание того а зачем вам, в вашем личном кодинге, юзать интерфейсы и модули
и уводит фокус внимание куда то на сторону, в вашем случае, к постороннему япу ...Запись от XLAT размещена 31.01.2024 в 09:14
-
Существующие решения не устраивают. Хотя можно эмулировать уже сейчас. Просто получится много стороннего кода. А так чисто академический, так скажем, интерес.
Сообщение от XLAT
Это про эмуляцию, а я с прицелом на новый язык. И не хедер там должен быть, а оператор, который принимает сообщение.
Сообщение от XLAT
Да я люблю Си. Люблю ради его простоты и возможностей. Сейчас читаю Страуструпа, и ты знаешь, начал проникаться. Короче, я с ним согласен во многом. Зауважал даже. Думаю, что многие много потеряли, если не читали этого. Конечно я не в особом восторге, но иначе, при нынешних технологиях, и невозможно. Поэтому C++ знать необходимо. Даже просто потому, что это - целая эпоха.. Ну и писать на простом голом си сейчас просто невыгодно.
В идеале модуль не должен знать даже имени другого модуля. Он знает только язык(протокол взаимодействия). Он посылает сообщение как бы в пустоту, без адреса. Адрес знает только интерфейсный модуль. Он получает сообщение. Сообщение завязано на таблицу, в которой прописаны методы отдельных обьектов в каждом модуле, которые ответственны за обработку каждого конкретного сообщения. И не надо смешивать понятие модуля и класса. Модуль может содержать несколько классов, и его нельзя "размножить". Это просто контейнер для классов. Ну это и существенно другой уровень абстракции. Это не для маленьких проектов. По сути это является декларативным программированием на уровне модулей. Мы говорим модулю ответственным за работу с файлами, допустим, не открыть файл, а посылаем сообщение с просьбой получить строку из файла. Если нам не нужно закрывать файл, то мы открываем транзакцию, а потом закрываем. Мы не работаем с файловыми указателями. Даже сейчас это можно эмулировать. Каждый модуль имеет главную функцию, которая принимает несколько параметров. Почти как функция main. Интерфейсный модуль вызывает эту функцию и передает ей сообщение в виде параметров. main принимает их и распределяет по отдельным своим компонентам. каким - это знает только она. Ну это же действительно удобно и в winapi сообщения устроены примерно так. Мы общаемся с системой на уровне интерфейсного языка сообщений. А система гарантирует их уникальность. Повторяю: не нужны тогда никакие области имен, которые загромождают листинги программ. Код становится чище и понятнее. Для человеческого мышления гораздо легче понять систему, которая основана на понятных интерфейсах.
Сообщение от voral
Запись от CoderHuligan размещена 31.01.2024 в 12:46
-
Ну предположим такой синтаксис:
Сообщение от voral
$EtoSoobschenie(EtoEgoParametry);
Доллар позволяет отделить посылку сообщения от вызова обычной функции.
pointerToBuffer = $GetFile((char*)"myfile.txt", pointerToBuffer);
Допустим хотим качнуть файл в память. Посылаем сообщение "в пустоту". То есть мы не знаем кому. Мы просто хотим файл. нам побоку кто его обработает и пошлет нам файл. Это сообщение "поймает" особый системный модуль. По таблице определит какой модуль вызвать в зависимости от id сообщения, и вызовет его. А тот в свою очередь определит конкретную операцию и возвратит результат системному модулю, а тот в тот модуль, который послал сообщение. Можно работать и асинхронно привязав коллбак функцию к сообщению.Запись от CoderHuligan размещена 31.01.2024 в 13:07
-
Так сейчас при правильной архитектуре можно в любом ЯП организовать. Именно для этого и существуют интерфейсы. И, по большому счету, даже не важно о модулях мы говорим или о классах. Т.е. есть соглашения оформленные в соответствии с возможностями конкретного ЯП. И вы можете "состыковывать" любые модули, которые поддерживают соответствующий интерфейс. При этом сразу обеспечивается то ,что мы подключим именно нужный модуль, а не неведомую хрень, которая случайно нам сломает все. (в общем то если сломает это в лучшем случае, хуже если она начнет что то делать не падая, но не то).
Сообщение от CoderHuligan
Первое, что пришло в голову в качестве примера. Это PSR т.е. это некий стандарт описанный интерфейсами (т.е. без реализации). Если придерживаться их, то пожалуйста можете вести разработку даже не задумываясь как будет реализован тот или иной функционал. Опять же не важно меж модулей или меж классов вы обдумываете связь. Вот вам и "не должен знать имя другого модуля". Например нужен мне в программе логгер, у меня есть публичные правила работы. Я указываю
Все я могу даже тесты написать проверяющие работу моего кода, и даже не задумываться, какой логгер подключат пользователи моего приложения/модуля/классаPHP 1 2 3 4 5
class Application{ public function __construct( private readonly Psr\Log\LoggerInterface $logger) { } }
Ну это типовое и широко применяемое решение.
Сообщение от CoderHuligan
Совсем не избавляющее нас от необходимости создавать интерфейсы.
Вот, кстати, из упомянутого выше PSR14 как раз на эту тему.
И что это меняет? Вы отправляете сообщение для примера 'getMeString', и к нему как то упакуете три переменных
Сообщение от CoderHuligan
А я отправлю просто текстом "дай мне 12ую строку из файла input.dat и положи ее в переменную моей области видимости x"Code 1 2 3
fileName = input.dat numLine = 12 value = ... ссылка на переменную куда положить результат
Теперь давайте думать. Схематично как это будет разгребаться если у нас нет ни каких вообще правил взаимодействия? Все классы в приложении будут слушать некую шину сообщений и каждое из них парсить? Уверены, что так не возникнет ложных срабатываний?
Более того:
1. Если мы придумаем некий формальный язык, и будем действовать через него. То это будет те же самые правила работы, контракты - те. интерфейсы. Условно говоря PSR, что я приводил выше - можно считать таким "языком"
2 Если мы будем, что как в примере выше можно отправить все что угодно. Мы будем обязаны в коде реализовать по сути "интерфейс". Он будет анализировать все пакеты и запускать функционал данного класса, при выполнении определенных условий. Т.е. по сути это будет скрытый интерфейс, который ИДЕ уже не подскажет. При этом еще и минус - будет усложнена валидация. Как отличить этот пакет не нам или у него ошибка в данных?
Это можно реализовать на любом ЯП уже сейчас. Сделайте так проект да и все.
Сообщение от CoderHuligan
Я ОЧЕНЬ сомневаюсь , что код станет понятнее. Скорее наоборот (причины я частично выше приводил)
Сообщение от CoderHuligan
Запись от voral размещена 31.01.2024 в 13:19
-
Короче: каждому инструменту свое место. То, что вы описываете, как я понял, это используется, и достаточно обычный подход. Единственное это не замена публичным интерфейсам. Ну и не пихается это решение везде, "чтоб было".Запись от voral размещена 31.01.2024 в 13:24
-
Понятие "сообщение" можно представить в виде события. Получаем событийную систему. Понятие состояния и события это фундаментальные понятия в программировании. Для модуля, который посылает сообщение - это есть посылка сообщения. А для принимающего модуля это сообщение уже будет событием. Получаем событийную систему основанную на посылках сообщений.Запись от CoderHuligan размещена 31.01.2024 в 13:26
-
офигеть ты вырос!
Сообщение от CoderHuligan
прямо на моих глазах рос)
ну, я так и прогнозировал, что рост неизбежен,
только вопрос был, а хватит ли отведенного Им времени на энтот рост)
нигде нету столько практического смысла, как в академическом интересе,
ибо выбор правильного набора аксиом есть определяющий вектор на качество будущего реза.
помню, была у меня беседа на другом форуме с челом, который кодил какую-то свою фикню,
в которой объекты ваще друг о друге ничего не знали, ни имени, ни протоколов, ваще ничо,
да и находились они(его объекты) удалённо - на разных планетах...
однако, допустим, что даже протоколы взаимодействия не известны,
то всё равно понадобится общий протокол(о котором все оповещены),
для поиска и построения такого протокола взаимодействия.
иначе, нечего тут будет кодить)
а далее всё улетает в траблы с производительностью, отладкой(реал-тайм ошибки с невозможностью их адекватной обработкой) и тп..Запись от XLAT размещена 31.01.2024 в 13:34
-
Запись от CoderHuligan размещена 31.01.2024 в 13:35
-
А в чем тут костыльность? Если вам они не понятны, может без них - просто свалите все в одну кучу, ну или автозагрузку классов переопределите)
Сообщение от CoderHuligan
Совершенно не понятно в чем их жутковатость.
А для чего? Не нравится делаете свою автозагрузку и все.
Сообщение от CoderHuligan
А главное, хотя бы примерно накидайте алгоритм, который будет разруливать тот пример что я описал выше с получением строкиЗапись от voral размещена 31.01.2024 в 13:40
-
Запись от CoderHuligan размещена 31.01.2024 в 13:42
-
обычно, тут приводят пример с социальной сетью,
где есть 1 бубл-модуль, который сервер,
и лям однотипных модулей-клиентов.
для клиентов у вас всё верно,
а для сервера уже нет.
сорри за бальность.
или пусть это будут 10 камер на шарнирах, которые записывают обстановку вокруг вашего бункера на один-единственный винчестер.Запись от XLAT размещена 31.01.2024 в 13:50
-
Да просто создать сообщение, которое задает абстрактное описание требуемой операции. В данном случае: $ПолучитьСтрокуИзФайлаПоНомеру("input.da t", 12, &x, BINARY); Функция принимает любое количество параметров. Или заранее задаем протокол для обмена параметрами: это чисто технические детали реализации. Главное идея.Запись от CoderHuligan размещена 31.01.2024 в 13:51
-
Вот. и чем это отличается например от PSR? Или от того, что вы хотите убрать из языков?
Сообщение от CoderHuligan
Т.е. вы описали все тот же самый интерфейс только другим образом.Запись от voral размещена 31.01.2024 в 13:55
-
Запись от XLAT размещена 31.01.2024 в 13:59
-
Как это сообщение будет обрабатываться в получателе и в менеджере сообщений. Т.е. как по вашему будетприниматься решение, что это сообщение именно этому классу, и какой параметр куда поставить?
Вот функция на PHP которая выполняет задачу
теперь давайте как будет выглядеть в коде вызовPHP 1 2
function getStringFromFileByNumber(string $fileNabe, int $line, string &value, FileType $type):void{ }
B что изменилось?PHP 1 2 3 4 5 6
// так сейчас getStringFromFileByNumber("input.dat", 12, &x, BINARY); // как то так предлагаете вы emit(getStringFromFileByNumber("input.dat", 12, &x, BINARY)); // ну либо мы организуем синтаксический сахар и это будет выглядеть так $getStringFromFileByNumber("input.dat", 12, &x, BINARY);
Запись от voral размещена 31.01.2024 в 14:01
-
Нет. Так как сообщение уникально и привязано к конкретному модулю, который его объявил, то получатель данного сообщения всегда этот конкретный модуль. Причем можно сделать минимизацию накладных расходов применив таблицу указателей на методы которые индексируется сообщением - по сути уникальным числом. Другой модуль не может переобъявить сообщение раз оно уже кем-то было создано и привязано.
Сообщение гарантировано найдет своего адресата. А если нам нужно заменить или изменить модуль, то все остается в силе если придерживаться соглашений.
Про то как я написал выше. Что касается параметров, то каждый метод класса в модуле или функция сообщает свою сигнатуру системному модулю в виде некой структуры данных, следуя которой системный модуль может разобраться с порядком и количеством параметров.
Очень многое. Вам же всё равно нужно будет получить доступ к конкретному методу объекта через точку. Еще хуже когда классы имеют свою область имен. Понимаете, здесь мы оперируем только декларацией того, что мы хотим получить от кода. Это самый высокий уровень. Вот эта декларация и есть язык, каждое слово которого является сообщением..Запись от CoderHuligan размещена 31.01.2024 в 17:45

(если, конечно, это не плановое изменение)
