Ніколи не використовуйте Strings на Java? [зачинено]


73

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

public void bookTicket(
  String name,
  String firstName,
  String film,
  int count,
  String cinema);

public void bookTicket(
  Name name,
  FirstName firstName,
  Film film,
  Count count,
  Cinema cinema);

З мого досвіду, читаючи блоги програмування, я прийшов до висновку, що 90% - це нісенітниця, але мені залишається цікаво, чи це правдивий пункт. Якось мені це не подобається, але я не міг точно визначити, що це стосується стилю програмування.


Відповідна дискусія в оригінальній Вікі: "що таке toString ()?"
Крістофер Хаммарстрем

5
О, ого, ця публікація в блозі змусила мене відчувати себе фізично хворими
Роб

5
Я прочитав принаймні одну книгу (яка, можливо, була першою редакцією Code Complete), в якій рекомендував вводити всі ваші примітивні типи, щоб мати імена з проблемного домену. Та сама ідея.
user16764

9
твердження, що починається з "ніколи", є "завжди" хибним!
Флоренц Целай

1
Цей хлопець - КТО ?!
Х’янгело

Відповіді:


88

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

Редагувати: Я прочитав пост у блозі, і він має принципово гарну ідею. Проблема полягає в тому, що він занадто далеко змінив його масштаб. Що - щось на зразок String orderId це дуже погано, тому що по- видимому "!"£$%^&*())ADAFVF, не є допустимим orderId. Це означає, що Stringпредставляє набагато більше можливих значень, ніж є дійсні orderIds. Однак, для чогось подібного name, тоді ви не можете передбачити, що може бути, а що не може бути дійсним іменем, а будь-яке Stringє дійсним name.

По-перше, ви (правильно) зводите можливі входи лише до дійсних. По-друге, ви не досягли звуження можливих дійсних входів.

Ще раз редагуйте: розгляньте випадок недійсного введення. Якщо ви напишете "Гарет Гобулькок" як своє ім'я, це буде виглядати нерозумно, це не буде кінцем світу. Якщо ви введете недійсний OrderID, швидше за все, він просто не працюватиме.


5
З ідентифікаторами замовлення я все-таки вважаю за краще додавати галочку в код, що приймає ідентифікатори та залишає String. Передбачається, що класи надають методи роботи з даними. Якщо якийсь клас перевіряє лише достовірність деяких даних, а потім нічого не робить, це не здається мені належним. OOP - це добре, але не варто перестаратися.
Малькольм

13
@Malcolm: Але тоді ви не можете дізнатися, які рядки перевірені, окрім того, щоб перевіряти їх знову і знову.
DeadMG

7
"[...] ви не можете передбачити, що може бути, а може бути неправдивим ім'ям, і будь-яка рядок - це дійсне ім'я." Я здогадуюсь, одна з переваг цієї методики полягає в тому, що якщо ваш параметр типу Name, ви не можете випадково передати інше незв'язане значення String. Там, де ви очікуєте Name, складеться лише Nameзаповіт, і рядок "Я тобі зобов'язаний 5 доларів" не можна випадково прийняти. (Зауважте, це не пов’язано з будь-яким видом перевірки імені!)
Андрес Ф.

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

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

46

Це просто божевільно :)


2
Це теж моя думка, але річ у тому, що я пам’ятаю цю пропозицію в «Кодексі завершено». Звичайно, не все в цій книзі є незаперечним, але принаймні це змушує задуматися двічі, перш ніж відкинути ідею.
DPM

8
Ви просто згадали деякі гасла без реального обґрунтування. Чи можете ви детальніше розглянути свою думку? Наприклад, деякі прийняті зразки та мовні особливості можуть виглядати як додаткові складності, але вони пропонують щось взамін цінне (наприклад: статичне введення тексту)
Андрес Ф.

12
Здоровий глузд - це що завгодно, але звичайне.
Kaz Dragon

1
+1, до цього я додам, що коли мова підтримує названі параметри, а IDE хороший, як це стосується C # і VS2010, тоді не потрібно компенсувати відсутність функцій у мові шаленими візерунками . Немає потреби в класі з назвою X і в класі з іменем Y, якщо можна писати var p = new Point(x: 10, y:20);Також, це не так, як Cinema відрізняється від рядка. Я розумію, якби мати справу з фізичними величинами, такими як тиск, температура, енергія, де одиниці різняться, а деякі не можуть бути негативними. Автору блогу потрібно спробувати функціонал.
робота

1
+1 для "Не надмірний інженер!"
Іван

23

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


2
Чи може коментувати коментар?
кевін клайн

Яку високу ціну ми платимо за статичний набір тексту?
Річард Тінгл

@RichardTingle: час перекомпілювати код та перезапустити JVM для кожної зміни коду. Час, втрачений при здійсненні змін, сумісних із джерелами, але бінарних, несумісних, і речей, які потрібно перекомпілювати, не є, і ви отримуєте "MissingMethodException".
Кевін Клайн

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

16

Не робіть цього; це надмірно ускладнить речі, і Ви цього не потребуєте

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


Переваги

Головне серед переваг - можливість змінювати та розширювати код. Якщо ви використовуєте

class Point {
    int x,y;
    // other point operations
}

замість того, щоб просто пропустити пару цілих чисел навколо, а це, на жаль, багато інтерфейсів, - згодом стає набагато легше додати ще один вимір. Або змінити тип на double. Якщо ви використовуєте List<Author> authorsабо List<Person> authorsзамість List<String> authorsцього, пізніше стає набагато простіше додати більше інформації до того, що представляє автор. Якщо записати це так, то здається, що я висловлюю очевидне, але на практиці я був винен, що я багато разів використовував рядки таким чином, особливо у тих випадках, коли це було не очевидно на початку, тоді мені знадобилося б більше, ніж рядок.

На даний момент я намагаюся переробити деякий список рядків, який переплітається в моєму коді, тому що мені там потрібна додаткова інформація, і я відчуваю біль: \

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

Кінцева вигода - це безпека типу , з очевидних причин, але в моїх очах це незначна річ.

Недоліки

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

Він може залучати безліч зайвих сеттерів (або конструкцій) та гетерів . Автор блогу new Point(x(10), y(10))замість цього дає справді некрасивий приклад new Point(10, 10), і я хотів би додати, що використання може також включати такі речі, як Math.max(p.x.get(), p.y.get())не Math.max(p.x, p.y). І довгий код часто вважається важчим для читання, і це справедливо. Але, чесно кажучи, у мене є відчуття, що багато коду переміщує об'єкти навколо, і лише вибрані методи створюють його, і ще менше людей потребують доступу до його суворих деталей (що так і не є OOPy).

Дискусійний

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


Як і в більшості інших шкіл програмування думки, я вважаю, що це нездорово сприймати цю крайність. Я не бачу, щоб я колись розділяв координати x і y, щоб вони були різного типу. Я не вважаю Countза потрібне, коли intслід вистачити. Мені не подобається unsigned intвикористання C - хоча теоретично це добре, воно просто не дає вам достатньо інформації, і забороняє розширювати ваш код пізніше, щоб підтримати цей магічний -1. Іноді вам потрібна простота.

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

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


5

Хоча це щось надмірне, я часто думаю, що більшість речей, які я бачив, замість цього недостатньо розроблені.

Це не лише "Безпека". Одна з дійсно приємних речей щодо Java - це те, що вона дуже допомагає вам запам’ятати / з'ясувати те, що потребує / очікує будь-який дзвінок методу бібліотеки.

НАЙВИШНІЙ (на сьогоднішній день) бібліотеку Java, з якою я працював, написав хтось, хто дуже любив Smalltalk і моделював бібліотеку GUI після неї, щоб вона діяла більше як smalltalk - проблема в тому, що кожен метод приймав один і той же базовий об'єкт, але насправді не вдалося ВИКОРИСТИТИ все, на що може бути передано базовий об'єкт, тому ви знову вгадували, що перейти до методів, і не знаючи, чи не вдалося ви виконати до виконання (Щось я мав мати справу з кожним разом, коли я працював у С).

Інше питання - Якщо ви обходите рядки, ints, колекції та масиви без об’єктів, все, що у вас є, - це кулі даних без сенсу. Це здається природним, коли ви думаєте, що з точки зору бібліотек використовуватиметься "якийсь додаток", але при розробці цілого додатка набагато корисніше призначити значення (код) всім вашим даним у місці, де дані визначені, і лише думати про умови взаємодії цих об'єктів високого рівня. Якщо ви обмінюєте примітиви замість об'єктів, ви, за визначенням, змінюєте дані в місці, відмінному від визначеного (Це також, чому я дійсно не люблю Setters і Getters - та сама концепція, ви працюєте на дані, які не є вашими).

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

Це насправді не погана ідея. Думаючи про це, я навіть не впевнений, я б сказав, що це взагалі надмірно розроблено, з цим просто легше працювати майже всіляко - єдиним винятком є ​​розповсюдження крихітних класів, але класи Java такі легкі та легкі написати, що це навряд чи навіть є вартістю (їх можна навіть генерувати).

Мені б дуже цікаво побачити добре написаний проект java, в якому насправді було визначено занадто багато класів (Де це ускладнило програмування), я починаю вважати, що не можна мати занадто багато класів.


3

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

public void bookTicket(
  String name,
  String firstName,
  String film,
  int count,
  String cinema);

Для визначення фактичного мецената, який бронює квитки, потрібні два параметри. У вас можуть бути два різних фільми з однаковими іменами (наприклад, рімейки), у вас може бути той самий фільм з різними іменами (наприклад, переклади). Певна мережу кінотеатрів можуть мати різні філії, так як ви будете мати справу з цим в рядку і послідовним чином (наприклад , ви використовуєте $chain ($city)або $chain in $cityнавіть що - то ще й як ви збираєтеся , щоб переконатися , що це Найгірше - це конкретизація вашого покровителя за допомогою двох параметрів, той факт, що надано і ім’я, і прізвище, не гарантує дійсного замовника (і ви не можете розрізнити двох John Doeс).

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

Отже, повідомлення в блозі IMHO просто говорить "переконайтеся, що ви передаєте правильні типи", його автор просто зробив надто обмежений вибір, зокрема, для вибору основних типів даних (що є неправильним повідомленням).

Пропонована альтернатива краще:

public void bookTicket(
  Name name,
  FirstName firstName,
  Film film,
  Count count,
  Cinema cinema);

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

Що варто, я б насправді написав щось подібне

public Ticket bookTicket(
  Person patron,
  Film film,
  int numberOfTickets,
  Cinema cinema);

Короткий короткий огляд: вам не слід передавати безглузді змінні; єдиний раз, коли ви насправді вказуєте об'єкт зі значенням, яке не визначає фактичного об'єкта, це коли ви запускаєте запит (наприклад public Collection<Film> findFilmsWithTitle(String title)) або коли ви збираєте доказ-концепцію. Утримуйте чисту систему, тому не використовуйте занадто загальний тип (наприклад, фільм, представлений a String) або занадто обмежуючий / конкретний / надуманий (наприклад, Countзамість int). Використовуйте тип, який визначає ваш об'єкт однозначно та однозначно, коли це можливо та життєздатний.

редагувати : ще коротший підсумок. Для невеликих застосувань (наприклад, доказ понять): навіщо турбуватися зі складною конструкцією? Просто використовуйте Stringабо intпродовжуйте це.

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

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


Я бачу вашу думку, але я стверджую, що ви припускаєте більше, ніж заявляє автор. Наскільки ми знаємо, його модель має лише String для мецената, String для фільму тощо. Ця гіпотетична додаткова функціональність насправді є сутністю проблеми, тому або він був дуже «неприйнятим», щоб пропустити це, роблячи свою справу, або вважає, що ми повинні надати більше смислової сили саме тому. Знову ж таки, за всім, що ми знаємо, це останнє він мав на увазі.
DPM

@Jubbat: насправді я припускаю більше, ніж те, що заявляє автор. Але моя думка полягає в тому, що або у вас є проста програма, і в будь-якому випадку будь-який спосіб надмірно складний. Для цієї шкали ремонтопридатність не є проблемою, а семантичне розрізнення перешкоджає швидкості кодування. Якщо, з іншого боку, ваш додаток великий, стає правильним визначити свої типи (але навряд чи це будуть прості обгортки). ІМХО, його приклади просто не переконливі або мають основні вади дизайну поза точкою, яку він намагається зробити.
Егон

2

Для мене це те саме, що використовувати регіони в C #. Як правило, якщо ви вважаєте, що це потрібно для того, щоб зробити ваш код читабельним, тоді у вас є більші проблеми, на які слід витратити свій час.


2
+1 - передбачає лікування симптомів, а не причини.
робота

2

Я б сказав, що це дійсно гарна ідея на мові з сильно набраною функцією typedef.

У Java у вас цього немає, тому створення цілого нового класу для цих речей означає, що вартість, ймовірно, перевершує вигоду. Ви також можете отримати 80% переваги, обережно ставившись до імені змінної / параметрів.


0

Було б добре, якщо IF String (кінцевий Integer, і ... кажучи лише про String) не був остаточним, тому ці класи могли бути БЕЗКОШТОВНИМ (обмеженим) рядком зі значенням і все-таки могли бути відправлені на якийсь самостійний об'єкт, який вміє обробляти базовий тип (без розмови назад і назад).

І "добрі" його збільшуються, коли є напр. обмеження на всі імена.

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


0

На приклад: у нашій розробленій в даний час системі існує безліч різних сутностей, які можна ідентифікувати за різними видами ідентифікаторів (за рахунок використання зовнішніх систем), іноді навіть одного типу сутності. Усі ідентифікатори - це рядки, тож якщо хтось змішує тип ідентифікатора, який повинен передаватися як параметр, не відображається помилка часу компіляції, але програма підірватиметься під час виконання. Це трапляється досить часто. Тож я мушу сказати, що головна мета цього принципу - не захист від змін (хоча це також і служить), а захист себе від помилок. І як би там не було, якщо хтось розробляє API, він повинен відображати поняття домену, тому концептуально корисно визначати конкретні доменні класи - все залежить від того, чи є порядок у свідомості розробників,


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