LINUX.ORG.RU

[OOP] Инкапсуляция — а оно вообще надо?

 


0

0

Писал я тут программу одну, и нужны были там объекты со свойствами, которые нужно read-write. Начал я по привычке делать геттеры-сеттеры, и посетила меня мысль, а нафига, собственно? Удалил я геттеры-сеттеры и сделал все свойства public-ом и проблем не обнаружил вообще никаких.

Объясните, нафига нужна инкапсуляция? Не лучше документацию писать, чем код от мнимого дурака защищать?

Вот с тем, что приходится воротить дикие костыли, когда вдруг в будущем оказывается, что нужна private переменная, а код модифицировать нельзя, я сталкивался.

★★★★★

Нужна, но как и все в меру, если стороннее изменение состояния объекта не приведет к ошибкам, то пожалуйста.

wfrr ★★☆
()

IIUC, геттеры-сеттеры - это для того, чтобы API класса не менялся, если понадобиться заменить поле чем-то сложным/вычислимым. К инкапсуляции это особого отношения не имеет.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

В том же D можно заменить

int x;

на

int x() {
   return ...; // get value
}

void x(int v) {
   // set value
}

И остальной код будет работать.

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

Проблемы могут быть, если берётся адрес от свойства, но это уже другое дело.

Legioner ★★★★★
() автор топика
Ответ на: комментарий от wfrr

> если стороннее изменение состояния объекта не приведет к ошибкам, то пожалуйста.

Конкретно в моём случае практически все объекты были immutable, вида

class Something {
   public const int x;
   puvlic const int y;

   public this(int x, int y) {
       this.x = x;
       this.y = y;
   }
}

(изначально были геттеры).

Legioner ★★★★★
() автор топика
Ответ на: комментарий от Legioner

Спасибо, капитан Очевидность :D

Пойнт в том, чтобы менять не пришлось вообще ничего. Ну и в том, чтобы сохранился не только API, но и ABI.

А инкапсуляция нужна, чтобы API-контракт было невозможно нарушить.

tailgunner ★★★★★
()

ответ уже дан klalafuda, тему можно закрывать.

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

dilmah ★★★★★
()
Ответ на: комментарий от wfrr

> реализацию оборачивать в объект Implement.

Это специфический Си++-костыль.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

> Пойнт в том, чтобы менять не пришлось вообще ничего.

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

Про ABI, это понятно, нужно подумать.

> А инкапсуляция нужна, чтобы API-контракт было невозможно нарушить.

Имхо, правильнее делать так, чтобы его было затруднительно нарушить непреднамеренно, но было легко нарушить намеренно (если я знаю, что делаю).

Legioner ★★★★★
() автор топика

>Объясните, нафига нужна инкапсуляция?

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

Рано или поздно приходится менять структуру и/или делать рефакторинг. И тут начинается обратная лихорадочная замена properties на methods :)

>Не лучше документацию писать, чем код от мнимого дурака защищать?

Проблема в том, что этим самым дураком с таким подходом начинаешь ощущать себя сам, занимаясь перестройкой сотен килобайт кода через год :D

KRoN73 ★★★★★
()
Ответ на: комментарий от Legioner

> Пойнт моего вопроса в том, что лучше, иметь килостроки ничего не делающего кода в виде геттеров-сеттеров и возможность вносить в них изменения

Нужно придумать для них нормальный синтаксис (как property в Delphi, например ;)), и никаких проблем.

> правильнее делать так, чтобы его было затруднительно нарушить непреднамеренно, но было легко нарушить намеренно

ИМХО, это невозможно.

tailgunner ★★★★★
()

ООП - это не надо. А инкапсуляция бывает полезна по вышеизложенным причинам.

Miguel ★★★★★
()
Ответ на: комментарий от tailgunner

> Ну и в том, чтобы сохранился не только API, но и ABI.

<troll_mode>Зачем сохранять ABI? Можно просто перекомпилировать всё что требуется и всё заработает.</troll_mode> ;)

naryl ★★★★★
()

Инкапсуляция в общем случае позволяет уменьшить число связей и ослабить существующие связи между частями кода. И в общем случае это хорошо.

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

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

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

gzh
()

То, о чем ты говоришь, не инкапсуляция, а защита данных. В данном приеме она основана на инкапсуляции, но прямого отношения к ней не имеет.

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

Раньше такое делалось на уровне договоренностей и документации, но сегодня доки никто не читает, комментарии никто не пишет, все лабают код в IDE и сходу дебажат. Соттветственно, нужны соответствующие инструменты ;)

Die-Hard ★★★★★
()
Ответ на: комментарий от alex_custov

>гг, при изменении ABI, скажем, Qt 4.3 <=> 4.4, будешь всё перкомпилировать? :)

даже без смены ABI (и какая смена ABI у прикладных библиотек, если компилятор тотже?), при смене Qt 4.3 <=> 4.4, придеться перекомпилять все, что использует систему плагинов Qt (http://doc.trolltech.com/4.4/plugins-howto.html#the-build-key)

ale ★★
()
Ответ на: комментарий от ale

>даже без смены ABI (и какая смена ABI у прикладных библиотек, если компилятор тотже?)

В С++ любая правка декларации класса - это изменение ABI. Поэтому чтобы добиться ABI в С++ надо делать все на геттерах-сеттерах и использовать pimpl.

Absurd ★★★
()
Ответ на: комментарий от kto_tama

> ненавижу геттеры-сеттеры

Их никто не любит.

> маразм какой-то :-)

Они могут быть оправданы.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>> В С++ любая правка декларации класса - это изменение ABI

>"Поздравляем вас, гражданин, соврамши" (c)

А по существу чего-нибудь будет? Размер объекта, и смещения полей будут другие - придется все пересобирать.

Absurd ★★★
()
Ответ на: комментарий от Absurd

>>> В С++ любая правка декларации класса - это изменение ABI

>>"Поздравляем вас, гражданин, соврамши" (c)

>А по существу чего-нибудь будет?

Не любая правка декларации.

> Размер объекта

Отнюдь не любая правка к этому приводит.

> и смещения полей будут другие - придется все пересобирать.

IIRC, смещения private-полей никого не интересуют, кроме самого класса; то же относится к их типам. Пока размер класса сохраняется, в private-части можно резвиться невозбранно.

tailgunner ★★★★★
()
Ответ на: комментарий от tailgunner

>смещения private-полей никого не интересуют, кроме самого класса; то же относится к их типам. Пока размер класса сохраняется, в private-части можно резвиться невозбранно.

А как можно баловаться в private секции класса, не изменяя его размер?

Absurd ★★★
()
Ответ на: комментарий от Absurd

>> смещения private-полей никого не интересуют, кроме самого класса; то же относится к их типам. Пока размер класса сохраняется, в private-части можно резвиться невозбранно.

> А как можно баловаться в private секции класса, не изменяя его размер?

Для начала - в private-секции описаны еще и методы (для простоты - невиртуальные), которые можно корежить как угодно. Дальше - переименование поля вполне подходит на роль "любой правки декларации класса", а оно ни на что не влияет. Можно менять типы полей-указателей, менять константность (полей, про аргументы методов и сами методы я уже сказал), менять float на int и обратно, в общем - есть определенные типы правок, которые не ломают ABI.

tailgunner ★★★★★
()
Ответ на: комментарий от Absurd

Заменить набор переменнх массивом того же размера. Добавлять битовые поля, пока есть место. Объеденять переменные в структуры.

anonymous
()
Ответ на: комментарий от Miguel

> ООП - это не надо.

Лол, из твоего быдлохаскеля уже выкинули классы типов и их наследование?

anonymous
()
Ответ на: комментарий от kto_tama

>ненавижу геттеры-сеттеры

>маразм какой-то :-)

Зато геттер может легко потом переопределяться комплексным методом. Или, наоборот, комплексный геттер преопределяться дёргающим переменную. У меня такое сплошь и рядом и в Java, и в PHP.

KRoN73 ★★★★★
()

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

smh ★★★
()

Это если язык/VM убоги, то всякие там геттеры-сеттеры позволяют, например, при желании логгировать все изменения поля, сериализовать структуры через рефлексию, и т.п. Если же есть достаточная гибкость, чтоб доступ к полю синтаксически не отличался от доступа к геттеру/сеттеру, то нуегонафиг, применять только по необходимости.

anonymous
()
Ответ на: комментарий от anonymous

> Лол, из твоего быдлохаскеля уже выкинули классы типов и их наследование?

Ха-ха. А теперь пойми, быдло, что информация о классах типов является совершенно отдельной и от значений и от типов. В отличие от ООП-языков.

Miguel ★★★★★
()
Ответ на: комментарий от tailgunner

>Для начала - в private-секции описаны еще и методы (для простоты - невиртуальные), которые можно корежить как угодно. Дальше - переименование поля вполне подходит на роль "любой правки декларации класса", а оно ни на что не влияет. Можно менять типы полей-указателей, менять константность (полей, про аргументы методов и сами методы я уже сказал), менять float на int и обратно, в общем - есть определенные типы правок, которые не ломают ABI.

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

Absurd ★★★
()

если в команде 2-4 человека, то оно разумеется нафиг не нужно. вся фишка в том, что если разработчиков куча, то возникает мания глючености и глобальное недоверие к (быдло)коду остальных. типо "ну я вот думал что это можно свободно вызывать/изменять".

ЗЫ private - это не совсем инкапсуляция, это та часть, ну которую чаще всего указывают

generatorglukoff ★★
()

за понимание инкапсуляции в виде набора геттеров/сеттеров программистам надо отрывать три и более конечностей. а за подобную трактовку в учебных курсах (в частности в книгах по ООП-языкам) - распинать вниз головой. чтоб другим неповадно было

jtootf ★★★★★
()

Legioner, вот закончишь ВУЗ, поработаешь год-два программистом, поймешь зачем нужны геттеры и сеттеры, ну или никогда не поймешь, если мозгом слаб. Их придумали не такие сопляки как ты, и видимо придумали не просто так, а ты вместо того, что-бы сопли распускать "с геттерами кодить больше, фу нафик, сложна", лучше бы молчал и пользовался опытом старший поколений.

fmj
()
Ответ на: комментарий от Pi

> ты даже не представляешь, как сморозил :)

Ну обьясни, о святоч истины, как же я сморозил

fmj
()
Ответ на: комментарий от fmj

> Legioner, вот закончишь ВУЗ, поработаешь год-два программистом, поймешь зачем нужны геттеры и сеттеры, ну или никогда не поймешь, если мозгом слаб.

Getters and setters are evil. Evil, evil, I say! Python objects are not Java beans. Do not write getters and setters. This is what the 'property' built-in is for. And do not take that to mean that you should write getters and setters, and then wrap them in 'property'. That means that until you prove that you need anything more than a simple attribute access, !!!don't write getters and setters!!!. They are a waste of CPU time, but more important, they are a waste of programmer time. Not just for the people writing the code and tests, but for the people who have to read and understand them as well.

In Java, you have to use getters and setters because using public fields gives you no opportunity to go back and change your mind later to using getters and setters. So in Java, you might as well get the chore out of the way up front. In Python, this is silly, because you can start with a normal attribute and change your mind at any time, without affecting any clients of the class. So, don't write getters and setters.

(C)

То бишь, геттеры и сеттеры -- это не более, чем костыли в убогих языках.

eugine_kosenko ★★★
()
Ответ на: комментарий от eugine_kosenko

Речь идет в контексте C++-а / прочего языка, у когорого нельзя написать

@property(readonly) NSInteger m_id;

А в этом контексте, геттеры/сеттеры нужны. C++ это не Python, идиот! Без них мы получаем нарушение принципов ООП, которое само по себе с технической точки зрения может и не так уж и страшно (поначалу), но обычно является началом хаоса в проекте. Если вы из тех, кто пишет по принципу "я напишу `правильно', а остальных в свой код все равно не пущу, так что пишу как хочу, я умный, мне можно и нарушить парадигму", -- то подумайте, много ли вы один напишете?

P.S. Да про waste CPU time тоже гон, а про programmers time -- ну так для написания, можно пару макросов в <любимый текстовый редактор> вставить, а прочтение -- при соблюдении установленных правил оформления кода (и при их наличии), геттеры/сеттеры вообще незметны, -- видишь краем глаза массив сеттеров/геттеров, пропускаешь его, и все, -- никаких проблем.

fmj
()
Ответ на: комментарий от fmj

> Речь идет в контексте C++-а / прочего языка, у когорого нельзя написать

Я так и написал: костыль для убогих языков.

> C++ это не Python, идиот!

Зачем так много эмоций? Или Вы это только что поняли?

> то подумайте, много ли вы один напишете?

Фокус в том, что одни языки позволяют безболезненно сменить реализацию постимплементум, а другие -- нет. Поэтому в одних языках хаос намного болезненнее воспринимается, чем в других.

> Да про waste CPU time тоже гон

Для Питона -- отчасти нет.

> видишь краем глаза массив сеттеров/геттеров, пропускаешь его, и все, -- никаких проблем.

Для пропуска тоже нужно время, не говоря уже о том, что с водой можно пропустить и ребенка.

eugine_kosenko ★★★
()
Ответ на: комментарий от eugine_kosenko

> > Речь идет в контексте C++-а / прочего языка, у когорого нельзя написать > Я так и написал: костыль для убогих языков.

> > C++ это не Python, идиот!

> Зачем так много эмоций? Или Вы это только что поняли?

Затем, что да, костыль, но раз речь шла в контексте C++, то не надо замахиватся сюда Python-ом -- мимо кассы ваши выпадки.

> Фокус в том, что одни языки позволяют безболезненно сменить реализацию постимплементум, а другие -- нет. Поэтому в одних языках хаос намного болезненнее воспринимается, чем в других.

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

> > Да про waste CPU time тоже гон

> Для Питона -- отчасти нет.

Вот видите, и у Python-а есть слабые стороны :)

> Для пропуска тоже нужно время, не говоря уже о том, что с водой можно пропустить и ребенка.

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

fmj
()
Ответ на: комментарий от fmj

По поводу C++-а, таблетка лечащая от необходимости писать
геттеры/сеттеры ручками, хотя и использует макросы, выглядит очень просто:

#define PROPERTY(type, name)					\
	private:						\
		type m_##name;					\
	public:							\
		const type &get##name()const {return m_##name;} 		\
		void set##name(const type &val) { m_##name = val; }	\
	private:	

И вместо декларации 

int m_a;
int m_b;

просто пишем

PROPERTY(int, A);
PROPERTY(int, B);

и пользуемся свежеполученными методам getA/setA/getB/setB


fmj
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.