Модераторы: LSD, AntonSaburov
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Для чего нужно сокрытие данных? 
V
    Опции темы
Властелин
Дата 30.8.2014, 14:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 22
Регистрация: 21.9.2009

Репутация: нет
Всего: нет



Приветствую!

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

Еще самое распространённое объяснение (которое выражено здесь http://stackoverflow.com/a/20895152/1931613) цели сокрытия данных, в каком-то смысле продолжение процитированному из Википедии, что сокрытие используется с целью, что в большом проекте public поле будет видно где-то там и будет по ошибке использовано вместо другого поля, определённого с таким же именем. И опять непонятно как будет выглядеть на конкретном примере эта проблема ошибочного использования поля с именем, уже определённого ранее.

Ещё, считается, практической целью использования инкапсуляции является сокрытие поля от пользователя, т.е. чтобы введенное пользователем некорректное значение было обработанное в set-методе, прежде чем будет присвоено полю. 

Вот выше представлены два, повторюсь, "распространённых" довода, вынуждающих пользоваться инкапсуляцией. В рамках большого проекта, в котором, скажем, 1000 классов, в скольких классах с большего веско возникают две вышеописанных ситуации использования инкапсуляции? 1-ое: не представляю как выглядит в коде проблема, пример конфликта имен (snapshot wanted!) Например, 2-ое, используется считаю максимум в 50 классах (типа 50 окон интерфейса в проекте). И то в большинстве классов set-теры используются, представляя собой очень "тщательную" обработку поля типа 
Код

class Person {
    private name;
}

public void setName(String name) {
    this.name = name;
}


И аналогично в большинстве случаев такая же "тщательная" обработка в get-терах возвращения значения поля:
Код

public String getName() {
    return name;
}


А принято за правило использовать во всех классах get-теры и set-теры (на всякий случай, а вдруг!..), когда они не несут в большинстве случаев функциональной нагрузки, а лишь для "декорации", для "поддержания концептуальности". Это наводит на логический вывод, что в частности инкапсуляция, представленная каркасом, остовом, фундаментом ОО программирования на практике являет собой метод, к которому возникает необходимость прибегнуть лишь иногда! И возникает тот же вопрос, часто ли возникает необходимость использования наследования и полиморфизма?! Поэтому с теперешней перспективы (очень не исключаю что моё мнение может перемениться после убедительного опровержения моих доводов) не называл ООП концепцией, фундаментом, а лишь примочкой в добавок к структурному программированию и запихнул бы эти три "кита" куда подальше, например в шаблоны проектирования, где порождающий шаблон Синглтон являлся бы расширением порождающего шаблона Инкапсуляция  smile 

Спасибо!

P.S. К чему, кажется, засорять эфир, казалось бы, столь банальным, избитым, промусоленным многократно вопросом об инкапсуляции? Однако далеко не моментально удаётся найти толковый пример по вопросу, часто обсасываемому в теории. Как например, абстрактный класс, примеров использования предостаточно, но что-то не припомню источник с вменяемым объяснением, почему возникла необходимость его создания (хорошо, абстрактный класс не даёт создать его экземпляр, в чём "плюшка"?), хотя в природе существуют интерфейсы, который впихнул в базовый класс, а объявленные в нём методы переопределил в производном. В свою очередь возникает вопрос, зачем нужен интерфейс, кроме того как чтобы избежать конфликтов множественного наследования ( которые возникают С++)?

Это сообщение отредактировал(а) Властелин - 30.8.2014, 19:34
PM MAIL   Вверх
jManiak
Дата 31.8.2014, 19:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 130
Регистрация: 6.2.2007
Где: Санкт-Петербург

Репутация: нет
Всего: 1



С планшета, поэтому кратко:
Цитата

Внимание, вопрос: приведите распространённый коварный пример "повреждения свойств (класса) другим кодом"

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

В принципе, никто вас не заставляет этим пользоваться. 
Хотите, обращайтесь напрямую к полям.
Но если потом нужно будет скрыть это поле за методы, я не уверен, что рефракторинг(планшет предложил вариант 'рефрактор ног' smile) будет автоматизированным. Хотя может и ошибаюсь, не пробовал.

Просто все уже по привычке делают поля приватными и get/set методы к ним. Даже если это не нужно напрямую. Может потом понадобится. Ничего плохого я в этом не вижу. Опять же, среда разработки все это за вас сделает.

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

Абстрактные классы нужны. 
Например вы пишите обработку каких-то штук. Штуки бывают разные. 80%обработки для них одинакова. А 20 для каждого типа своя.
Пишется абстрактные класс с 80% логики доя всех, в которой вызываются абстрактные методы, которые еще не реализованы, и для каждого типа свой класс, в которых только реализуются эти абстрактные методы. Все.
Напрямую абстрактные класс никто не сможет использовать(помните про защиту от дурака?), зато есть набор конкретных классов с законченной функциональностью.

В структурном подходе это была бы у вас куча бооооольших if'ов, которые бы плохо читались.

Другое дело когда у народа начинает сносить крышу на почве шаблонов проектирования. И они для решения задачи в 20 строк начинают лепить 2 абстрактных класса, три фабрики и т.д.
Это печально, да.

Также помните, что ООП не для компьютера. Для него лучше набор ноликов и единичек. Вы пишите код, чтобы его еще и другие читали, изменяли. В том числе и для этого все эти плюшки.

Ищите везде компромисс, не нужно впадать в крайности ни в чем.

P.S.
Если есть опечакти, извиняюсь.

PM MAIL ICQ   Вверх
Mirkes
Дата 1.9.2014, 20:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



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

То, что вы назвали инкапсуляцией и ее основной целью находится после "а также".
Без инкапсуляции не было бы полей а работали бы вы с массивами и отдельными переменными. В лучшем случае со структурами (С) или записями (Паскаль).

Абстрактные классы мне лично экономят кучу времени по причинам прекрасно описанным в предыдущем топике.

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

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

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

Насчет совпадения имен - сколько угодно. Любому создаваемому объекту часто хочется дать имя. Как правило, это поле называешь name. Вроде никаких проблем. Позже я с удивлением обнаружил, что ряд классов, в которых я создал поле name имели предков с таким полем. У меня проблем не было, а без private - точно были бы.


--------------------
Mirkes
PM MAIL   Вверх
Властелин
  Дата 2.9.2014, 12:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 22
Регистрация: 21.9.2009

Репутация: нет
Всего: нет



Mirkes, спасибо за толковый достойный убедительный логичный обоснованный ответ! 

Абстрактные классы

В чём "плюшка" что нельзя создавать объекты абстрактных классов? Встречающиеся ответы что для поддержания концептуальности и "защиты от дурака" звучит как-то неубедительно...

Зачем было создавать абстрактный класс, если его можно сфарганить путём создания обычного класса и объявленный интерфейс в нём. Соответственно вопрос: кроме того что нельзя создать объект абстрактного класса, в чём ещё преимущества абстрактного класса по сравнению с предложенной мной) конструкцией класс+интерфейс?

Цитата(Mirkes @  1.9.2014,  20:05 Найти цитируемый пост)
Насчет примеров повреждения одним классом данных другого. Этого сколько угодно.
Рассмотрим, например, реализацию шрифта. Размер может быть указан двумя способами. Причем в зависимости от того, как указан размер, изменяется значение ряда внутренних переменных. Если делаете через сеттер - все нормально.Если залезаете и меняете ручками - можете потом долго искать причину развала, который произойдет, что самое печальное не сразу, а в какой-то отдаленный момент.

Выглядит очень убедительно, но боюсь не в силах сейчас вообразить "примеров повреждения одним классом данных другого" на практике:) Snapshot wanted!) Начну код, который бы показывал пример повреждения одним классом другого, просьба продолжить\изменить) чтобы в примере были очевидны проблемы "в зависимости от того, как указан размер" и "Если залезаете и меняете ручками - можете потом долго искать причину развала"
...
Код

class Font {
...
    protected int size;
}
...

class MobileFont extends Font {
...
    // так понимаю если я хочу в производном классе тоже использовать переменную size, то благодаря наследованию её не надо объявлять она будет     //отнаследована от базового класса
}
...

class TabletFont extends Font {
...
    //
}
...

class MainScreen {
...
    private final SCREEN_SIZE = 4;
    private final FONT_SIZE_MOBILE = 15;
    private final FONT_SIZE_TABLET = 18;
...
    void changeFont(int screenSizeInches, Font font) {

        if (mobileScreenSizeInches < SCREEN_SIZE) {
            font.size = FONT_SIZE_MOBILE;
        } else {
            font.size = FONT_SIZE_TABLET;
        }
    }
...
}


И пожалуйста пример для (а то воображалка не соображает):
Цитата(Mirkes @  1.9.2014,  20:05 Найти цитируемый пост)
Насчет совпадения имен - сколько угодно. Любому создаваемому объекту часто хочется дать имя. Как правило, это поле называешь name. Вроде никаких проблем. Позже я с удивлением обнаружил, что ряд классов, в которых я создал поле name имели предков с таким полем.



Это сообщение отредактировал(а) Властелин - 2.9.2014, 13:06
PM MAIL   Вверх
Mirkes
Дата 2.9.2014, 13:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Ок. Пример про повреждение. Причем пример реальный.
Я подготовил серию апплетов по методам анализа данных. Апплеты используют визуализацию всего, что происходит. Речь идет о точках на плоскости. При всех вычислениях используются нормальные координаты точек x,y. Теперь про отображение. Есть несколько типов точек и они имеют разный размер. Очевидно, что для точки x,y координаты расположения объекта, который ее показывает вычисляются по тривиальному правилу:
xPlain = x-size/2;
yPlain = y-size/2;
Это преобразование запрятано в сеттер. Обратное преобразование в геттер.
Пока я не закрыл полностью доступ к xPlain и yPlain я несколько раз разыскивал ошибки, возникшие из желания сэкономить и вместо вызова сеттера прямо присвоить значения.

Цитата(Властелин @  2.9.2014,  12:44 Найти цитируемый пост)
И пожалуйста пример для (а то воображалка не соображает):
Цитата(Mirkes @  1.9.2014,  20:05 )
Насчет совпадения имен - сколько угодно. Любому создаваемому объекту часто хочется дать имя. Как правило, это поле называешь name. Вроде никаких проблем. Позже я с удивлением обнаружил, что ряд классов, в которых я создал поле name имели предков с таким полем.


Вообще-то это и есть пример  smile 

Забыл про абстрактные классы.
Цитата(Властелин @  2.9.2014,  12:44 Найти цитируемый пост)
В чём "плюшка" что нельзя создавать объекты абстрактных классов? Встречающиеся ответы что для поддержания концептуальности и "защиты от дурака" звучит как-то неубедительно...


Пойдем по пунктам.
1. В абстрактном классе как правило часть методов не реализована а только объявлена.
2. Когда я создаю абстрактный классификатор, я предусматриваю в нем множество методов, которые одинаковы для всех классификаторов и реализую. Так же я предусматриваю методы, которые для каждого классификатора уникальны, объявляю эти методы и не реализую. Это абстрактные методы.
3. Вызовы абстрактных методов используются в реализованных методах.

Ответы на вопросы автора поста.
Почему нельзя создавать объекты абстрактного класса? (в чем плюшка)
Это не плюшка, а вынужденная мера, поскольку при попытке работы с созданным объектом абстрактного класса получим облом не вызове абстрактного метода. Так что это таки защита от дурака!

Почему нельзя создать нормальный класс и не вынести все методы в интерфейс.
Не совсем понял идею. Если я создал нормальный класс. а все абстрактное вынес в интерфейс, то возможно два варианта:
1. Мой нормальный класс реализует интерфейс. Тогда я буду ВЫНУЖДЕН реализовать все методы интерфейса. Зачем их было выносить?
2. Мой нормальный класс не реализует интерфейс. Тогда я не могу использовать методы интерфейса в реализованных методах (см. пункт 3 предыдущего списка).
Таким образом создание "нормального" дкласса и объявление интерфейса ПРИНЦИПИАЛЬНО не могут заменить создание абстрактного класса.



Это сообщение отредактировал(а) Mirkes - 2.9.2014, 13:50


--------------------
Mirkes
PM MAIL   Вверх
Властелин
Дата 3.9.2014, 14:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 22
Регистрация: 21.9.2009

Репутация: нет
Всего: нет



Согласен полностью! smile 

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

Код

class Game {

protected /*abstract*/ void startGame()/*;*/ {}
protected /*abstract*/ void endGame()/*;*/ {}

public void play() {
    startGame();
    endGame();
}
}

class Chess /* a-la abstract */ extends Game {

public  void startGame() { print("Start chess"); }
public  void endGame(){ print("Finish chess"); }

}

class GamePlay {

   public void main() {
    ...
   Game game = new Chess();
   game.play();
}
 
}


Это сообщение отредактировал(а) Властелин - 3.9.2014, 14:34
PM MAIL   Вверх
Mirkes
Дата 4.9.2014, 19:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 586
Регистрация: 18.8.2011
Где: Красноярск

Репутация: 7
Всего: 17



Цитата(Властелин @  3.9.2014,  14:18 Найти цитируемый пост)
И всё равно возникает вопрос: в чём преимущества абстрактного по сравнению с обычным (без использования интерфейса в нём). Вот есть базовый класс, есть методы, которые можем переопределить в производных. То что они неабстрактные и их можно вызвать и можно создать объект такого класса, то было бы как-то наивно из-за этих "недостатков" создавать абстрактный класс. Причина создания абстрактного класса должна быть, полагаю, более везкой, поэтому интересует, какая?


Предлагаю рассмотреть на полуконкретном примере - создание набора классификаторов. Я создал класс абстрактного классификатора и из него произвел кучу потомков. В абстрактном классе были реализованы многие методы с довольно навороченной логикой. Расписывать все - долго и нудно. Ограничимся несколькими методамиЖ

Абстрактные
modelFit формирует модель по конкретному набору данных
oneCaseTest  тестирует один пример

Реализованные
dataTest управляет формированием и тестированием многих моделей одного классификатора
writeResult записывает результаты работы классификатора в стандартной форме.

Теперь попытка ответа на вопрос. Есть два соображения.
1. В базовом классе - классе затычке - нам придется реализовывать все методы. При этом создание объектов базового класса не предполагается, поскольку затычка она и есть затычка. Если кто-то попытается создать объект базового класса, то он потом будет долго соображать, чего это там творится. Например, я реализовал в качестве тестирования примера выдачу всегда правильного ответа. Некий пользователь по недомыслию не поняв названия"АБСТРАКТНЫЙ" создал экземпляр базового класса и запустил его на тестирование. Потрясающий результат - 100% попадание в правильный ответ! Он сообщает заказчику, что задача решена идеально и ...

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

Так что схема с абстрактными классами наиболее дружественна именно к программисту.


--------------------
Mirkes
PM MAIL   Вверх
Властелин
Дата 12.9.2014, 14:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 22
Регистрация: 21.9.2009

Репутация: нет
Всего: нет



Спасибо!

Пункт 2 показался наиболее убедительным. Дополню. На примере организации-подрядчика. Т.е. каждый класс (организация-подрядчик), реализовывая методы абстрактного класса, как и в случае интерфейса, как бы подписывает контракт с абстрактным классом, что обязуется выполнить все условия "контракта" (все условия заказчика по контракту)

А также очень убедительным и наглядным примером применимости абстрактного класса считаю паттерн "Шаблонный метод"! 

Код

    abstract class Game {

        abstract void start();
        abstract void pause();
        abstract void finish();
        
        void play() {
            
            start();
            pause();
            finish();
        }
    }
    
    class Chess extends Game {
        
        void start() { print("Начать игру в шахматы"); }
        void pause() { print("Приостановить игру в шахматы"); }
        void finish() { print("Закончить игру в шахматы"); }
    }

    class PlayGame {

        void MainMenu() {

           // из меню выбираем игру
           Game game = new Chess();
           game.play();
        }
    }


Это сообщение отредактировал(а) Властелин - 12.9.2014, 14:13
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0752 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.