ООП - объектно-ориентированое программирование

Общие принципы, проектирование, модуляризация, темплейты и шаблоны
Аватара пользователя
Vitekkz88

Activity Silver Автор
expert
expert
Сообщения: 1100
Зарегистрирован: 21 янв 2014, 15:45
Награды: 3
Версия LabVIEW: 12,13,14
Откуда: Томск
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Vitekkz88 »

jane_wild, еще какой реальный и довольно понятный.
Не бывает отдельного ООП для LV, отдельного ООП для Java, отдельного ООП для C++. Модель везде одинаковая, просто представление отличается и некоторые фишки. Чем раньше это поймете, тем лучше.
Зайти на ООП через практику - это как влазить в дом через дымоход. После того, как поймете теорию(именно поймёте) - попробуйте собрать этот пример самостоятельно, тем более для этого есть всё :D
За ссылку - пожалуйста...лишь бы просмотр не был ради просмотра, как с чтением :cry:
Инженер - это открыто светящийся интеллект, свободный и не обидный юмор, это легкость и широта мысли...Это воспитанность, тонкость вкусов, хорошая речь, плавно согласованная и без сорных словечек...
-А. И. Солженицын
Аватара пользователя
Kosist

Activity Gold
expert
expert
Сообщения: 1236
Зарегистрирован: 21 фев 2011, 23:44
Награды: 2
Версия LabVIEW: 2013-2020
Благодарил (а): 23 раза
Поблагодарили: 30 раз
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Kosist »

jane_wild писал(а):Если дочерние классы содержат свои данные A, B, C, тогда зачем они в базовом классе?
"Физически" дочерние классы не содержат поля А, В, С. Их содержит только базовый класс. Под "физическим" содержанием я имею ввиду эти поля в тайп-деф котроле класса.
Дочерние классы таким образом лишь имеют доступ к этим полям, и таки образом, могут хранить/читать данные "в себе", в своем контексте. Но, если базовый класс не будет иметь read/write методы (геттеры, и сеттеры), то тогда и дочерние классы не могут менять/читать напрямую эти поля.
Мы делили апельсин - много наших полегло...
Аватара пользователя
jane_wild
master
master
Сообщения: 459
Зарегистрирован: 30 июн 2016, 02:11
Версия LabVIEW: 2020
Благодарил (а): 83 раза
Поблагодарили: 15 раз
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение jane_wild »

"Физически" дочерние классы не содержат поля А, В, С. Их содержит только базовый класс.
Вот это уже более логично, но тогда возникает вопрос: как Vitekkz88 умудряется записывать/считывать если эти самые данные имеют разный тип у дочерних классов? В одном булевый тип, а в другом double. Поэтому я и сделала вывод, что данные у дочерних классов свои собственные, тогда зачем A, B, C в базовом. Кроме того базовый класс не имеет к ним доступа и как следствие никому его не предоставляет...
Аватара пользователя
Kosist

Activity Gold
expert
expert
Сообщения: 1236
Зарегистрирован: 21 фев 2011, 23:44
Награды: 2
Версия LabVIEW: 2013-2020
Благодарил (а): 23 раза
Поблагодарили: 30 раз
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Kosist »

jane_wild писал(а):Вот это уже более логично, но тогда возникает вопрос: как Vitekkz88 умудряется записывать/считывать если эти самые данные имеют разный тип у дочерних классов? В одном булевый тип, а в другом double. Поэтому я и сделала вывод, что данные у дочерних классов свои собственные, тогда зачем A, B, C в базовом. Кроме того базовый класс не имеет к ним доступа и как следствие никому его не предоставляет...
Извиняюсь, этот момент я проглядел... Я просмотрел лишь скрины, а сам проект не открывал. Действительно, в таком случае каждый дочерний класс имеет свои поля, и базовый класс в них, по сути, не нуждается; и они там лишние. Если бы дочерние классы имплементрировали операции с числами, то тогда они могли бы использовать снова родительский класс и его поля данных.
Смотрите, все просто. В идеале, в родительские классы будут иметь поля, которые могут/или же нет использовать дочерние классы. Такую возможность тогда предоставляют геттеры и сеттеры. И дочерним классам нужно давать доступ лишь к тем полям, которые они действительно будут использовать - ведь давая доступ ко всем полям, Вы можете изменить логику работы родительского класса по ошибке, а это будет уже проблемой.
Мы делили апельсин - много наших полегло...
Аватара пользователя
Vitekkz88

Activity Silver Автор
expert
expert
Сообщения: 1100
Зарегистрирован: 21 янв 2014, 15:45
Награды: 3
Версия LabVIEW: 12,13,14
Откуда: Томск
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Vitekkz88 »

Kosist, выше я уже дал ответ на этот вопрос:
jane_wild: Если дочерние классы содержат свои данные A, B, C, тогда зачем они в базовом классе?
Vitekkz88: Потому что класс описывает объект, у которого есть атрибуты и методы. Абстрактный класс может содержать, а может и не содержать полей и методов.

Kosist: Действительно, в таком случае каждый дочерний класс имеет свои поля, и базовый класс в них, по сути, не нуждается; и они там лишние
Они там как минимум не вносят логического диссонанса.
Kosist: Физически" дочерние классы не содержат поля А, В, С.
jane_wild:Вот это уже более логично...В одном булевый тип, а в другом double.
Вот в этом и заключается проблема непонимания: во-первых, наследование не обязывает использовать всё, что определенно в родительском классе(хотя это уже ближе к композиции). Иными словами, вы можете описать абстрактный объект и не использовать его поля. Да и методы можете не использовать, смотря как проектируете :D
Я описал его(абстрактный объект) в абстрактном классе. Во-вторых - я эти поля мог бы использовать для обычного калькулятора, но я этой цели не преследовал.
В примере мы наследуем от родительского класса, но используем только методы. Поля я определил в дочерних классах самостоятельно с нужным мне типом данных. Смысловая нагрузка атрибутов одинаковая, только представление разное. Поэтому в абстрактном классе даётся общее описание, а в дочерних классах уже конкретное.

Kosist:В идеале, в родительские классы будут иметь поля, которые могут/или же нет использовать дочерние классы. Такую возможность тогда предоставляют геттеры и сеттеры. И дочерним классам нужно давать доступ лишь к тем полям, которые они действительно будут использовать - ведь давая доступ ко всем полям, Вы можете изменить логику работы родительского класса по ошибке
Верно, только set-ер нужно делать приватным, а get-ер - на своё усмотрение(как правило публичным). В этом случае никто данные шатать не будет кроме самого класса и инкапсуляция сохранится.

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

Activity Gold
expert
expert
Сообщения: 1236
Зарегистрирован: 21 фев 2011, 23:44
Награды: 2
Версия LabVIEW: 2013-2020
Благодарил (а): 23 раза
Поблагодарили: 30 раз
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Kosist »

Vitekkz88 писал(а):Верно, только set-ер нужно делать приватным, а get-ер - на своё усмотрение(как правило публичным). В этом случае никто данные шатать не будет кроме самого класса и инкапсуляция сохранится.
Тут я не совсем согласен. Если сеттер (родительского класса) будет private, то его может использовать лишь родительский класс; а дочерние - нет. Тогда метод-сеттер будет лишним, т.к. в родительском классе поле можно достать и без метода, а прямо "вытащить" его, как из кластера.
Другое дело - сделать его protected. Тогда да, дочерние классы могут его использовать, а все остальные, все контекста этих классов - нет.
Мы делили апельсин - много наших полегло...
Аватара пользователя
Vitekkz88

Activity Silver Автор
expert
expert
Сообщения: 1100
Зарегистрирован: 21 янв 2014, 15:45
Награды: 3
Версия LabVIEW: 12,13,14
Откуда: Томск
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Vitekkz88 »

Сеттер делают приватным, а если кому-то нужен доступ и об этом просят - открывают(чаще protected, ага...а иногда прям public, тоже от степени необходимости и осторожно). Закрыт для того, чтоб инкапсуляцию не потерять. Детей может быть много, потом ищи-свищи кто гадит. Вы можете не хотеть давать права на изменение тех или иных полей, поэтому должны их защищать от записи. Сеттер это позволяет сделать в явном виде и избыточным он не будет. Или всё ещё будет? :crazy: С геттером наоборот - всегда доступен, чтоб читать могли.
Инженер - это открыто светящийся интеллект, свободный и не обидный юмор, это легкость и широта мысли...Это воспитанность, тонкость вкусов, хорошая речь, плавно согласованная и без сорных словечек...
-А. И. Солженицын
Аватара пользователя
Kosist

Activity Gold
expert
expert
Сообщения: 1236
Зарегистрирован: 21 фев 2011, 23:44
Награды: 2
Версия LabVIEW: 2013-2020
Благодарил (а): 23 раза
Поблагодарили: 30 раз
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Kosist »

Vitekkz88 писал(а): Сеттер это позволяет сделать в явном виде и избыточным он не будет. Или всё ещё будет? :crazy:
Конечно будет лишним. Зачем создавать виайку, которая будет делать то, что можно сделать и без нее? Плюс по умолчанию, accessors создаются с public scope, т.е. нужно вручную менять на private - лишние телодвижения :crazy:
Ну а так - дело вкуса, конечно. Во всем есть плюсы, и минусы. С одной стороны - лишняя виайка. Но с другой - есть один удобный плюс - легко можно найти, где изменяется то или иное поле внутри класса, т.к. можно через древо проекта найти всех callers. Нужно над этим задуматься тогда... :think:
Мы делили апельсин - много наших полегло...
Artem.spb

Activity Автор
professor
professor
Сообщения: 3392
Зарегистрирован: 31 июл 2011, 23:05
Награды: 2
Версия LabVIEW: 12-18
Благодарил (а): 49 раз
Поблагодарили: 172 раза
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Artem.spb »

Kosist писал(а):
Vitekkz88 писал(а): Сеттер это позволяет сделать в явном виде и избыточным он не будет. Или всё ещё будет? :crazy:
Конечно будет лишним. Зачем создавать виайку, которая будет делать то, что можно сделать и без нее?
Эдак можно скатиться до маразма: зачем SubVI, когда можно обойтись без них.
Аватара пользователя
Kosist

Activity Gold
expert
expert
Сообщения: 1236
Зарегистрирован: 21 фев 2011, 23:44
Награды: 2
Версия LabVIEW: 2013-2020
Благодарил (а): 23 раза
Поблагодарили: 30 раз
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Kosist »

Artem.spb писал(а):Эдак можно скатиться до маразма: зачем SubVI, когда можно обойтись без них.
Со всем уважением, но сравнение не корректное.
SubVI нужны для создания модульного кода, который можно использовать повторно и не повторять один и тот же код во многих местах; и обычно (но не всегда) сабвиайки содержат внутри больше, чем одну функцию. А сеттер/геттер - ни что иное, как bundle by name/unbundle by name функция, ну и кейс-структура на обработку ошибок (да и то, не всегда). Для них даже не нужно юнит тесты делать, ведь там нет (в идеале) дополнительного функционала.
Вы же в сабвиайках обычных не делаете для каждого элемента кластера отдельный сеттер/геттер? Ведь параметр можно достать прямо из кластера. Так вот - зачем для класса делать accessor, который будет использовать только в этом классе? Если у меня 30 полей в классе, то это может быть до 60 дополнительных виаек в проекте. А зачем? Читабельность кода это не улучшит.
Даже в текстовых языках геттеры/сеттеры объявляются лишь для тех полей, которые должны быть доступны вне этого класса. А те, которые используются лишь внутри этого класса - их можно считать/изменить напрямую, ведь это обычная переменання с ограниченым scope.
Я понимаю, зачем Вы так делаете - с точки зрения общего овервью проекта это имеет смысл; Вы видите сразу какие параметры читаются, какие пишутся - а уже за тем можете менять их тип, чтобы другие классы могли "достучаться" до этих данных. Но с другой стороны, это лишние методы, которые другого разработчика, не привыкшего к такому стилю, могут запутать.
Я ведь не говорю, что это плохо так делать - просто как по мне, это лишняя работа; а выгоды не так уж и много.
Мы делили апельсин - много наших полегло...
Аватара пользователя
Vitekkz88

Activity Silver Автор
expert
expert
Сообщения: 1100
Зарегистрирован: 21 янв 2014, 15:45
Награды: 3
Версия LabVIEW: 12,13,14
Откуда: Томск
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Vitekkz88 »

Да никто и не говорит, что Вы говорите, что это плохо :crazy:
Kosist писал(а):, Если у меня 30 полей в классе, то это может быть до 60 дополнительных виаек в проекте. А зачем? Читабельность кода это не улучшит. Даже в текстовых языках геттеры/сеттеры объявляются лишь для тех полей, которые должны быть доступны вне этого класса.
А что Вас удивляет? Если у Вас такой объект и все поля могут требовать доступа, то будет +60 доп.методов. С читабельностью ничего не случится, просто дерево проекта раздуется. Работать с ООП и волноваться по поводу большого дерева проекта не стоит. Разумеется, не стоит создавать пару гет/сет для полей, которые используются только для внутренних нужд класса. Вы всё верно говорите, но в Вас борются два мнения между собой :D Мол, этого делать не нужно, а с другой стороны -можно и делать, как хотите в общем.
Чем меньше полей и методов будет использовано для полного описания объекта - тем качественнее будет сам класс. А если класс разрабатывают с прицелом на будущее - то очень удобно иметь уже реализованные методы доступа заменяя ключевое слова модификатора(private, public, protected).

Kosist писал(а): Это лишние методы, которые другого разработчика, не привыкшего к такому стилю, могут запутать. Я ведь не говорю, что это плохо так делать - просто как по мне, это лишняя работа; а выгоды не так уж и много.
Ни одного разработчика в текстовых языках это не запутает, да и LV-разраб не запутается, если есть опыт и понимание. Стиль может показаться непривычным, эт верно. Вообще, чтобы втянуть нового разработчика в проект - с ним(с разработчиком) проводится беседа и рассказывается о философии разработки ПО, которой придерживается компания или команда. Во всех компаниях есть свои фишки, которые будут непривычным первое время, но в целом все компании занимаются одним и тем же программированием.
Инженер - это открыто светящийся интеллект, свободный и не обидный юмор, это легкость и широта мысли...Это воспитанность, тонкость вкусов, хорошая речь, плавно согласованная и без сорных словечек...
-А. И. Солженицын
Аватара пользователя
Vitekkz88

Activity Silver Автор
expert
expert
Сообщения: 1100
Зарегистрирован: 21 янв 2014, 15:45
Награды: 3
Версия LabVIEW: 12,13,14
Откуда: Томск
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Vitekkz88 »

Часть 3. LVOOP
LabVIEW OOP не поддерживает множественное наследование - это означает, что дети могут наследовать поля и методы только от одного родителя. Что делать, если требуется обратиться к методу класса, который не связан общим родителем?

В языке программирования, который поддерживает множественное наследование было бы проще в решении этой проблемы: наследуем и всё. Увы и ах, но наследование не бывает частичным, поэтому бонусом будут получены поля и методы от всякого. При разработке приложения это приведёт к приличному бардаку и путанице в проекте.
С другой стороны, есть языки, которые не поддерживают множественное наследование, например Java или C#. Однако, в этих языках программирования используются механизм интерфейсов. Интерфейс определяет контракт доступа к какому-то объекту. Важно понимать, что интерфейс не реализует сам метод, он предоставляет лишь доступ к нему. Одним и тем же интерфейсом могут пользоваться множество объектов, но реализация методов, которые содержит интерфейс, будет определена либо в каждом классе на свой манер, либо будет использоваться реализация метода по умолчанию из того класса, который предоставил этот интерфейс. Как раз то, что нужно, чтобы добраться до какого-то метода вне родственных классов.

В LabVIEW нет ни интерфейсов, ни множественного наследования и возникает вопрос: чего делать? я хочу конкретный метод(вероятно с конкретной реализацией) вооон того класса, который не от кого не зависит. Придётся выкручиваться :crazy: мы можем создать отдельный класс с реализацией нужного метода и наследоваться от него(ку-ку, дублирование кода), либо напрямую обратиться к объекту нужного класса для вызова метода(инкапсуляция, прости). И если в тексте это выражается в двух строчках быдло-кода(именно так), то в LabVIEW это будет выглядеть вот так:
Вызов_метода.png
Вот пожалуйста: у нас в методе класса Test создаётся объект класса ClassCalculator и вызывается его метод add. Поля мы берём из класса Test, вот пожалуйста – открыли доступ к set-еру класса ClassCalculator. Ну как?... Оно? Начал искать информацию про «интерфейсы в LabVIEW», нашел темы на Lava.org...Так там этой проблеме посвящены десятки тем с демонстрациями изворотливости на всякий вкус, посмеялся. В общем - это первая фишка, которая будет троллить LVOOP-шника. :D Второй фишкой будет отсутствие конструктора. Зачем в ООП-языках конструктор используют? Правильно, чтобы инициализировать поля некоторыми первоначальными значениями. Я наивно полагал, что делаю тоже самое, когда открываю кластер с полями объекта и устанавливаю в них какие-то значения. Ничего подобного: как были нули – так и остаются до тех пор, пока в явном виде не вызовешь сеттер для заполнения полей. Или нужно где-то галочку поставить? Еще одна фишка, которой пользуются эффективно и изящно – вложенные классы(nested class), но в LVOOP этого нет. Конечно же есть там какие-то варианты сделать нечто подобное – но это так же как и с интерфейсом. Мол, вложенные классы не часто используются( это я прочитал на одном из форумов :D ), поэтому и не нужны они, а если очень надо – вот вам кусочек как это можно обойти-заменить. А мне бы эта фишка пригодилась для того, чтобы не создавать отдельный дубль-класс с минимальными изменениями или объединить мелкие классы вместе внутри класса.
Так что в LabVIEW придётся более детально прорабатывать архитектуру проекта и сетовать на недостаточный уровень абстракции (хотя бы из-за отсутствия интерфейсов). Либо вообще изолировать классы друг от друга, используя их как кирпичи для построения финального приложения, в этом есть определенное здравое зерно. Возможно, в 2018 версии что-то изменилось, а то я в 2014-ой работаю.
Еще один пример напоследок как не надо делать: определили класс «прямоугольник» и класс «монитор».
Это два абсолютно не связанных логически между собой класса. Тем не менее, мы хотим наследовать реализованный метод из прямоугольника, чтобы нарисовать монитор прямоугольной формы. Берём и наследуем, да? На этом моменте мурашки пробегают и становится не по себе…особенно, если метод «рисовать» был унаследован от абстрактного класса «Геометрические фигуры». Поэтому, будьте внимательны и осторожны, дабы не ошибиться с проектированием домика. Не жалейте времени на качественный чертёж, тогда ваш объект прослужит верой и правдой долгие годы.
Трёх китов LVOOP вытягивает, уже хорошо. Дальше буду обозревать конкретные шаблоны(паттерны) проектирования: основные, порождающие, структурные, поведенческие. Вообще в википедии я насчитал около 60 паттернов, в хитовой книге Эрика и Элизабет Фримен приведены 12(может побольше, надо пролистать), в книге «Приёмы объектно-ориентированного проектирования» под авторством Гамм, Хелм, Джонсон, Влиссидес – 23. Посмотрю, сравню с тем, что уже приводится из готового для LabVIEW, сопоставлю с информацией из книг и что-нибудь разберу.
Многа буков, кто дочитал - тот молодец :super:
Полезная ссылка: https://forums.ni.com/t5/LabVIEW-Develo ... -p/3523820
Инженер - это открыто светящийся интеллект, свободный и не обидный юмор, это легкость и широта мысли...Это воспитанность, тонкость вкусов, хорошая речь, плавно согласованная и без сорных словечек...
-А. И. Солженицын
Аватара пользователя
Juri
I/O
I/O
Сообщения: 263
Зарегистрирован: 19 апр 2017, 23:06
Версия LabVIEW: 2021
Благодарил (а): 13 раз
Поблагодарили: 6 раз

Re: ООП - объектно-ориентированое программирование

Сообщение Juri »

Лабвьюшные классы заставляют меня грустить. Все хорошо, пока нет необходимости их редактировать. Удалил пару лишних типов из базового контрола и вся прога сломалась. На блок диаграмме нет никаких ошибок, но в листе ошибок 77 забракованных vi. Открываю любую из них, в блок-диаграмме все чисто, но в листе ошибок написано "owning library has blocked execution of the VI". Иногда помогает пересохранить базовый тип данных, иногда нет. Где искать проблему не понятно.
Аватара пользователя
Kosist

Activity Gold
expert
expert
Сообщения: 1236
Зарегистрирован: 21 фев 2011, 23:44
Награды: 2
Версия LabVIEW: 2013-2020
Благодарил (а): 23 раза
Поблагодарили: 30 раз
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Kosist »

Vitekkz88 писал(а):LabVIEW OOP не поддерживает множественное наследование - это означает, что дети могут наследовать поля и методы только от одного родителя. Что делать, если требуется обратиться к методу класса, который не связан общим родителем?
Для этого можна использовать вот этот подход - https://forums.ni.com/t5/LabVIEW-Archit ... -p/3804592, мутацию классов. А по-сути - расширение функционала by composition (не знаю, как правильно перевести - при помощи композиции?).
Ну, и нужно не забывать, что начиная с LabVIEW 2017 SP1 (именно в сервис паке это уже работает), malleable VIs стали поддерживать адаптацию классов - т.е. это сейчас уже наиболее близко к интерфейсам. Можно создать класс-интерфейс, определить методы, и вызывать их из других классов, которые находятся в отдельной от него иерархии. Уже есть несколько постов по этому поводу, и примеры в самом LabVIEW (C:\Program Files (x86)\National Instruments\LabVIEW 2017\examples\Malleable VIs\Class Adaptation), посты: https://www.mediamongrels.com/malleable ... plication/, http://kosist.org/2018/07/malleable-vis ... -accessor/
P.S. Vitekkz88, забыл добавить: статьи по ООП дизайн-паттернам - это крутая мысть, будет классно и полезно, если Вы это сделаете! Есть идея тогда их выносить в отдельные посты, потому что боюсь если Вы будете писать их в этом посте, то они потеряются среди комментов и обсуждений которых тут, надеюсь, будет много.
Мы делили апельсин - много наших полегло...
Аватара пользователя
Kosist

Activity Gold
expert
expert
Сообщения: 1236
Зарегистрирован: 21 фев 2011, 23:44
Награды: 2
Версия LabVIEW: 2013-2020
Благодарил (а): 23 раза
Поблагодарили: 30 раз
Контактная информация:

Re: ООП - объектно-ориентированое программирование

Сообщение Kosist »

Usss писал(а):Лабвьюшные классы заставляют меня грустить. Все хорошо, пока нет необходимости их редактировать. Удалил пару лишних типов из базового контрола и вся прога сломалась. На блок диаграмме нет никаких ошибок, но в листе ошибок 77 забракованных vi. Открываю любую из них, в блок-диаграмме все чисто, но в листе ошибок написано "owning library has blocked execution of the VI". Иногда помогает пересохранить базовый тип данных, иногда нет. Где искать проблему не понятно.
В таком случае причина всегда одна - если хотя бы один из override методов "сломан", то и другие тоже будут неисполняемые; хотя их блок диаграмма и будет чиста. Обычно, LabVIEW помещает "сломанную" виайку на начало списка с ошибками, хотя это и не точно. Но пофиксив проблемный метод (или пересохранив базовый метод/целый проект) все будет работать.
Если удалять поля из базового класса, которые нигде не используются, и без них код будет работать - проблемы не бывает, проверено.
Мы делили апельсин - много наших полегло...
Ответить
  • Похожие темы
    Ответы
    Просмотры
    Последнее сообщение

Вернуться в «Модели программирования»