Чи є вагомі причини зробити чисті функції непублічними?


25

У мене були невеликі дебати з колегою. Простіше кажучи, чи є вагомі причини приховувати / інкапсулювати чисті функції?

Під "чистим" я маю на увазі визначення вікіпедії :

  • Завжди повертає однакові результати з одного входу. (Заради цього обговорення Foo Create(){ return new Foo(); }вважається нечистим, якщо Fooне має значення значення семантики.)
  • Не використовує змінний стан (крім локальних змінних) або I / O.
  • Не викликає побічних ефектів.

26
Може бути зроблений аргумент взагалі не робити такого функціонального члена класу.
Пітер Б

6
@ PieterB - дійсно, хоча не всі мови підтримують це, а деякі мови дозволяють використовувати модуль, навіть для вільних функцій.
Теластин

3
Яка ваша думка? Я не думаю, що чистота функції пов'язана з тим, чи належить вона до публічного API.
Андрес Ф.

@andresF. - дискусія фактично полягала у тому, чи слід оприлюднювати правила про необмежену перевірку. Я висловив аргумент, що оскільки це була чиста функція, шкоди було мало. У цьому конкретному випадку він допоміг перевірку і, ймовірно, буде використаний повторно. Це питання стосується того, наскільки широко цей аргумент може / повинен застосовуватися.
Теластин

2
@Telastyn Якщо це багаторазове використання, але воно не є частиною відповідальності поточного класу, воно, ймовірно, має бути в окремому класі / модулі / що б не було вашою мовою. Тоді, будучи частиною відповідальності нового класу / модуля, це було б обов'язково оприлюднено. Оскільки ви згадуєте про тестування, зазначу, що вам не обов’язково знущатися над методом, коли ви це робите. Як "деталь реалізації", знущання над ним під час тестів мало користі.
jpmc26

Відповіді:


76

Чистою функцією все-таки може бути деталь реалізації. Хоча ця функція не може заподіяти ніякої шкоди (з погляду не порушувати важливих інваріантів / контрактів), оголюючи її як автор, так і користувачі цього класу / модуля / пакету, втрачають. Автор втрачає, оскільки тепер він не може його видалити, навіть якщо реалізація зміниться і функція для нього більше не корисна. Користувачі втрачають через те, що їм доведеться просіювати та ігнорувати додаткові функції, які не мають відношення до використання API, щоб зрозуміти це.


33
+1. Публічні члени вашого класу - це його API. Це не питання "Чи може мені зашкодити, якщо він публічний?", А скоріше "Чи повинен він бути частиною API?"
Звичайний

1
Єдине, що я хотів би додати, це те, що якщо ви починаєте бачити схожі функції, що спливають в інших місцях, переробляйте його в інший клас, щоб кілька класів могли використовувати його. Або якщо це стосується спочатку, визначте його в окремому класі для початку.
jpmc26

1
Я б закріпив громадські класи, які також не мають намір поширюватись.
День

+1 для вказівки, що приховування або показ функції (або класу) - це насамперед питання захисту та підтримки API, а не пов'язані з типом коду, який він є.
Марко

42

Питання назад.

Ви не шукаєте причини зробити функцію непублічною. Починати з (на мою думку) неправильного мислення. Міркування повинні йти іншим шляхом.

Іншими словами - не запитуйте "чому я ставлю це приватним?" Запитайте: "навіщо я оприлюднюю це?"

Якщо ви сумніваєтесь, не піддавайте цього. Це схоже на бритву Оккама - не примножуйте право за межі необхідності.

EDIT: Розгляд контраргументів, викладених @Telastyn у коментарях (щоб уникнути розширеної дискусії там):

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

Так, іноді це біль, якщо клас відкритий для спадкування, але ви не можете перекрити деякі приватні методи (поведінку яких ви хочете змінити).

Але protectedвистачило б - і це все ще не публічно.

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

Якщо це стане проблематичним, то просто оприлюднити його! Є необхідність, про яку я говорив :)

Моя думка, що ви не повинні робити це на всякий випадок (YAGNI та всі).

Зауважте, що завжди простіше зробити приватну функцію загальнодоступною, ніж повернути її до конфіденційності. Останній, ймовірно, порушить існуючий код.


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

3
@Telastyn IMHO, це здається, що програма страждає від певних помилок дизайну; якщо ви виявляєте схильність викривати метод, тому що вам потрібно викликати його через окремі шляхи коду, це, мабуть, ознака того, що цей метод повинен бути розбитий на власний модуль, який можна вводити або включати, де це доречно, особливо якщо це чиста функція .
лео

@leo - вибачте, я не дотримуюся. Розглянемо функцію на зразок цілого числа менше, ніж. Це приємна чиста функція, яка спеціально піддається впливу, оскільки її можна потім вводити та включати (або просто використовувати), де це доречно.
Теластин

5
@Telastyn На мій погляд, це симптом філософії "все є об'єктом / класом" у поєднанні з тим, що деякі мови OOP не мають інших типів даних, крім класів. Як тільки комусь потрібно одночасно передати два значення, він закінчить писати клас, і тоді кожен співробітник та програмне забезпечення для перевірки стилю скажуть вам зробити ці поля приватними.
Довал

@Telastyn Справа. Що я маю на увазі, якщо ваш метод 'integerLessThan' почався як інкапсульована деталь реалізації публічного методу, але ви виявите, що вам потрібно викликати приватний метод в інших місцях, це, мабуть, ознака того, що приватний метод належить до іншого модуль / пакет / клас, щоб його можна було імпортувати незалежно від логіки, яка викликає його. Просто оприлюднення методу як є означає, що метод довільно розташований у першому модулі, де ви вважаєте його корисним.
лео

6

Я не думаю, що рішення про приховування / інкапсуляцію функції повинно залежати від її чистоти. Просто те, що функція чиста, не означає, що зовнішні люди повинні знати про неї. Досить цікаво, хоча якщо функція чиста і призначена для загального користування, можливо, їй навіть не потрібно бути членом інтерфейсу інтерфейсу, можливо, вона краще підходить як статична. Але знову ж таки все це залежить від наміру договору і в цьому випадку логічного групування функціональності, а не чистоти функції.


5

Класи повинні дотримуватися Єдиного принципу відповідальності . Хоча класу, можливо, потрібно буде використовувати інші функціональні можливості для досягнення своїх цілей, він повинен піддавати лише функції, що входять до його єдиної відповідальності.

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

Розглянемо клас, який frobnicates віджети. Можливо, як частина свого коду для фробнікації йому потрібна якась утиліта, яка розбирає рядок: можливо, їй потрібно перетворити ім'я віджета таким чином, що стандартні функції рядків не підтримують.

Оскільки це чиста функція (рядок входить, трансформує її якось, повертає нову рядок), вона може бути публічною або приватною без наслідків. Або могло?

Якщо ви зробите це загальнодоступним, тепер ваш клас покладається на два обов'язки: розмахування віджетів та трансформація рядків. Це порушує SRP і може спричинити проблеми, якщо інші класи покладаються на функцію. Оскільки це те, що, на вашу думку, використовується лише внутрішньо для класу, можливо, ви змінюєте його інтерфейс чи видимість. Зараз класи в інших частинах системи розбиті.

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


2
Я б заперечував, що він повинен містити лише функції, що є частиною його єдиної відповідальності, а не просто викривати.
Теластин

1
Я думаю, що є сіра зона. Вам справді потрібен окремий StringTransformerклас, щоб капсулювати два-три рядки коду, які використовуються лише в одному місці? Я погоджуюся, що як тільки код використовується в декількох місцях, найкраще відокремити його в новий клас з однією відповідальністю, але відбувається компроміс.

Звичайно. Настанови - це вказівки, а не правила.
Теластин

@snowman in non java ви просто зробите бібліотеку функцій.
Пітер Б

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