Кілька класів з однаковою назвою, але різні простори імен?


11

Я зіткнувся з деяким кодом (C #, якщо це має значення), який має класи, які мають однакове ім'я, але відрізняються між собою просторами імен. Всі вони, як правило, представляють одну і ту ж логічну річ, але часто є різними «поглядами» на один і той же об’єкт. Різні простори імен часом є частиною одного рішення і навіть одного і того ж dll.

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

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

Які вказівки навколо цієї практики?


1
У OOP є багато винятків, але спочатку я вважаю це поганим дизайном, особливо якщо всі класи мають однаковий рівень доступності. Коли я читаю ім'я класу в будь-якому файлі, то я хочу знати, в якому просторі імен клас знаходиться негайно, без необхідності посилатися на директиви щодо простору імен.
Леопольд Аспергер

Існує дійсний випадок, коли є кілька пов'язаних об'єктів, які мають однаковий стан, але зазвичай вони розділені за функцією. Можливо, у вас є один об’єкт, який адаптує модель для перегляду. Інший може виконати обчислення на об'єкті. Іншим може бути перехідник або фасад. Таким чином , ви могли б FooAdapter, FooViewer, FooCalculatorі т.д. , але , звичайно , не назвав те ж саме. Однак без додаткової інформації про конкретні класи, про які ви говорите, важко дати відповідь.

@JohnGaughan - розглянемо Employeeяк приклад. Є працівник, який призначений для інших працівників, один для HR, один для зовнішнього опромінення. Всі вони названі "співробітниками", і всі вони мають різновиди помічників (EmployeeRepository, EmployeeView тощо). Всі вони перебувають у одній і тій же кімнаті, і хоча вони діляться деякими загальними властивостями (ім'я, назва), всі вони відрізняються (номер телефону, зарплата).
Теластин

Працюючи з системами, які моделюють Employees кілька разів, здається, що вам потрібна модель із об'єднанням усіх атрибутів і включає атрибут typeабо department. Інакше відбувається непотрібне дублювання коду.

5
Чи не це було однією з причин простору імен? Щоб дозволити зіткнення імен?
Ден Пішельман

Відповіді:


5

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

Читання коду

Перш за все, вважайте, що читабельність коду важлива, і ця практика суперечить цьому. Хтось читає фрагмент коду, і скажемо просто, що він має функцію doSomething(Employee e). Це більше не можна читати, оскільки через 10 різних Employeeкласів, що існують у різних пакетах, вам доведеться спочатку вдатися до декларації про імпорт, щоб дізнатися, який насправді ваш вклад.

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

Речі розбиваються, як тільки ви залишаєте ці пакунки. Після того, як ви працюєте в іншому модулі / пакеті / тощо і вам потрібно працювати з працівником, ви вже жертвуєте читабельністю, якщо не повністю кваліфікуєте її тип. Крім того, наявність 10 різних Employeeкласів означає, що автоматичне завершення роботи IDE більше не працюватиме, і вам доведеться вручну вибирати тип працівника.

Копіювання коду

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

Складність коду

Що ви можете так складно запитати? Зрештою, кожен з класів може тримати його так просто, як хоче. Те, що справді стає проблемою, - це те, як ви поширюєте зміни. За допомогою програмного забезпечення, що має достатньо функціональних можливостей, ви зможете змінити дані співробітників - і ви хочете відобразити це скрізь. Кажуть , що одна дама тільки що вийшла заміж , і ви повинні перейменувати її від XдоYзавдяки тому. Досить важко зробити це правильно всюди, але ще складніше, коли у вас є всі ці виразні класи. Залежно від інших варіантів дизайну, це може легко означати, що кожен з класів повинен впроваджувати власного типу слухача або такого, щоб його повідомляти про зміни - що в основному означає фактор, що застосовується до кількості класів, з якими вам доведеться мати справу. . І більше дублювання коду, звичайно, і менша кількість читабельності коду, .. Такі фактори можуть бути ігноровані при аналізі складності, але вони, безумовно, дратують, коли застосовуються до розміру бази вашого коду.

Кодове спілкування

Крім перерахованих вище проблем зі складністю коду, які також пов'язані з вибором дизайну, ви також знижуєте чіткість дизайну вищого рівня та втрачаєте можливість належним чином спілкуватися з експертами по домену. Коли ви обговорюєте архітектуру, дизайн чи вимоги, ви більше не можете робити прості заяви на кшталт given an employee, you can do .... Розробник більше не знатиме, що employeeнасправді означає в цей момент. Хоча експерт із домену це, звичайно, знатиме. Ми всі робимо. типу. Але з точки зору програмного забезпечення спілкуватися вже не так просто.

Як позбутися від проблеми

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

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

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

Що стосується позбавлення від застарілих класів співробітників, ви можете вирішити для кожного окремого випадку, як його найкраще усунути, або просто домовитись про загальну схему. Ви можете прикріпити ім'я класу і обгорнути його навколо справжнього співробітника, можете використовувати шаблони (декоратор чи адаптер приходить на думку), або, або або.

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


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

1
Це недостатньо розумно, коли ви знаходитесь поза одним контекстом. Розглянемо клас, який насправді потребує обох проблемних класів - автоматичне завершення зовсім не допоможе. І це навіть не відрізнятиме їх від інших десяти класів. Але справжня проблема полягає в тому, що новий член команди, який навіть не знає, що насправді є всі ці доменні пакети та який саме він повинен вибрати.
Френк

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

@randomA - на жаль, цей випадок досить поширений у цій кодовій базі.
Теластин

Хороші моменти, але ви насправді не вирішили основну проблему, а лише методологію після того, як буде вирішена основна проблема ("що таке працівник насправді"). Єдине очевидне "рішення" ("інтегрувати всі індивідуальні атрибути в одного бога-службовця") ви відкинули, справедливо. Скажімо, у вас є DB.E zaposee, Marshalling.E Employee, ViewModels.E Employee, GUI.Views.E Employee тощо, яке ваше рішення?
Пабло Н

9

Рамка колекції Scala є хорошим прикладом цього. Є колекції, що змінюються і змінюються, а також серійні та паралельні (а в майбутньому, можливо, і поширені) колекції. Отже, є, наприклад, чотири Mapознаки:

scala.collection.Map
scala.collection.immutable.Map
scala.collection.mutable.Map
scala.collection.concurrent.Map

плюс три ParMapс:

scala.collecion.parallel.ParMap
scala.collecion.parallel.immutable.ParMap
scala.collecion.parallel.mutable.ParMap

Ще цікавіше:

scala.collection.immutable.Map            extends scala.collection.Map
scala.collection.mutable.Map              extends scala.collection.Map
scala.collection.concurrent.Map           extends scala.collection.mutable.Map

scala.collecion.parallel.ParMap           extends scala.collection.Map
scala.collecion.parallel.immutable.ParMap extends scala.collecion.parallel.ParMap
scala.collecion.parallel.mutable.ParMap   extends scala.collecion.parallel.ParMap

Таким чином, ParMapпродовжується ParMapтриває Mapі Mapтриває Mapтриває Map.

Але, визнаймося: як би ти ще їх назвав? Це має ідеальний сенс. Ось для чого створені простори імен!


3
"Для цього потрібні простори імен!" => +1
svidgen

Мені це подобається, але в C # /. NET світі, коли я переміщував паралельні класи в паралельний простір імен паралельних, це потім стикається з допоміжним класом System.Threading.Parallel, який, як мені здається, сильно використовує для отримання паралелізму. Я не хотів натомість використовувати простір імен "Concurrent", оскільки це вже використовується для означання "одночасного доступу" на класи колекцій. В якості компромісу я вибрав простір імен "Паралельне".
redcalx

2

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

Я думаю, що є два дуже чіткі міркування, що змусили б ви йти в ту чи іншу сторону, виходячи з двох відповідей від @Jorg та @Frank:

  1. Якщо ви передбачаєте ситуацію, коли в одному і тому ж контексті коду може знадобитися використовувати більше одного однойменного класу, це не може використовувати те саме ім'я. Тобто, якщо вам потрібно працювати, hr.Employeeале в той же час потрібно дивитись на дозволи, через security.Employeeце буде набридливо реально швидко.
  2. І навпаки, якщо всі ваші класи з тим самим іменем мають однакові або дуже схожі API, то це хороший приклад того, коли вам слід використовувати одне ім’я з різними просторами імен. Приклад Scala - саме такий, коли всі Mapкласи реалізують один і той же інтерфейс або є підкласами один одного. У цьому випадку неоднозначність або «плутанина» можна, безсумнівно, назвати хорошою справою, оскільки користувач справедливо може ставитися до всіх реалізацій класу або інтерфейсу однаково ... що є сутністю інтерфейсів та підкласів.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.