![]() |
Модераторы: LSD, AntonSaburov |
![]() ![]() ![]() |
|
Властелин |
|
||||
Новичок Профиль Группа: Участник Сообщений: 22 Регистрация: 21.9.2009 Репутация: нет Всего: нет |
Приветствую!
Как известно любителям и почитателям ООП, оно зиждется на трёх китах, первым из коих выступает инкапсуляция (порядковый номер "кита" явно намекает на его особую важность, необходимость, частоту применения), подразумевающая сокрытие данных. Например, в материале в Википедии (Инкапсуляция (программирование)) упоминается что инкапсуляция применяется , цитирую: "с целью предотвращения повреждения свойств другим кодом, которому необходимо предоставить только права на чтение". Внимание, вопрос: приведите распространённый коварный пример "повреждения свойств (класса) другим кодом", который наглядно показывает что без сокрытия данных туговато придётся в этом суровом объектно-ориентированном мире программирования! Еще самое распространённое объяснение (которое выражено здесь http://stackoverflow.com/a/20895152/1931613) цели сокрытия данных, в каком-то смысле продолжение процитированному из Википедии, что сокрытие используется с целью, что в большом проекте public поле будет видно где-то там и будет по ошибке использовано вместо другого поля, определённого с таким же именем. И опять непонятно как будет выглядеть на конкретном примере эта проблема ошибочного использования поля с именем, уже определённого ранее. Ещё, считается, практической целью использования инкапсуляции является сокрытие поля от пользователя, т.е. чтобы введенное пользователем некорректное значение было обработанное в set-методе, прежде чем будет присвоено полю. Вот выше представлены два, повторюсь, "распространённых" довода, вынуждающих пользоваться инкапсуляцией. В рамках большого проекта, в котором, скажем, 1000 классов, в скольких классах с большего веско возникают две вышеописанных ситуации использования инкапсуляции? 1-ое: не представляю как выглядит в коде проблема, пример конфликта имен (snapshot wanted!) Например, 2-ое, используется считаю максимум в 50 классах (типа 50 окон интерфейса в проекте). И то в большинстве классов set-теры используются, представляя собой очень "тщательную" обработку поля типа
И аналогично в большинстве случаев такая же "тщательная" обработка в get-терах возвращения значения поля:
А принято за правило использовать во всех классах get-теры и set-теры (на всякий случай, а вдруг!..), когда они не несут в большинстве случаев функциональной нагрузки, а лишь для "декорации", для "поддержания концептуальности". Это наводит на логический вывод, что в частности инкапсуляция, представленная каркасом, остовом, фундаментом ОО программирования на практике являет собой метод, к которому возникает необходимость прибегнуть лишь иногда! И возникает тот же вопрос, часто ли возникает необходимость использования наследования и полиморфизма?! Поэтому с теперешней перспективы (очень не исключаю что моё мнение может перемениться после убедительного опровержения моих доводов) не называл ООП концепцией, фундаментом, а лишь примочкой в добавок к структурному программированию и запихнул бы эти три "кита" куда подальше, например в шаблоны проектирования, где порождающий шаблон Синглтон являлся бы расширением порождающего шаблона Инкапсуляция ![]() Спасибо! P.S. К чему, кажется, засорять эфир, казалось бы, столь банальным, избитым, промусоленным многократно вопросом об инкапсуляции? Однако далеко не моментально удаётся найти толковый пример по вопросу, часто обсасываемому в теории. Как например, абстрактный класс, примеров использования предостаточно, но что-то не припомню источник с вменяемым объяснением, почему возникла необходимость его создания (хорошо, абстрактный класс не даёт создать его экземпляр, в чём "плюшка"?), хотя в природе существуют интерфейсы, который впихнул в базовый класс, а объявленные в нём методы переопределил в производном. В свою очередь возникает вопрос, зачем нужен интерфейс, кроме того как чтобы избежать конфликтов множественного наследования ( которые возникают С++)? Это сообщение отредактировал(а) Властелин - 30.8.2014, 19:34 |
||||
|
|||||
jManiak |
|
|||
![]() Шустрый ![]() Профиль Группа: Участник Сообщений: 130 Регистрация: 6.2.2007 Где: Санкт-Петербург Репутация: нет Всего: 1 |
С планшета, поэтому кратко:
По большей части это защита от дурака. Т.е. Чтобы не нашелся какой-нибудь умник, который посмотрит на поля объекта в каком-то куске кода, да поменяет его значение, хотя по логике, оно не должно меняться снаружи своего контекста. В принципе, никто вас не заставляет этим пользоваться. Хотите, обращайтесь напрямую к полям. Но если потом нужно будет скрыть это поле за методы, я не уверен, что рефракторинг(планшет предложил вариант 'рефрактор ног' ![]() Просто все уже по привычке делают поля приватными и get/set методы к ним. Даже если это не нужно напрямую. Может потом понадобится. Ничего плохого я в этом не вижу. Опять же, среда разработки все это за вас сделает. С другой стороны, представьте, что вы пишите не реализацию бизнес-логики доя своего заказчика, а некий публичный API. Вот тут уж действительно нужно будет и методы скрывать и поля и классы от посторонних глаз. Во первых, не нужно чтобы была видна ваша внутренняя кухня, люди будут теряться, во вторых опять же чтобы кто-нибудь по незнанию чего-нибудь бы не попортил. Абстрактные классы нужны. Например вы пишите обработку каких-то штук. Штуки бывают разные. 80%обработки для них одинакова. А 20 для каждого типа своя. Пишется абстрактные класс с 80% логики доя всех, в которой вызываются абстрактные методы, которые еще не реализованы, и для каждого типа свой класс, в которых только реализуются эти абстрактные методы. Все. Напрямую абстрактные класс никто не сможет использовать(помните про защиту от дурака?), зато есть набор конкретных классов с законченной функциональностью. В структурном подходе это была бы у вас куча бооооольших if'ов, которые бы плохо читались. Другое дело когда у народа начинает сносить крышу на почве шаблонов проектирования. И они для решения задачи в 20 строк начинают лепить 2 абстрактных класса, три фабрики и т.д. Это печально, да. Также помните, что ООП не для компьютера. Для него лучше набор ноликов и единичек. Вы пишите код, чтобы его еще и другие читали, изменяли. В том числе и для этого все эти плюшки. Ищите везде компромисс, не нужно впадать в крайности ни в чем. P.S. Если есть опечакти, извиняюсь. |
|||
|
||||
Mirkes |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 586 Регистрация: 18.8.2011 Где: Красноярск Репутация: 7 Всего: 17 |
Вообще-то изначально, на заре ООП инкапсуляция предназначалась не для скрытия полей а для того, чтобы поля вообще были!
Первая ссылка погуглу дает "Инкапсуляция (encapsulation) - это механизм, который объединяет данные и код, манипулирующий зтими данными, а также защищает и то, и другое от внешнего вмешательства или неправильного использования. В объектно-ориентированном программировании код и данные могут быть объединены вместе; в этом случае говорят, что создаётся так называемый "чёрный ящик". Когда коды и данные объединяются таким способом, создаётся объект (object). Другими словами, объект - это то, что поддерживает инкапсуляцию." То, что вы назвали инкапсуляцией и ее основной целью находится после "а также". Без инкапсуляции не было бы полей а работали бы вы с массивами и отдельными переменными. В лучшем случае со структурами (С) или записями (Паскаль). Абстрактные классы мне лично экономят кучу времени по причинам прекрасно описанным в предыдущем топике. Насчет полиморфизма трудно даже понять, с чего начать - представьте любой графический интерфейс без полиморфизма и посмотрите, на что это будет походить. Например размещение всех элементов в форме. Я сейчас даже не могу вспомнить, как я это делал. Помню, что времени это отнимало кучу, поскольку все позиции приходилось указывать ручками - никаких лайоутов, так как нет объектов. Насчет примеров повреждения одним классом данных другого. Этого сколько угодно. Рассмотрим, например, реализацию шрифта. Размер может быть указан двумя способами. Причем в зависимости от того, как указан размер, изменяется значение ряда внутренних переменных. Если делаете через сеттер - все нормально. Если залезаете и меняете ручками - можете потом долго искать причину развала, который произойдет, что самое печальное не сразу, а в какой-то отдаленный момент. Нужно ли запечатывать все поля в геттеры и сеттеры? Вообще говоря нет. Я достаточно часть этим пренебрегаю. Должен признаться, что пару раз по этой причине имел большие затыки с отладкой. Насчет совпадения имен - сколько угодно. Любому создаваемому объекту часто хочется дать имя. Как правило, это поле называешь name. Вроде никаких проблем. Позже я с удивлением обнаружил, что ряд классов, в которых я создал поле name имели предков с таким полем. У меня проблем не было, а без private - точно были бы. -------------------- Mirkes |
|||
|
||||
Властелин |
|
|||
Новичок Профиль Группа: Участник Сообщений: 22 Регистрация: 21.9.2009 Репутация: нет Всего: нет |
Mirkes, спасибо за толковый достойный убедительный логичный обоснованный ответ!
Абстрактные классы В чём "плюшка" что нельзя создавать объекты абстрактных классов? Встречающиеся ответы что для поддержания концептуальности и "защиты от дурака" звучит как-то неубедительно... Зачем было создавать абстрактный класс, если его можно сфарганить путём создания обычного класса и объявленный интерфейс в нём. Соответственно вопрос: кроме того что нельзя создать объект абстрактного класса, в чём ещё преимущества абстрактного класса по сравнению с предложенной мной) конструкцией класс+интерфейс? Выглядит очень убедительно, но боюсь не в силах сейчас вообразить "примеров повреждения одним классом данных другого" на практике:) Snapshot wanted!) Начну код, который бы показывал пример повреждения одним классом другого, просьба продолжить\изменить) чтобы в примере были очевидны проблемы "в зависимости от того, как указан размер" и "Если залезаете и меняете ручками - можете потом долго искать причину развала" ...
И пожалуйста пример для (а то воображалка не соображает): Это сообщение отредактировал(а) Властелин - 2.9.2014, 13:06 |
|||
|
||||
Mirkes |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 586 Регистрация: 18.8.2011 Где: Красноярск Репутация: 7 Всего: 17 |
Ок. Пример про повреждение. Причем пример реальный.
Я подготовил серию апплетов по методам анализа данных. Апплеты используют визуализацию всего, что происходит. Речь идет о точках на плоскости. При всех вычислениях используются нормальные координаты точек x,y. Теперь про отображение. Есть несколько типов точек и они имеют разный размер. Очевидно, что для точки x,y координаты расположения объекта, который ее показывает вычисляются по тривиальному правилу: xPlain = x-size/2; yPlain = y-size/2; Это преобразование запрятано в сеттер. Обратное преобразование в геттер. Пока я не закрыл полностью доступ к xPlain и yPlain я несколько раз разыскивал ошибки, возникшие из желания сэкономить и вместо вызова сеттера прямо присвоить значения. Вообще-то это и есть пример ![]() Забыл про абстрактные классы. Пойдем по пунктам. 1. В абстрактном классе как правило часть методов не реализована а только объявлена. 2. Когда я создаю абстрактный классификатор, я предусматриваю в нем множество методов, которые одинаковы для всех классификаторов и реализую. Так же я предусматриваю методы, которые для каждого классификатора уникальны, объявляю эти методы и не реализую. Это абстрактные методы. 3. Вызовы абстрактных методов используются в реализованных методах. Ответы на вопросы автора поста. Почему нельзя создавать объекты абстрактного класса? (в чем плюшка) Это не плюшка, а вынужденная мера, поскольку при попытке работы с созданным объектом абстрактного класса получим облом не вызове абстрактного метода. Так что это таки защита от дурака! Почему нельзя создать нормальный класс и не вынести все методы в интерфейс. Не совсем понял идею. Если я создал нормальный класс. а все абстрактное вынес в интерфейс, то возможно два варианта: 1. Мой нормальный класс реализует интерфейс. Тогда я буду ВЫНУЖДЕН реализовать все методы интерфейса. Зачем их было выносить? 2. Мой нормальный класс не реализует интерфейс. Тогда я не могу использовать методы интерфейса в реализованных методах (см. пункт 3 предыдущего списка). Таким образом создание "нормального" дкласса и объявление интерфейса ПРИНЦИПИАЛЬНО не могут заменить создание абстрактного класса. Это сообщение отредактировал(а) Mirkes - 2.9.2014, 13:50 -------------------- Mirkes |
|||
|
||||
Властелин |
|
|||
Новичок Профиль Группа: Участник Сообщений: 22 Регистрация: 21.9.2009 Репутация: нет Всего: нет |
Согласен полностью!
![]() И всё равно возникает вопрос: в чём преимущества абстрактного по сравнению с обычным (без использования интерфейса в нём). Вот есть базовый класс, есть методы, которые можем переопределить в производных. То что они неабстрактные и их можно вызвать и можно создать объект такого класса, то было бы как-то наивно из-за этих "недостатков" создавать абстрактный класс. Причина создания абстрактного класса должна быть, полагаю, более везкой, поэтому интересует, какая?
Это сообщение отредактировал(а) Властелин - 3.9.2014, 14:34 |
|||
|
||||
Mirkes |
|
|||
![]() Опытный ![]() ![]() Профиль Группа: Участник Сообщений: 586 Регистрация: 18.8.2011 Где: Красноярск Репутация: 7 Всего: 17 |
Предлагаю рассмотреть на полуконкретном примере - создание набора классификаторов. Я создал класс абстрактного классификатора и из него произвел кучу потомков. В абстрактном классе были реализованы многие методы с довольно навороченной логикой. Расписывать все - долго и нудно. Ограничимся несколькими методамиЖ Абстрактные modelFit формирует модель по конкретному набору данных oneCaseTest тестирует один пример Реализованные dataTest управляет формированием и тестированием многих моделей одного классификатора writeResult записывает результаты работы классификатора в стандартной форме. Теперь попытка ответа на вопрос. Есть два соображения. 1. В базовом классе - классе затычке - нам придется реализовывать все методы. При этом создание объектов базового класса не предполагается, поскольку затычка она и есть затычка. Если кто-то попытается создать объект базового класса, то он потом будет долго соображать, чего это там творится. Например, я реализовал в качестве тестирования примера выдачу всегда правильного ответа. Некий пользователь по недомыслию не поняв названия"АБСТРАКТНЫЙ" создал экземпляр базового класса и запустил его на тестирование. Потрясающий результат - 100% попадание в правильный ответ! Он сообщает заказчику, что задача решена идеально и ... 2. Все потомки должны переопределить абстрактные методы. В схеме с абстрактными классами за этим следить компилятор. А вот в схеме с базовыми классами - только программист. Причем если базовый класс разработан не им, а кем-то другим, то не совсем понятно, как он должен догадаться какие методы должны быть переопределены. Так что схема с абстрактными классами наиболее дружественна именно к программисту. -------------------- Mirkes |
|||
|
||||
Властелин |
|
|||
Новичок Профиль Группа: Участник Сообщений: 22 Регистрация: 21.9.2009 Репутация: нет Всего: нет |
Спасибо!
Пункт 2 показался наиболее убедительным. Дополню. На примере организации-подрядчика. Т.е. каждый класс (организация-подрядчик), реализовывая методы абстрактного класса, как и в случае интерфейса, как бы подписывает контракт с абстрактным классом, что обязуется выполнить все условия "контракта" (все условия заказчика по контракту) А также очень убедительным и наглядным примером применимости абстрактного класса считаю паттерн "Шаблонный метод"!
Это сообщение отредактировал(а) Властелин - 12.9.2014, 14:13 |
|||
|
||||
![]() ![]() ![]() |
Правила форума "Java" | |
|
Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic. |
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей) | |
0 Пользователей: | |
« Предыдущая тема | Java: Общие вопросы | Следующая тема » |
|
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности Powered by Invision Power Board(R) 1.3 © 2003 IPS, Inc. |