Два структури з однаковими членами, але різними назвами, це гарна ідея?


49

Я пишу програму, яка передбачає роботу як з полярними, так і декартовими координатами.

Чи має сенс створювати два різних структур для кожного виду точок, одна з яких Xі Yчленів і один з Rі Thetaчленами.

Або це занадто багато, і краще мати лише одну структуру з членами firstі secondв якості членів.

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

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


11
Я завжди створюю нову структуру / клас за ціллю. Потрібен 3d-вектор, створити структуру 3d_vector з трьома поплавками. Потрібно представлення uvw, створіть структуру text_coords з трьома поплавками. Потрібна позиція в 3d-середовищі, створіть структуру з трьома поплавками. Ви отримуєте бал. Це дозволяє набагато краще читати, ніж використовувати одне й те саме. Якщо вас турбує, визначаючи одну і ту ж річ кілька разів, використовуйте базову 3-float-структуру та визначте кілька імен, щоб бути тією самою структурою.
Кевін

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

8
@Sidney Якщо ви абсолютно не потребуєте функціоналу, я б не став. Для перетворення між двома представленнями потрібно виконати операцію sin / arcsin. Це збирається ввести трохи бітрот в найменш значущі біти щоразу, коли ви здійснюєте конверсію. Я майже впевнений, що ти закінчишся подібним болем до того, що я переживав, намагаючись мати справу з класом, який забезпечував як частоту подій, так і час між подіями (x та 1 / x) поданням чогось. Відстеження того, яке представництво є канонічним на уроці, і боротьба з усіма головними болями в округленні - це не те, що я хотів би зробити ще раз.
Ден Нелі

3
Хорошим прикладом типу даних, який може представляти багато речей, є рядок, але "строго набраний" - це антипатерн. Запишіть свій приклад. Спробуйте впровадити крапковий продукт для типу, який підтримує обидві системи координат.
Натан Купер

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

Відповіді:


17

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

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

Крайнє рішення (яке ми врешті-решт надали) - мати базовий клас шаблонів CRTP з функціями get <0> () get <1> () та <2> (), щоб отримати елементи в загальному вигляді. Потім ці функції визначаються в декартовій або полярній структурі, що походить від цього базового класу. Він вирішує всі проблеми, але виходить досить нерозумною ціною: вивчити метапрограмування шаблонів. Однак якщо метапрограмування шаблонів уже є справедливою грою для вашого проекту, це може бути хорошим співпадінням.


1
Ваша відповідь дуже цікава. чи можна навести приклад?
Моха всемогутній верблюд

1
Я можу запропонувати приклад того, що я зробив: фільтруючі полярні фільтри, декарти та вектори. Математика була досить схожою, sans angle (un) обгортка, у коді деякі частини дублювались все-таки з міркувань продуктивності, і ми використовували шаблони там, де це було те саме. Використовуються різні назви для всіх матеріалів. "Екстремальне рішення" Корта могло врятувати кілька дублювань, але майже не всі.
Євген Рябцев

1
Моя перша реакція полягала в тому, що подібну ситуацію краще вирішити кастингом, але вона виявляється дещо ризикованою .
200_успіх

114

Так, це має багато сенсу.

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

Люди погано запам’ятовують такі химерні деталі, але добре створюють сміливі винахідливі плани. Комп'ютери непогані у химерних деталях і погані у творчих планах. Тому завжди корисно перенести на комп’ютер якнайбільше технічного обслуговування деталей, залишаючи розум вільним працювати над грандіозним планом.


6
+1 Досконалий опис використання комп’ютера, щоб робити те, що комп’ютери добре роблять, і дозволяти мозку зосередитися на власній роботі.
БрайанХ

8
"Програми повинні бути написані для того, щоб люди читали, і лише випадково, щоб вони виконували машини." - з "Структура та інтерпретація комп'ютерних програм" Абельсона та Суссмана.
hlovdal

18

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

В залежності від ваших потреб, вона також може бути варто реалізації координатно - інтерфейс, з допомогою методів , таких як X(), Y(), Displacement()і Angle()(або , можливо , Radius()і Theta(), в залежності від).


Ще важливіше, якщо ОП проводить класи з цих структур, оскільки операції на декартових та полярних координатах різні.
Міндвін

1
+1 за останній абзац, це ідеальне рішення. Точка - простір - це об'єкт; внутрішнє представлення цього пункту не має значення. Звичайно, проблеми в реальному світі (продуктивність, помилки округлення) можуть спричинити його значення. Все залежить від того, для чого це використовується.
BlueRaja - Danny Pflughoeft

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

8

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

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

Крім того, те, що всі вони є репрезентативними, doubleне означає, що ви можете використовувати їх взаємозамінно. Наприклад, θ - безрозмірний кут, тоді як y має одиниці довжини. Оскільки типи не є логічно замінними, вони повинні бути двома несумісними структурами.

Якщо вам справді потрібно грати в трюки з повторним використанням пам’яті - а ви майже напевно не повинні - ви можете використовувати unionтип C, щоб зрозуміти свій намір.


Найкраща відповідь, ІМХО
Дін Редкліфф

2

По-перше, мати обидва явно, відповідно до цілком обгрунтованої відповіді @ Kilian-foth.

Однак я хочу додати:

Запитайте: Чи дійсно у вас є операції, які є загальними для обох, якщо їх розглядати як пари double? Зауважте, це не те саме, що говорити про те, що у вас є операції, які стосуються обох у власних умовах. Наприклад, "сюжет (Коорда)" переймається тим Coord, полярний чи декартовий. З іншого боку, зберігання файлів просто обробляє дані як є. Якщо у вас дійсно є загальні операції, розгляньте або визначення базового класу, або визначення перетворювача в std::pair<double, double>або, tupleабо що ви маєте на своїй мові.

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

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


1

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

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

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


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

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

0

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

Уявіть сценарій: у вас є кілька декартових точок: pntA і pntB. Тоді ви чомусь вирішите, що вони повинні бути краще представлені в полярних координатах, і змінити декларацію та конструктор.

Тепер, якщо всі ваші операції були лише методами викликів типу:

double distance = pntA.distanceFrom(pntB);

значить, ти все добре. Але що робити, якщо ви явно користувались членами? Порівняйте

double leftMargin = abs(pntA.x - pntB.x);
double leftMargin = abs(pntA.first - pntB.first);

У першому випадку код не складеться. Ви побачите помилку одразу і зможете її виправити. Але якщо у вас однакові імена учасників, помилка буде лише на логічному рівні, набагато складніше виявити.

Якщо ви пишете не об'єктно-орієнтованою мовою, тоді навіть простіше передати неправильну структуру функції. Що заважає вам написати наступний код?

double distance = calculate_distance_polar(cartesianPointA, polarPointB);

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

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