Чи може перейменування методу зберегти інкапсуляцію?


9

Я читав цю сторінку про те, коли геттери / сетери виправдані, і ОП дав такий зразок коду:

class Fridge
{
     int cheese;

     void set_cheese(int _cheese) { cheese = _cheese; }
     int get_cheese() { return cheese; }
 }

void go_shopping(Fridge fridge)
{
     fridge.set_cheese(fridge.get_cheese() + 5);        
}

У прийнятій відповіді зазначено:

До речі, в вашому прикладі, я дав би клас і методи, замість і . Тоді у вас все-таки буде інкапсуляція.FridgeputCheese()takeCheese()get_cheese()set_cheese()

Як зберігається інкапсуляція, перейменувавши її з get / set на putCheese()/ takeCheese()Ви очевидно отримуєте / встановлюєте значення, так чому б просто не залишити його як get / set?

У цій же відповіді також зазначено:

Маючи геттерів та сеттерів, само по собі не порушує інкапсуляцію. Що означає порушення інкапсуляції, це автоматично додавання геттера та сеттера для кожного члена даних (у кожному полі, в java lingo), не задумуючись про це.

У цьому випадку у нас є лише одна змінна, cheeseі ви можете взяти та повернути сир у холодильник, тому пара get / set у цьому випадку виправдана.


7
putCheeseдодав би сир у холодильник і takeCheeseвидалив би його - це (більш високі рівні) доменно-орієнтовані абстракції, а не об’єкти гетерів та сеттерів (які є (абстракції низького рівня) комп'ютерного програмування).
Ерік Ейдт

Відповіді:


17

Я думаю, що ви пропускаєте суть. Це не говорить про те, що ви повинні перейменовувати сетер та геттер, але мати методи, які додають та виймають предмети з холодильника. тобто

public class Fridge
{
    private int numberOfCheeseSlices;

    public void AddCheeseSlices(int n)
    {
         if(n < 0) { 
             throw new Exception("you cant add negative cheese!");
         }
         if(numberOfCheeseSlices + n > this.capacityOfFridge) { 
             throw new Exception("TOO MUCH CHEESE!!");
         }
         ..etc
         this.numberOfCheeseSlices += n;
    }
}

Тепер приватна змінна інкапсульована. Я не можу просто встановити його на все, що хочу, можу лише додати та вийняти шматочки сиру з холодильника методами, які, у свою чергу, гарантують, що будь-які правила ділової логіки я хочу застосовувати.


2
Правильно, але ви все одно можете залишити його, як setCheese()мати ту саму логіку в сетері. Для мене все одно ви намагаєтесь приховати той факт, що використовуєте сеттер, перейменувавши метод, але ваш очевидно дістає / налаштовує щось.
QueenSvetlana

21
@QueenSvetlana це не сетер. Це операція. Ви говорите холодильнику "ось ще один сир", і він додає його до свого внутрішнього, капсульованого кількості сирів. Сетер сказав би: "у вас зараз 5 сирів". Це функціонально різні операції, а не просто різні назви.
Ant Ant

1
Ви можете назвати це setCeese, але це буде заплутано, оскільки він не встановить значення сиру.
Еван

6
Зверніть увагу, тут є ціла помилка переповнення. Якщо сиру вже більше 0, AddCheese(Integer.MAX_VALUE)слід вийти з ладу, але не буде.
користувач253751

3
що б загалом було висвітлено у розділі
Ewan

5

Геттери і сетери порушують інкапсуляцію кожен раз , за визначенням. Що може стверджувати, що іноді ми повинні зробити це. З цим не виходить, ось мої відповіді:

Як інкапсуляція зберігається, перейменувавши її з get / set у putCheese () / takeCheese () Ви, очевидно, отримуєте / встановлюєте значення, так чому б не просто залишити його як get / set?

Різниця полягає в семантиці, тобто значенні того, що ви пишете. Інкапсуляція - це не лише захист, але приховування внутрішнього стану. Внутрішній стан навіть не повинен бути відомий зовні, натомість очікується, що об'єкт пропонує відповідні для бізнесу методи (іноді їх називають "поведінка") для маніпулювання / використання цього стану.

Отже, getі setце технічні терміни і, здається, не мають нічого спільного з доменом "холодильник", хоча putі takeможуть насправді мати певний сенс.

Однак те, putчи все-таки takeмає фактичний сенс, все ще залежить від вимог і об'єктивно не можна судити про це. Дивіться наступний пункт:

У цьому випадку у нас є лише одна змінна, сирна, і ви можете взяти і повернути сир в холодильник, тому пара отримання / встановлення в цьому випадку виправдана.

Це неможливо об'єктивно визначити без більшого контексту. Ваш приклад містить go_shopping()метод десь в іншому місці. Якщо це те , що Fridgeдля, ніж це НЕ потрібно getабо те set, що вона потребує Fridge.go_shopping(). Таким чином, у вас є метод, який випливає з вимог, всі "дані", які йому потрібні, є локальними, і ви маєте фактичну поведінку замість просто тонко завуальованої структури даних.

Пам'ятайте, ви не будуєте загального, багаторазового використання Fridge. Ви будуєте лише Fridgeсвої вимоги. Будь-які зусилля, витрачені на те, щоб зробити це більше, ніж потрібно, насправді марнотратні.


10
Getters and setters break encapsulation every single time- Це трохи занадто сильно сформульовано на мій смак. Методи (включаючи геттерів та сетерів) мають те саме призначення, що й ворота на концерті: вони дозволяють впорядковувати вхід та вихід із місця проведення.
Роберт Харві

8
"Геттери та сетери порушують інкапсуляцію кожен раз, за ​​визначенням" - за яким визначенням? У мене теж виникає проблема з цим, оскільки геттери та сетери - це лише частина публічного інтерфейсу, як і будь-який інший громадський член; вони порушують інкапсуляцію лише в тому випадку, якщо вони розкривають деталі реалізації, які вважаються внутрішніми за дизайном (тобто за рішенням програміста (або команди)). Той факт, що геттери і сетери часто додаються випадково, при цьому управління зчепленням є поміркованим, цілком інша справа.
Філіп Мілованович

2
@ FilipMilovanović Справедливий пункт. Як я згадую у відповіді, "інкапсуляція" використовується для приховування внутрішніх об'єктів (== об'єкт стану == змінні екземпляра). Геттери і сетери публікують внутрішні об'єкти. Тому за цими визначеннями вони перебувають у вічному конфлікті. Тепер, чи інколи нам потрібно порушувати інкапсуляцію - це інша справа, яку слід вирішувати окремо. Щоб було зрозуміло, кажучи, що сетери / геттери завжди порушують інкапсуляцію, я не кажу, що це завжди "неправильно", якщо це допомагає.
Роберт

5
геттери і сетери не «порушують інкапсуляцію буквально завжди». Геттери і сетери часто навіть не посилаються на членів, що знаходяться під ними, виконують якусь операцію або є лише виключно визначеними, у вас може бути тільки геттер або може бути лише сетер. Це порушує інкапсуляцію, коли геттери та сетери не роблять нічого іншого, ніж доступ для членів і обидва визначені для одних і тих же полів. Навіть це може знадобитися для керівників бібліотек, які не хочуть порушувати ABI / API з внутрішніми змінами та уникати перекомпіляції клієнтів.
WHN

4
Гаразд, геттери та сетери порушують інкапсуляцію лише 99% часу. Щасливі? І, виставляючи тип інкапсульованого об'єкта, вони безумовно порушують інкапсуляцію. Після того, як у вас public int getFoo()це практично неможливо, змінити на інший тип практично неможливо.
user949300

3

Майже все це свідчить про принципове непорозуміння інкапсуляції та те, як воно застосовується.

Початкова відповідь, що ви порушили інкапсуляцію, - просто неправильна. У вашій програмі може виникнути потреба просто встановити значення сиру в холодильнику замість збільшення / зменшення або додавання / видалення. Крім того, це не семантика, незалежно від того, як ви її називали, якщо у вас є необхідність доступу та / або зміни атрибутів, ви не порушуєте інкапсуляцію, надаючи їх. Нарешті, інкапсуляція насправді не є "приховуванням", це контролем доступу до стану та цінностей, які не повинні бути публічними або маніпулювати поза класом, надаючи його тим, хто повинен та виконує завдання, доступні всередині країни.

Геттер або сетер не порушує інкапсуляцію, коли є законна потреба отримати або встановити значення. Ось чому методи можуть бути оприлюднені.

Інкапсуляція стосується збереження даних та методів, що змінюють ці дані безпосередньо разом в одному логічному місці, класі.

У цьому конкретному випадку явно є необхідність змінити значення сиру в додатку. Незалежно від того, як це робиться, за допомогою get / set або add / remove, якщо методи інкапсульовані у класі, який ви дотримуєтесь об'єктно-орієнтованого стилю.

Для уточнення я наведу приклад того, як інкапсуляція порушена шляхом надання доступу незалежно від назви методу чи логічного виконання.

Скажімо, ваш холодильник має "термін експлуатації", лише кількість кліщів до того, як холодильник більше не працює (заради аргументу, холодильник не може бути відремонтований). Логічно немає можливості користувачеві (або решті вашої програми) мати можливість змінити це значення. Це має бути приватним. Це було б видно лише через інший атрибут, відомий як "isWorking". Коли термін експлуатації закінчується, внутрішньо холодильник встановлює функцію "Налаштування".

Виконання відліку часу життя та його перемикання перемикача isWorking - все внутрішнє для холодильника, нічого зовнішнього не могло / не могло би вплинути на процес. isWorking має бути видно лише, таким чином геттер не порушує інкапсуляцію. Однак додавання аксесуарів до елементів життєвого процесу порушить вашу інкапсуляцію.

Як і більшість речей, визначення інкапсуляції не є буквальним, воно є відносним. Чи маєте ви змогу побачити X поза класом? Чи зможете ви змінити Y? Чи є все, що стосується вашого об'єкта тут, у цьому класі або функціонал розподілений у кількох класах?


Давайте розглянемо це прагматично. Ви говорите, що інкапсуляція не порушена, якщо код призначений саме так, або якщо є "законна" причина. Це в основному означає, що ми ніколи не можемо сказати, що інкапсуляція порушена, тому що розробник повинен лише сказати, що вона "задумала" таким чином, або була "законна" причина. Тож це визначення тоді взагалі марне, чи не так?
Роберт

Ні, я кажу, що капсулювання не порушується просто шляхом надання доступу. І це не має нічого спільного з тим, що каже програміст, але якими є потреби програми. Додаток повинен мати доступ для маніпулювання об'єктами, інкапсуляція - це контроль доступу. У програмі може знадобитися "встановити" атрибут об'єкта, але логіка та / або обчислення, необхідні для виконання цієї операції "встановити", повинні бути інкапсульовані в клас об'єктів.
користувач343330

Я думаю, що тут ми принципово відрізняємось, ви говорите: "Програмі може знадобитися" встановити "атрибут об'єкта ...". Я не думаю, що так. Вибір дизайну, який передбачає встановлення або отримання атрибута, залежить від розробника. Це вибір! Існує маса способів кодування чогось, не всі з них передбачають отримання або встановлення змінних значень екземпляра.
Роберт

Може бути ... Зазвичай дизайн базується на задоволенні потреби, чи обирає програміст виходячи з бізнес-середовища. І в будь-якому випадку, якщо у програми є принципова потреба маніпулювати значенням, яке є частиною об'єкта, то вам доведеться певною мірою зробити цю функціональність доступною. Надання точки входу для роботи не порушує інкапсуляцію. Візьміть додаток для продажу. У якийсь момент транзакція повинна нарахувати податок, якщо це зробить об’єкт транзакції, він буде інкапсульованим, але додаток повинен мати змогу констатувати транзакцію.
CalcTax

Встановники є поганими прикладами через їх простоту, думайте з точки зору функції, яка робить набагато більше роботи, що призводить до того, що атрибут об'єкта оновлюється проти чогось поза класом, який виконує роботу, а потім каже object.attribute = x. Перша - це хороша капсуляція при забезпеченні відповідного доступу, друга - ні. Що стосується гетерів, чи потрібно додатку знати саме цей фрагмент об'єкта, щоб зробити щось інше, що не може міститися в об'єктному класі? якщо так, викриття не порушує вашу інкапсуляцію. якщо ні, то інкапсулюйте його в клас об’єктів
user343330

1

Це не просто перейменування методу. Два методи функціонують по-різному.

(Малюйте це на увазі)

get_cheese і set_cheese виставляє сир. putCheese () і takeCheese () зберігає сир прихованим і піклується про управління ним і дає користувачеві спосіб поводитися з ним. Спостерігач не бачить сиру, він бачить лише два способи маніпулювання ним.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.