Re[22]: Почему объектно-ориентированное программирование про
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 18.02.11 17:52
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, gandjustas, Вы писали:


G>>У тебя каша в терминах. Приведи в порядок их тогда продолжим.


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


Чтобы сосредоточиться на сути надо понимать о какой сути мы говорим. Разговор был о проектировании, а ты как-то внезапно съехал на внутренности компилятора, подменяя суть терминов.
Re[9]: Почему объектно-ориентированное программирование пров
От: vdimas Россия  
Дата: 18.02.11 19:37
Оценка:
Здравствуйте, gandjustas, Вы писали:

G>Любые свои типы это классы ООП?

G>Я вот знаю много разных типов: кортежи, списки, варианты, записи.

Там вопрос был "оно модель?", а не "оно ООП?"


G>Даже боюсь спрашивать что есть ПИД.


Неужели в гугле нашел?

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


G>А зачем вы стараетесь так делать? Какой положительный эффект для кода от этого образуется, желательно метриками.


А чтобы не оперировать понятиями, взятыми от балды.

V>>Пример программы — не модели.

G>Я уже приводил, читай тему.

Сорри, не могу себе такое позволить, если не трудно кинь ссылку.


V>>Можно начать хотя бы с вики: http://ru.wikipedia.org/wiki/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D0%BD%D0%BE-%D0%BE%D1%80%D0%B8%D0%B5%D0%BD%D1%82%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%BE%D0%B5_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%B8%D0%B5

V>>

V>> Взаимодействие объектов происходит посредством сообщений.


G>И?


А остальное — подробности реализации.

G>>>Тут все понимают интефейс, как явно указанный набор методов\свойств, которые реализует объект.



V>>Еще раз, клиентам объектов до фени. Или ты о том, что в некоторых платформах вызовы абстрактных методов и методов интерфейсов компилируются в разные инструкции? Ну дык, для того ЯВУ и нужны, чтобы мы от этих вещей абстрагировались.


G>Видимо я неправильно понял чем отличаются твои абстрактные интерфейсы от неабстрактных.

G>Может объяснишь?

Вопрос терминологии конкретного языка.
class C {
  public void M1() {}
  public void M2() {}
}

Публичный интерфейс класса С составляют методы М1, М2. Коль эти методы содержат реализацию, интерфейс не абстрактный. Знаешь, наверно полезней погуглить по фразе "полностью абстрактный интерфейс". Просто, если такое, значит бывает и "неполностью абстрактный".


G>

V>>>>Рациональное зерно в наследовании абстрактных интерфейсов для построения объектной иерархии разумеется есть. И чем больше иерархия, тем больше в этом смысла.

G>Я же тебе говорю что в .NET нету больших иерархий абстрактных интерфейсов.

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

Можно заметить, что интерфейс, с точки зрения реализации — это просто чистый абстрактный класс, то есть класс, в котором не определено ничего, кроме абстрактных методов. Если язык программирования поддерживает множественное наследование и абстрактные методы (как, например, C++), то необходимости во введении в синтаксис языка, отдельного понятия «интерфейс» не возникает. Данные сущности описываются с помощью абстрактных классов и наследуются классами для реализации абстрактных методов.


G>Хотя я понял, что я не понял твоего определения абстрактного интерфейса.


И не надо. Достаточно понять определение интерфейса класса.
http://habrahabr.ru/blogs/development/30444/
http://www.cyberguru.ru/programming/cpp/cpp-language-straustrup5-page11.html
И вот не совсем по теме, но занятно: http://msdn.microsoft.com/ru-ru/library/4fcadw4a.aspx

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



V>>Это проблема только в тех конкретных ситуациях, где это проблема. Понимаешь, вот так походя всё обобщая, ты превращаешь полезнейшие практики и наработки в средневековые предрассудки, которыми пытаешься пугать коллег.


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


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


V>>>>Вы в пылу спора всё время забываете, что вся эта абстрактность — вторична.

G>>>Абстрактность ты придумал. Я использую интерфейс для отделения вещей друг от друга.

V>>И сие не есть абстрагирование? http://ru.wikipedia.org/wiki/%D0%90%D0%B1%D1%81%D1%82%D1%80%D0%B0%D0%BA%D1%86%D0%B8%D1%8F


G>Третья ссылка ни о чем. Приведи что ли конкретный фрагмент, в котором будет написано что "разделение" является "абстракцией".


Твое выделил. А вот по ссылке "ни о чем":

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


Добавлю от себя, что процесс абстрагирования — это процесс связанный с допущениями/упрощениями, угу, для выделения сути. Поэтому понятие" абстрактного класса" для меня прочно ассоциируется с "упрощенным классом", в интерфейсе которого описана лишь суть порождаемой им иерархии.
Re[23]: Почему объектно-ориентированное программирование про
От: vdimas Россия  
Дата: 18.02.11 19:39
Оценка:
Здравствуйте, artelk, Вы писали:

A>Ээ.. А причем тут гомоморфность?


Иерархии, следующие LSP автоматически выходят гомоморфными.


V>>Вопрос в силе: с чего ты взял, что конечный объект должен участвовать лишь в одной иерархии?

A>Это ко мне вопрос? Где я такое утверждал или имел ввиду?

Парой постов выше.
Re[13]: Почему объектно-ориентированное программирование про
От: FR  
Дата: 19.02.11 07:03
Оценка:
Здравствуйте, gandjustas, Вы писали:

V>>Ну так в ООП объект и представляет из себя элемент модели. Класс — это уже термин из области типов, к понятию "объект" в общем случае ортогональный.

G>Ага, только в типизированных языках каждый объект имеет некоторый класс. Так что не ортогональный.

При структурной типизации как-раз ортогональный.
Например в OCaml классы по сути синтаксический сахар, можно легко обойтись без них.
Re[2]: Почему объектно-ориентированное программирование пров
От: vdimas Россия  
Дата: 19.02.11 11:06
Оценка: +1
Здравствуйте, samius, Вы писали:

S>разве в других парадигмах рефакторинг исключен? Я вообще думаю что рефакторинг это следствие отношения к предварительному проектированию и собственно самому коду.


Нужен меньше. По опыту плюсов, чем меньше создаешь классов, т.е. чем больше функциональности удается разнести в свободные ф-ии, тем меньше надобность рефакторинга. ИМХО, это объяснимо, т.к. объект в ООП — это группировка в "одно целое" относительно большого кол-ва элементов программы. И весь рефакторинг, по-сути, это перетасовка элементов таких групп. Пройдись по формальным методам рефакторинга: 90% рефакторинга — это способы перемещения методов и полей м/у объектами таким образом, чтобы не поломать случайно исходное поведение программы.

Согласись, набор свободных ф-ий подобного практически не требует. Ну, максимум их идентификаторы порой надо причесать, но сие несущественно.


S>Возможно стоит сменить название на следущее: Что считают гуру причиной провала ООП?


Никто еще не доказал, что ООП провалилось. Оно провалилось лишь как "серебряная пуля" — ИМХО, это проблемы тех, кто считает, что серебряные пули существуют.
Re[3]: Почему объектно-ориентированное программирование пров
От: samius Япония http://sams-tricks.blogspot.com
Дата: 19.02.11 12:16
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, samius, Вы писали:


S>>разве в других парадигмах рефакторинг исключен? Я вообще думаю что рефакторинг это следствие отношения к предварительному проектированию и собственно самому коду.


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

Согласен что меньше

V>ИМХО, это объяснимо, т.к. объект в ООП — это группировка в "одно целое" относительно большого кол-ва элементов программы.

Согласен лишь с тем что при группировке относительно большого кол-ва элементов необходимость рефакторинга возрастает. Но кто заставляет группировать много элементов?

V>И весь рефакторинг, по-сути, это перетасовка элементов таких групп. Пройдись по формальным методам рефакторинга: 90% рефакторинга — это способы перемещения методов и полей м/у объектами таким образом, чтобы не поломать случайно исходное поведение программы.

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

V>Согласись, набор свободных ф-ий подобного практически не требует. Ну, максимум их идентификаторы порой надо причесать, но сие несущественно.

Согласен. Но все же ООП не запрещает свободные функции. В рамках ООП их можно рассматривать их как объект с одним методом. Т.е. если мы возьмем ФП программу и свободные функции преобразуем в объекты, необходимости к рефакторингу не прибавится.
Проблема ООП в отношении рефакторинга лишь в том, что существует тенденция лепить все в существующие уже классы дабы "не плодить сущности".
Все ИМХО, без претензий на абсолютную истину.

S>>Возможно стоит сменить название на следущее: Что считают гуру причиной провала ООП?


V>Никто еще не доказал, что ООП провалилось. Оно провалилось лишь как "серебряная пуля" — ИМХО, это проблемы тех, кто считает, что серебряные пули существуют.

+1, не совсем корректно выразился.
Re[9]: Почему объектно-ориентированное программирование пров
От: SV.  
Дата: 19.02.11 13:41
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Здравствуйте, SV., Вы писали:


SV.>>Давайте перейдем ко второму утверждению — что это проблема. Это может быть проблемой в частных случаях (каких, кстати?), но не в общем, коль скоро потомки активно заставляют предка отрабатывать контракт, а сами они запечатаны (и действие контракта прервано).


AVK>Это не есть теоретические измышления, это сугубо практические наблюдения — в 99% случаев наследование используется криво. Т.е. механизм приносит больше вреда, чем реальной пользы.


С количеством (99%) не согласен, соглашусь лишь с "достаточно часто". Достаточно, для того, чтобы кривое использование наследования надо было признать как явление. И это возвращает нас к моему самому первому утверждению про нездоровые следствия overhype (http://rsdn.ru/forum/philosophy/4155690.1.aspx
Автор: SV.
Дата: 13.02.11
).

С итоговым утверждением "механизм приносит больше вреда, чем реальной пользы" не согласен тоже, но не потому, что механизм приносит больше пользы, чем вреда, а потому, что это утверждение не имеет смысла. Надо не мерить суммарный вред и пользу, а применять строго по делу. Чтобы не разводить пустословие, предлагаю вернуться к моему примеру с приборами. У ganjustas'а, как оказалось, "нет времени" приводить пример API без наследования реализаций, а я бы с удовольствием обсудил.
Re[10]: Почему объектно-ориентированное программирование про
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 19.02.11 15:46
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, gandjustas, Вы писали:


G>>Любые свои типы это классы ООП?

G>>Я вот знаю много разных типов: кортежи, списки, варианты, записи.

V>Там вопрос был "оно модель?", а не "оно ООП?"


Модель это или нет зависит от того кто проектирует.

G>>Даже боюсь спрашивать что есть ПИД.

V>Неужели в гугле нашел?
Не искал.

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


G>>А зачем вы стараетесь так делать? Какой положительный эффект для кода от этого образуется, желательно метриками.

V>А чтобы не оперировать понятиями, взятыми от балды.
Никакие понятия от балды не берутся в любом случае.

V>>>Пример программы — не модели.

G>>Я уже приводил, читай тему.
V>Сорри, не могу себе такое позволить, если не трудно кинь ссылку.

Лень искать. Тебе надо — ты ищи.


G>>>>Тут все понимают интефейс, как явно указанный набор методов\свойств, которые реализует объект.



V>>>Еще раз, клиентам объектов до фени. Или ты о том, что в некоторых платформах вызовы абстрактных методов и методов интерфейсов компилируются в разные инструкции? Ну дык, для того ЯВУ и нужны, чтобы мы от этих вещей абстрагировались.


G>>Видимо я неправильно понял чем отличаются твои абстрактные интерфейсы от неабстрактных.

G>>Может объяснишь?

V>Вопрос терминологии конкретного языка.

V>
V>class C {
V>  public void M1() {}
V>  public void M2() {}
V>}
V>

V>Публичный интерфейс класса С составляют методы М1, М2. Коль эти методы содержат реализацию, интерфейс не абстрактный. Знаешь, наверно полезней погуглить по фразе "полностью абстрактный интерфейс". Просто, если такое, значит бывает и "неполностью абстрактный".

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


G>>

V>>>>>Рациональное зерно в наследовании абстрактных интерфейсов для построения объектной иерархии разумеется есть. И чем больше иерархия, тем больше в этом смысла.

G>>Я же тебе говорю что в .NET нету больших иерархий абстрактных интерфейсов.

V>Там нет больших иерархий, наверно.

То есть то что ты писал практикой не подтверждается.

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

V>Ну так, если связь эта не столь сильна, что ее придется разрывать, нафига там наследование? Заканчивай сам с собой спорить.

Затем что заранее ты не сможешь сказать придется связь разрывть или нет. Зато есть стремление создавать большие иерархии.
Суть проблемы понимаешь?


V>>>>>Вы в пылу спора всё время забываете, что вся эта абстрактность — вторична.

G>>>>Абстрактность ты придумал. Я использую интерфейс для отделения вещей друг от друга.

V>>>И сие не есть абстрагирование? http://ru.wikipedia.org/wiki/%D0%90%D0%B1%D1%81%D1%82%D1%80%D0%B0%D0%BA%D1%86%D0%B8%D1%8F


G>>Третья ссылка ни о чем. Приведи что ли конкретный фрагмент, в котором будет написано что "разделение" является "абстракцией".


V>Твое выделил. А вот по ссылке "ни о чем":

V>

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

Я не про "мысленное явление" писал.

V>Добавлю от себя, что процесс абстрагирования — это процесс связанный с допущениями/упрощениями, угу, для выделения сути. Поэтому понятие" абстрактного класса" для меня прочно ассоциируется с "упрощенным классом", в интерфейсе которого описана лишь суть порождаемой им иерархии.

Очередная черезчур заумная фраза.
Re[10]: Почему объектно-ориентированное программирование про
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 19.02.11 15:59
Оценка: +2
Здравствуйте, SV., Вы писали:

SV.>Чтобы не разводить пустословие, предлагаю вернуться к моему примеру с приборами. У ganjustas'а, как оказалось, "нет времени" приводить пример API без наследования реализаций, а я бы с удовольствием обсудил.


Это лишено какого либо смысла. Потому что говорим мы о разном. Я о том, что, де-факто, так сложилось, что наследование реализации приносит больше вреда, чем пользы, а ты пытаешься доказать, что все таки иногда можно применить и по делу. Да, можно. Особенно учитывая, что в большинстве мейнстрима альтернативы приводят к громоздкому синтаксису. Но, если разнести наследование интерфейсов и реализаций по независимым механизмам, языки от этого только выиграют.
... << RSDN@Home 1.2.0 alpha 5 rev. 1495 on Windows 7 6.1.7600.0>>
AVK Blog
Re[11]: Почему объектно-ориентированное программирование про
От: Zontin  
Дата: 19.02.11 20:30
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>... де-факто, так сложилось, что наследование реализации приносит больше вреда, чем пользы...


А в чем, де-факто, по-твоему, выражается этот вред?
Re[4]: Почему объектно-ориентированное программирование пров
От: vdimas Россия  
Дата: 19.02.11 21:03
Оценка: 16 (3) +3
Здравствуйте, samius, Вы писали:

V>>ИМХО, это объяснимо, т.к. объект в ООП — это группировка в "одно целое" относительно большого кол-ва элементов программы.

S>Согласен лишь с тем что при группировке относительно большого кол-ва элементов необходимость рефакторинга возрастает. Но кто заставляет группировать много элементов?

А ты сторонник объектов с одним полем и одним методом? Если нет, то сказанное в силе.


V>>И весь рефакторинг, по-сути, это перетасовка элементов таких групп. Пройдись по формальным методам рефакторинга: 90% рефакторинга — это способы перемещения методов и полей м/у объектами таким образом, чтобы не поломать случайно исходное поведение программы.

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

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

S>Согласен. Но все же ООП не запрещает свободные функции. В рамках ООП их можно рассматривать их как объект с одним методом. Т.е. если мы возьмем ФП программу и свободные функции преобразуем в объекты, необходимости к рефакторингу не прибавится.


В какие объекты? Функциональные? Я их тоже за ф-ии считаю.

А по существу... Не проблема преобразовать. Проблема в том, что дается механизм, достаточно легкий для дизайна "верхнего уровня", который практически сразу позволяет набросать основной скелет программы и всё выходит красивым и очевидным. Это ООП, и это его несомненное достоинство. От этого трудно отказаться, поэтому это используют. Не зря коллеги говорят, что дизайн верхнего уровня в ООП смотрится лучше всего. Так в чем засада? ИМХО, проблемы в ООП начинаются там, где объекты начинают "тяжелеть", а иерархии разрастаться. А они это делают не потому что кто-то дурак, а по объективным мотивам: уточняется функциональность, растет список сценариев и т.д. Что мы делаем в ответ на разбухание кода? Мы вводим новые "слои" в наш ООП, выделяем "хелперы" и целые "независимые подсистемы" и так до бесконечности. Вот что имелось ввиду, когда утверждалось, что ООП обречен на постоянный рефакторинг, то бишь на постоянное переписывание кода.

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

Справедливости ради, в ООП тоже сей подход можно использовать. Т.е. брать у ФП не только модную здесь иммутабельность и замыкания (порой до экстаза), но саму программу плясать от данных и небольших объектов, представляющих эти данные с одной стороны, и осуществляющие обработку этих данных с другой стороны. Заметь, это отход от парадигмы общепринятой парадигмы ООП, хотя используются ООП ср-ва. Итоговый дизайн должен представлять из себя большое кол-во легковесных объектов минимальнейшей функциональности, которые не потребует рефакторинга ни при каких обстоятельствах. ИМХО, такой стиль — дело привычки. Типичная "лакмусовая бумажка" для ситуации, когда такой стиль не только стоит применять из сображений "хорошего тона", но когда только он и спасает проект — это если подмечаешь объекты, в которых относительно мало полей, но очень много методов. Применять ООП-декомпозицию тут бессмысленно, ибо в таких случаях обычно невозможно декомпозировать состояние. Поэтому поступаешь просто — объект превращаешь в "контекст". Остальное разбивается либо на маленькие объекты, которые используются как временные "стековые" при вычислении (value-type в дотнете просто рулез для такого), а что не требует промежуточного состояния при вычислении — вовсе разносишь на свободные ф-ии.


S>Проблема ООП в отношении рефакторинга лишь в том, что существует тенденция лепить все в существующие уже классы дабы "не плодить сущности".


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


S>Все ИМХО, без претензий на абсолютную истину.


Окстись, мы тут максимум личными наблюдениями делиться можем, а они субъективны априори.
Re[5]: Почему объектно-ориентированное программирование пров
От: samius Япония http://sams-tricks.blogspot.com
Дата: 20.02.11 03:12
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, samius, Вы писали:


S>>Согласен лишь с тем что при группировке относительно большого кол-ва элементов необходимость рефакторинга возрастает. Но кто заставляет группировать много элементов?


V>А ты сторонник объектов с одним полем и одним методом? Если нет, то сказанное в силе.

Нет, не сторонник. Но SRP уважаю и начинаю нервничать когда число полей у объекта, который по сути не является property-bag-ом, начинает увеличиваться.

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


V>Не может быть никаких четких критериев.

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

S>>Согласен. Но все же ООП не запрещает свободные функции. В рамках ООП их можно рассматривать их как объект с одним методом. Т.е. если мы возьмем ФП программу и свободные функции преобразуем в объекты, необходимости к рефакторингу не прибавится.


V>В какие объекты? Функциональные? Я их тоже за ф-ии считаю.


V>А по существу...

V>... Что мы делаем в ответ на разбухание кода? Мы вводим новые "слои" в наш ООП, выделяем "хелперы" и целые "независимые подсистемы" и так до бесконечности. Вот что имелось ввиду, когда утверждалось, что ООП обречен на постоянный рефакторинг, то бишь на постоянное переписывание кода.
Неа, новые слои и хелперы могут быть реакцией на уточнение требований. А имелось ввиду следующее:

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

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

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

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

V>Справедливости ради, в ООП тоже сей подход можно использовать. Т.е. брать у ФП не только модную здесь иммутабельность и замыкания (порой до экстаза), но саму программу плясать от данных и небольших объектов, представляющих эти данные с одной стороны, и осуществляющие обработку этих данных с другой стороны.

О!

V>Заметь, это отход от парадигмы общепринятой парадигмы ООП, хотя используются ООП ср-ва.

С некоторых пор я отделяю общепринятую парадигму ООП от взглядов Кея "Всё является объектом...". Мне даже кажется что на чисто ФП программу можно смотреть через призму "Все является объектом... Объекты взаимодействуют, посылая и получая сообщения", в то время как общепринятое ООП ожидает от программы доминирования трех китов в традиционном ООП понимании.

V>Итоговый дизайн должен представлять из себя большое кол-во легковесных объектов минимальнейшей функциональности, которые не потребует рефакторинга ни при каких обстоятельствах. ИМХО, такой стиль — дело привычки. Типичная "лакмусовая бумажка" для ситуации, когда такой стиль не только стоит применять из сображений "хорошего тона", но когда только он и спасает проект — это если подмечаешь объекты, в которых относительно мало полей, но очень много методов. Применять ООП-декомпозицию тут бессмысленно, ибо в таких случаях обычно невозможно декомпозировать состояние. Поэтому поступаешь просто — объект превращаешь в "контекст". Остальное разбивается либо на маленькие объекты, которые используются как временные "стековые" при вычислении (value-type в дотнете просто рулез для такого), а что не требует промежуточного состояния при вычислении — вовсе разносишь на свободные ф-ии.


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

S>>Проблема ООП в отношении рефакторинга лишь в том, что существует тенденция лепить все в существующие уже классы дабы "не плодить сущности".


V>Ну дык правильно. Ведь эти классы обычно отражают первоначальное видение дизайна, т.е. не случайны. И в этих классах могут находится нужные нам данные, поэтому мы и пихаем функциональность в них, дабы не нарушать нашу любимую "инкапсуляцию".

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

V>А ее по мере роста программы нарушать все-равно придется. Правда, не факт, что при этом пострадает надежность, ведь по-прежнему можно обеспечить безопасный набор любых операций, просто этот набор после декомпозиции будет более "мелкогранулирован".

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


S>>Все ИМХО, без претензий на абсолютную истину.


V>Окстись, мы тут максимум личными наблюдениями делиться можем, а они субъективны априори.


Просто эти 4 буквы отпугивают любителей разоблачать, понижают температуру дебатов и дают понять что я не настроен воинственно. В общем, на всякий случай
Re[24]: Почему объектно-ориентированное программирование про
От: artelk  
Дата: 20.02.11 10:01
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, artelk, Вы писали:


A>>Ээ.. А причем тут гомоморфность?


V>Иерархии, следующие LSP автоматически выходят гомоморфными.


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

Допустим, создаю я некий класс и реализую в нем некий интерфейс (например, IEnumerable<T>). Начинай доказывать, что я не должен добавлять в него ни одного вируального метода, чтобы это не противоречило LSP...

V>>>Вопрос в силе: с чего ты взял, что конечный объект должен участвовать лишь в одной иерархии?

A>>Это ко мне вопрос? Где я такое утверждал или имел ввиду?

V>Парой постов выше.


Ок, допустим мы имеем некий (с) Очень Объектно Ориентированный язык программирования. Все есть объекты, включая классы. Фунции — тоже первокласные сущности. Классы представляют собой фабрики объектов на основе прототипов или как-то еще. Более того, класс объекту можно назначить динамически — просто скопировать в него соответствующие поля и методы. Таким образом, типом объекта будет просто набор сингатур методов, которые у него можно вызвать, плюс некая семантика, стоящая за ними и их объединяющая. Можно создать некий безликий Object, а потом последовательно назначить ему несколько классов. Если у этих классов есть совпадающие по сигнатуре методы, то они переопределяются в зависимости от порядка назначения этих классов объекту. Ты подобную ситуацию имел ввиду, говоря об участии объекта в нескольких иерархиях?
Re[2]: Почему объектно-ориентированное программирование пров
От: AVC Россия  
Дата: 20.02.11 13:02
Оценка:
Здравствуйте, VoidEx, Вы писали:

VE>

VE>"Ну и где мы теперь, с этой вашей красивой теорией относительности, кто-нибудь может мне назвать хоть какие-то реально-практические результаты её применения в вашей обыденной жизни после целого века её ковыряния и массового насаждения?"- как всегда язвительно вопрошает Гэбриел.


VE>Гэбриел не в курсе про существование GPS что ли?


Сомнительно, что GPS являет реально-практический пример применения ТО.
http://www.physicsmyths.org.uk/gps.htm

Но существует одно качество, которое нельзя купить, — это надежность. Цена надежности — погоня за крайней простотой. Это цена, которую очень богатому труднее всего заплатить.

Хоар
Re[6]: Почему объектно-ориентированное программирование пров
От: vdimas Россия  
Дата: 20.02.11 13:31
Оценка: 4 (1)
Здравствуйте, samius, Вы писали:

S>Получается что ООП — это сверху вниз, а ФП — снизу вверх?

S>ИМХО, ООП не мешает продумывать тщательно как "протаскиваются" данные. Или можно сказать что мешает не ООП, а проектирование сверху в какой-то мере, или даже не проектирование а реализация сверху. Именно она обеспечивает нас неизбежным рефакторингом.

Не так. Оба подхода применимы для разработки снизу и сверху. Акцент не на "снизу vs сверху", а на "данные vs акторы". Что происходит при отталкивании от данных? Очень простая, но важная вещь: мы выполняем гораздо больший анализ происходящего ДО созревания цельной картинки, то бишь дизайна, в голове. Мы банально начинаем обладать большей информацией. В советской школе программирования учили разрабатывать сначала данные и алгоритмы их обработки. А выделение в модули — это уже результат анализа получаемого решения, фактически выходит автоматически из анализа кол-ва связей по передаче данных, и деления программы по принципу наибольшей связанности внутри модуля и наименьшей — м/у модулями (древнейший принцип, идет еще из электроники/автоматики). Разве можно это разделение выполнить, не обладая достаточно подробным списком этих самых связей, то бишь не обладая достаточно полным списком используемых алгоритмов и структурами оперируемых данных? В общем, разработка сверху в ООП в клиническом своем варианте, за который его критикуют — это предварительное разделение на "модули" то бишь относительно независимые единицы — объекты, без обладания достаточной информацией о подробностях реализации. Ну и потом, по мере реализации, эти подробности всплывают, уточняется наше собственное видение решения, и мы наше первоначальное решение вынуждены модифицировать.

В общем, проблема не в ООП, а в заблуждении относительно того, что предварительная стадия анализа может быть минимальной для большой задачи. Не обращал тут внимание на мнения коллег, что иерархии/системы ООП относительно неплохо работают на небольших и средних задачах (уточнять размеры сейчас не будем)? ИМХО, это от того, что вероятность допустить ошибку при "беглом" анализе на небольших системах меньше.

Я, размеется, не призываю всех тут же удариться в глубокий анализ и прочее. Тем более, что на некоторых языках/средах/IDE пошаговый рефакторинг может оказаться более дешевым решением, чем углубленный предварительный анализ. Опять же, пошаговость разработки позволяет получать успешные результаты на каждом шаге, что тоже есть гут, бо позволяет уточнять функциональные требования через обратную связь с заказчиком. Но описанные наблюдения "удачных кейзов" стоит держать в голове, и пытаться формировать в ООП заведомо относительно небольшие "экосистемы" типов, дабы переделки внутри этой экосистемы как можно меньше задевали окружающее. Мое замечание относительно ФП в предыдущем посте лишь показывало, что такая независимость при проектировании от данных получается автоматически, т.е. каждая такая экосистема сама формируется вокруг конкретной структуры данных и алгоритмов по трансформации/обработки этих данных.

S>Да, т.е. двумя словами — рефакторинг в ООП может быть сведен к минимуму при некоторых правильных привычках. И обилие рефакторинга в ООП не может быть предъявлено как причина его провала в качестве серебрянной пули. Это я и имел ввиду изначально.


Разумеется! Проблема не может быть в ООП-языках, предоставляющих "три кита", наивно жаловаться на молоток, если гвоздь криво вбил. Проблема в руках. А называя вещи своими именами — проблема в том, что в последние лет 10-15 особенно заметна попытка игнорировать, скажем так, академические вещи из разработки ПО. Народ бросается на поверхностность, типа ООП-паттернов GOF, контейнеров IOC и XML. Овладев ими, начинает считать что всё знает и всё умеет. Сему способствовал бешенный рост спроса на программистов все 90-е, который продолжается до сих пор и объективно предъявляет к массе программистов сейчас заниженные требования. Например, основные требования — знать что есть ООП (если набирают програмистов на ООП-языки), знать основные паттерны ООП и библиотеки целевого языка. И еще этот, как его... XML. Это что, требования к профессионалу-программисту? Блин, сегодня рядовые программисты самостоятельно проектируют доверенные им куски ПО такой сложности, которые раньше проходили через руки аналитиков и архитекторов. ИМХО, требования к программистам ввиду этого должны были вырасти, а не понизиться. И удобные IDE не сей разрыв компенсировать не способны, увы. В общем, що маємо то маємо.

V>>Ну дык правильно. Ведь эти классы обычно отражают первоначальное видение дизайна, т.е. не случайны. И в этих классах могут находится нужные нам данные, поэтому мы и пихаем функциональность в них, дабы не нарушать нашу любимую "инкапсуляцию".

S>Меняется видение — нужно менять класс, а не пихать в него пока он не лопнет. Менять проще пока класс и его ответственность обозримы.

Это ты подтверждаешь верность исходного постулата. Конечно, классы меняют, модернизируют. Но не заранее, а когда налицо симптомы такой надобности. Не факт, что перед каждым добавлением новой функциональности стоит заранее всё рефакторить. Может так оказаться, что это была последняя добавленная функциональность в класс, и нафига тогда делать работу, которую никто не оценит? В том смысле, что результаты декомпозии не будут востребованы т.е. не будет последующего добавления функциональности в этот участок кода.

S>Правильное слово какое. По поводу размеров грануляции соображения следующие: крупная грануляция тяготеет к рефакторингу, а мелкая — к усложнению композиции на верхнем уровне. И это вне парадигм. Решение может быть в ведении дополнительных уровней/этажей абстракции.


Они и так сами получаются. Деление на слои процесс как бы автоматический, управляется сложностью единичного элемента, которую может обозреть человек. Какое там правило для ф-ий и методов — не больше 1-2 экранов кода? Примерно так.


S>Просто эти 4 буквы отпугивают любителей разоблачать, понижают температуру дебатов и дают понять что я не настроен воинственно. В общем, на всякий случай


Почему нет, кстати? Если воинственность будет означать большее желание что-то доказать и в итоге означает большую аргументированность и полезность информации — это даже кул. Спор с интересным собеседником во-первых, раскручивает его на дележ информации, которую он бы в ответ на простой вопрос давать бы не стал (хотя бы из-за лени или снобизма). Ну и построение собственных аргументов всегда способсвует наведению порядка в собственной же голове, что тоже всегда гут. Так что если до обкладывания органами не доходит, то ради бога. Для поддержания формы и ясности мысли, так сказать.
Re[25]: Почему объектно-ориентированное программирование про
От: vdimas Россия  
Дата: 20.02.11 13:58
Оценка:
Здравствуйте, artelk, Вы писали:

V>>Иерархии, следующие LSP автоматически выходят гомоморфными.


A>Говоря о гомоморфности, ты как-то намекаешь на множественную диспечеризацию?


Какая связь?

A>Допустим, создаю я некий класс и реализую в нем некий интерфейс (например, IEnumerable<T>). Начинай доказывать, что я не должен добавлять в него ни одного вируального метода, чтобы это не противоречило LSP...


По справедливости, никто никому ничего не должен.

Строго говоря, данный критерий можно выполнить и без гомоморфизма. Если производный класс содержит дополнительные открытые функции, их можно просто не вызывать из клиента базового класса. Постойте-ка… а зачем добавлять открытые функции, если их не использовать? Если в одном производном классе были добавлены одни скрытые функции, а в другом — другие, со временем в вашей программе наверняка отыщется точка, в которой их нельзя свободно поменять местами.
Настоящая опасность заключается втом, чо без выполнения этого критерия клиентам придется думать о производных классах, а не только о базовом классе, который они знают и любят. Если бы в Dad присутствовали дополнительные открытые члены, клиента Grandpa со временем мог бы спросить свой объект: «Долой притворство — что ты представляешь собой в действительности?» В итоге было бы нарушено столько принципов модульного строения и инкапсуляции, что об этом можно было бы написать целую книгу.
Самый простой способ обеспечить взаимозаменяемость — воспользоваться гомоморфизмом. По крайней мере, для интерфейсов гомоморфизм обеспечивает взаимозаменяемость по определению, поскольку клиентам Grandpa не придется беспокоиться о существовании других функций, с которыми им положено работать.


И в реальной разработке я видел многократно, когда IEnumerable<T> приводят к List<T>, T[] или к некоему своему уникальному типу. Только LSP тут уже не при чем.


A>Ок, допустим мы имеем некий (с) Очень Объектно Ориентированный язык программирования. Все есть объекты, включая классы. Фунции — тоже первокласные сущности. Классы представляют собой фабрики объектов на основе прототипов или как-то еще. Более того, класс объекту можно назначить динамически — просто скопировать в него соответствующие поля и методы. Таким образом, типом объекта будет просто набор сингатур методов, которые у него можно вызвать, плюс некая семантика, стоящая за ними и их объединяющая. Можно создать некий безликий Object, а потом последовательно назначить ему несколько классов. Если у этих классов есть совпадающие по сигнатуре методы, то они переопределяются в зависимости от порядка назначения этих классов объекту. Ты подобную ситуацию имел ввиду, говоря об участии объекта в нескольких иерархиях?


Нет, я обращал внимание на то, что нарушение LSP происходит не в месте проектирования иерархии, а в месте ее использования. Т.е. рассматривать каждую иерархию в отдельности при использовании надо как гомоморфную. Явная гомоморфность иерархии — это просто способ заранее дать себе линейкой по рукам, дабы неповадно.
Re[26]: Почему объектно-ориентированное программирование про
От: artelk  
Дата: 20.02.11 15:18
Оценка:
Здравствуйте, vdimas, Вы писали:

V>Здравствуйте, artelk, Вы писали:


A>>Говоря о гомоморфности, ты как-то намекаешь на множественную диспечеризацию?


V>Какая связь?


Говорю же, телепатический канал на твоей волне что-то шумит...

A>>Допустим, создаю я некий класс и реализую в нем некий интерфейс (например, IEnumerable<T>). Начинай доказывать, что я не должен добавлять в него ни одного вируального метода, чтобы это не противоречило LSP...


V>По справедливости, никто никому ничего не должен.

V>

V>Строго говоря, данный критерий можно выполнить и без гомоморфизма. Если производный класс содержит дополнительные открытые функции, их можно просто не вызывать из клиента базового класса. Постойте-ка… а зачем добавлять открытые функции, если их не использовать? Если в одном производном классе были добавлены одни скрытые функции, а в другом — другие, со временем в вашей программе наверняка отыщется точка, в которой их нельзя свободно поменять местами.
V>Настоящая опасность заключается втом, чо без выполнения этого критерия клиентам придется думать о производных классах, а не только о базовом классе, который они знают и любят. Если бы в Dad присутствовали дополнительные открытые члены, клиента Grandpa со временем мог бы спросить свой объект: «Долой притворство — что ты представляешь собой в действительности?» В итоге было бы нарушено столько принципов модульного строения и инкапсуляции, что об этом можно было бы написать целую книгу.
V>Самый простой способ обеспечить взаимозаменяемость — воспользоваться гомоморфизмом. По крайней мере, для интерфейсов гомоморфизм обеспечивает взаимозаменяемость по определению, поскольку клиентам Grandpa не придется беспокоиться о существовании других функций, с которыми им положено работать.


Чем дальше в лес — тем злее дятлы... А кто ж заставляет работать с объектами через сылки на самую дальнюю базу? В .NET, например, есть огромное количество различных методов, принимающих IEumerable<T>. Только ничто не запрещает переменным иметь более точный тип IList<T>, Dictionary<T1, T2> и т.п.

Код:
class SomeClass
{
  private List<int> items = new List<int>();

  public void SomeMethod1()
  {
    //...
    var x = items.Count(_ => _ > 10);
    //...
  }

  public void SomeMethod2()
  {
    //...
    items.Add(42);
    //...
  }
}

Почему этот код должен быть переписан в:
class SomeClass
{
  private IEnumerable<int> items = new List<int>();

  public void SomeMethod1()
  {
    //...
    var x = items.Count(_ => _ > 10);
    //...
  }

  public void SomeMethod2()
  {
    //...
    ((IList<int>)items).Add(42);
    //...
  }
}

?

V>И в реальной разработке я видел многократно, когда IEnumerable<T> приводят к List<T>, T[] или к некоему своему уникальному типу. Только LSP тут уже не при чем.

Действительно непричем.

<...>

V>Нет, я обращал внимание на то, что нарушение LSP происходит не в месте проектирования иерархии, а в месте ее использования. Т.е. рассматривать каждую иерархию в отдельности при использовании надо как гомоморфную. Явная гомоморфность иерархии — это просто способ заранее дать себе линейкой по рукам, дабы неповадно.


Нифига не понятно.
Re[27]: Почему объектно-ориентированное программирование про
От: vdimas Россия  
Дата: 20.02.11 16:39
Оценка:
Здравствуйте, artelk, Вы писали:

V>>Нет, я обращал внимание на то, что нарушение LSP происходит не в месте проектирования иерархии, а в месте ее использования. Т.е. рассматривать каждую иерархию в отдельности при использовании ее через корень иерархии надо как гомоморфную. Явная гомоморфность иерархии — это просто способ заранее дать себе линейкой по рукам, дабы неповадно.


A>Нифига не понятно.


Тем не менее, в этом вся соль. Добавил выделенное курсивом, вдруг поможет.
Re[11]: Почему объектно-ориентированное программирование про
От: vdimas Россия  
Дата: 20.02.11 17:17
Оценка:
Здравствуйте, gandjustas, Вы писали:


V>>>>Пример программы — не модели.

G>>>Я уже приводил, читай тему.
V>>Сорри, не могу себе такое позволить, если не трудно кинь ссылку.
G>Лень искать. Тебе надо — ты ищи.

Так и запишем. Примера программы, не являющейся моделью, нет.

G>Гугл вообще не дает точного совпадения по фразе "полностью абстрактный интерфейс".


Попробуй по фразе "абстрактный интерфейс", или "интерфейс класса". Прояви воображение, поищи не только на русском.

Смотри, что пишут даже по языку Java, где это ключевое слово, если не ошибаюсь, было встречено во второй раз, после IDL:

An interface in the Java programming language is an abstract type that is used to specify an interface (in the generic sense of the term) that classes must implement. Interfaces are declared using the interface keyword, and may only contain method signatures and constant declarations (variable declarations which are declared to be both static and final). An interface may never contain method definitions.


Обрати внимание на выделенное. Ну и на этой же странице есть ссылка на определение понятия interface в IT.


G>Приведи в порядок терминологию, потом продолжим разговор.


Ты уже 10-й пост демонстрируешь, что путаешь понятие "интерфейс" с особым типом и ключевым словом конкретного языка. Воистину, вот и выросло next generation. ИМХО, остальное обсуждать бессмысленно, пока не сдвинемся с этой мертвой точки.

V>>Там нет больших иерархий, наверно.

G>То есть то что ты писал практикой не подтверждается.

Я писал для случая больших иерархий. Т.е. пример с небольшими иерархиями ничего не подтверждает, а всего-лишь неподходит. Ну и, они таки есть, эти большие иерархии, в прикладных либах дотнета, хоть и используется больше техника абстрактных классов, чем интерфейсов. Коментарии по этому поводу уже давал в этой же ветке, ты невнимателен. Так же предлагал сравнить с библиотеками GUI от Java, например AWT. Очень показательно применение интерфейсов в больших иерархиях. Поверь, тут есть куда двигаться дальше, а не топтаться с этим вопросом на месте. Был бы интерес. Пока ты хотя бы краем глаза не посмотришь, обсуждать практически нечего.


G>Затем что заранее ты не сможешь сказать придется связь разрывть или нет. Зато есть стремление создавать большие иерархии.

G>Суть проблемы понимаешь?

Понимаю. Это называется детской болезнью, переходящей, в случае поражения предрассудками, в инженерный идиотизм и заканчивающейся смертельным диагнозом "это придется переделать заново". Лечится гибкостью ума и желанием обучаться, с дополнительными припарками в виде зачатков анализа функциональных требований, с выходом хоть на какой-то список сценариев до написания первого класса. Более подробно высказался здесь
Автор: vdimas
Дата: 20.02.11
и здесь
Автор: vdimas
Дата: 20.02.11
.


V>>Твое выделил. А вот по ссылке "ни о чем":

V>>

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

G>Я не про "мысленное явление" писал.

Не явление, а выделение. Смени там у себя шрифт.

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


V>>Добавлю от себя, что процесс абстрагирования — это процесс связанный с допущениями/упрощениями, угу, для выделения сути. Поэтому понятие" абстрактного класса" для меня прочно ассоциируется с "упрощенным классом", в интерфейсе которого описана лишь суть порождаемой им иерархии.


G>Очередная черезчур заумная фраза.


Мде. Как ты экзамены сдавал, интересно...
Re[12]: Почему объектно-ориентированное программирование про
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 20.02.11 19:27
Оценка:
Здравствуйте, Zontin, Вы писали:

Z>А в чем, де-факто, по-твоему, выражается этот вред?


Неоправданное повышение связности, уменьшение гибкости API.
... << RSDN@Home 1.2.0 alpha 5 rev. 1495 on Windows 7 6.1.7600.0>>
AVK Blog
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.