Чи було б сенсом використовувати об'єкти (замість примітивних типів) для всього в C ++?


17

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

static bool getGPS(double plane_latitude, double plane_longitude, double plane_altitude,
                       double plane_roll, double plane_pitch, double plane_heading,
                       double gimbal_roll, double gimbal_pitch, double gimbal_yaw, 
                       int target_x, int target_y, double zoom,
                       int image_width_pixels, int image_height_pixels,
                       double & Target_Latitude, double & Target_Longitude, double & Target_Height);

Тому я хочу переробити його так, щоб виглядати приблизно так:

static GPSCoordinate getGPS(GPSCoordinate plane, Angle3D planeAngle, Angle3D gimbalAngle,
                            PixelCoordinate target, ZoomLevel zoom, PixelSize imageSize)

Мені здається, це значно читабельніше та безпечніше, ніж перший метод. Але чи є сенс створювати PixelCoordinateта проводити PixelSizeзаняття? Або мені було б краще просто використовувати std::pair<int,int>для кожного. І чи є сенс мати ZoomLevelклас, чи я просто повинен використовувати клас double?

Моя інтуїція використання класів для всього ґрунтується на цих припущеннях:

  1. Якщо для всього класу є класи, неможливо було б передати ZoomLevelтуди, де Weightочікували об'єкт, тому було б складніше надати неправильні аргументи функції
  2. Так само деякі незаконні операції можуть спричинити помилки компіляції, такі як додавання GPSCoordinateдо того ZoomLevelчи іншогоGPSCoordinate
  3. Юридичні операції легко представити та надати безпеку. тобто віднімання двох GPSCoordinates дало б aGPSDisplacement

Однак більшість кодів C ++, які я бачив, використовує безліч примітивних типів, і я думаю, що це має бути вагомою причиною. Це гарна ідея використовувати предмети для чого-небудь, чи у неї є недоліки, про які я не знаю?


8
"Більшість кодів C ++, які я бачив, використовує безліч примітивних типів" - приходять у голову дві думки: багато C ++ коду, можливо, було написано програмістами, знайомими з C, у яких слабша абстракція; також Закон Стерджена
congusbongus

3
І я думаю, що це тому, що примітивні типи прості, прості, швидкі, гладкі та ефективні. Зараз у прикладі ОП, безумовно, хороша ідея запровадити нові класи. Але відповідь на титульне запитання (де написано "все") - це чудове "ні"!
Містер Лістер

1
@Max typedeffing удвічі не робить абсолютно нічого для вирішення проблеми ОП.
Містер Лістер

1
@CongXu: У мене майже однакова думка, але більше в тому сенсі, "багато коду будь-якою мовою можливо було написано програмістами, які взагалі мають проблеми зі створенням абстракцій".
Док Браун

1
Як говориться, "якщо у вас функція з 17 параметрами, ви, мабуть, пропустили кілька".
Joris Timmermans

Відповіді:


24

Однозначно так. Функції / методи, які беруть занадто багато аргументів, мають запах коду і вказують на принаймні одне з наступних:

  1. Функція / метод робить занадто багато речей одночасно
  2. Функція / метод вимагає доступу до цього багатьох речей, оскільки він запитує, не розповідає чи не порушує деякий закон про розробку ОО
  3. Аргументи насправді тісно пов'язані

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

static GPSCoordinate getGPS(Plane plane, Angle3D gimbalAngle, GPSView view)

(Я не знайомий з GPS, тому це, мабуть, не правильна абстракція; наприклад, я не розумію, що зум має відношення до функції, що називається getGPS.)

Будь ласка, віддайте перевагу класам, як PixelCoordinateнад std::pair<T, T>, навіть якщо вони мають однакові дані. Це додає смислового значення плюс трохи додаткового типу безпеки (компілятор не дозволить вам переходити ScreenCoordinateдо рівня, PixelCoordinateнавіть якщо обидва є pairs.


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

6

Відмова: Я віддаю перевагу від C до C ++

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

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

З вашого запитання, здається, це саме те, що ви шукаєте, загальний контейнер, а не надзвичайна структура. Як вже згадували інші, я б не переймався введенням Zoom у свій клас; Я особисто ненавиджу заняття, побудовані для того, щоб проводити тип даних (мої професори люблять створювати Heightта проводити Idзаняття).

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

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

Так само деякі незаконні операції можуть спричинити помилки компіляції, такі як додавання GPSCoordinate до ZoomLevel або іншого GPSCoordinate

Добре використовувати перевантаження оператора.

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

Також добре використовувати перевантаження оператора.

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


3

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

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

Я рекомендую використовувати typedefs, які є просто псевдонімами для примітивних типів ( typedef double ZoomLevel) не тільки з цієї причини, але й з іншої: Це дозволяє легко змінити тип пізніше. Одного разу, коли ви зрозумієте, що використання floatможе бути настільки ж добре, doubleале може бути ефективнішим для пам’яті та процесора, вам просто потрібно змінити це в одному місці, а не в лінійці.


+1 для пропозиції typedef. Отримує найкраще з обох світів: примітивні типи та предмети.
Карл Білефельдт

Змінити тип члена класу не важче, ніж typedef; чи ти мав на увазі порівняно з примітивами?
MaHuJa

2

Тут вже є точні відповіді, але я хочу додати одне:

Використання PixelCoordinateта PixelSizeкласифікація робить речі дуже конкретними. Генерал Coordinateабо Point2Dклас, можливо, краще відповідає вашим потребам. A Point2Dповинен бути класом, що реалізує найпоширеніші операції з точкою / вектором. Ви могли б реалізувати такий клас навіть загалом ( Point2D<T>де T може бути intабоdouble або що - то ще).

Операції 2D точок / вектор так настільки поширені для багатьох завдань програмування 2D геометрії, що, на мій досвід, таке рішення підвищить шанс повторного використання існуючих 2D-операцій. Ви уникнути необхідності повторних їх реалізації для кожного PixelCoordinate, GPSCoordinate«Whatsover» -коордіната класу знову і знову.


1
Ви також можете зробити, struct PixelCoordinate:Point2D<int>якщо хочете належної безпеки,
трепетка, урожай

0

Тому я хочу переробити його так, щоб виглядати приблизно так:

static GPSCoordinate getGPS(GPSCoordinate plane, Angle3D planeAngle, Angle3D gimbalAngle,
                        PixelCoordinate target, ZoomLevel zoom, PixelSize imageSize)

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

Це [більш читабельно]. Це також гарна ідея.

Але чи є сенс створювати класи PixelCoordinate та PixelSize?

Якщо вони представляють різні поняття, це має сенс (тобто "так").

Або мені буде краще просто використовувати std :: пару для кожного.

Можна почати, роблячи typedef std::pair<int,int> PixelCoordinate, PixelSize;. Таким чином, ви покриваєте семантику (ви працюєте з координатами пікселів та розмірами пікселів, а не зі std :: парами у вашому клієнтському коді), і якщо вам потрібно розширити, ви просто заміните typedef класом, а ваш клієнтський код повинен вимагають мінімальних змін.

І чи є сенс мати клас ZoomLevel, або я повинен просто використовувати подвійний?

Ви також можете ввести його, але я використовував би, doubleпоки мені не потрібна функція, пов’язана з ним, яка не існує для подвійного (наприклад, наявність ZoomLevel::defaultзначення вимагає визначення класу, але поки у вас нічого не буде що, тримай удвічі).

Моя інтуїція використання класів для всього ґрунтується на цих припущеннях [пропущено для стислості]

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

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

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

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

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

Тобто, якщо ви маєте координати, у яких завжди є x, y і z, то набагато краще мати coordinateклас, ніж всюди передавати три параметри.

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