Чи погано створювати класи, єдиною метою яких є неявне перетворення в інший клас?


10

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

Для того, щоб «вирішити» це, я створюю свій власний Circleклас в іншому просторі імен, який тільки приймає radiusі в centerякості параметрів, але має неявний перетворювач до зовнішньої бібліотеці Circleкласу , який просто створює Circle(this.radius, this.center, Flavours.Cardboard)об'єкт. Тому всюди, де мені потрібен інший тип Circle, я дозволяю автоматичне перетворення.

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


Це здається варіантом Адаптерного шаблону , хоча тут, мабуть, має сумнівне значення (це лише зручність для уникнення встановлення параметра).
Роберт Харві

5
Чому б не створити MakeCircle функцію ?
користувач253751

1
@immibis Thay була моєю першою думкою. Коли я створюю ігри, я часто створюю функцію, makePlayerяка сама приймає команди, щоб розмістити гравця, але делегує набагато складнішому конструктору.
Carcigenicate

Відповіді:


13

Хоча це не завжди погано, досить рідко є неявна конверсія - найкращий варіант.

Є проблеми.

  1. Неявні перетворення не дуже зручні для читання.
  2. Оскільки ви створюєте новий об’єкт, той, до якого перетворюєтесь, не побачить будь-яких змін оригінального об'єкта, що призводить до помилок.
  3. Якщо ви викидаєте предмет, це зайвий сміття для очищення.
  4. Якщо є проблема, це станеться тоді, коли відбудеться перетворення, а не тоді, коли річ буде створена, а помилки, які важче відстежити.

Загалом, є кращі рішення.

  1. Зробіть свою власну тонку обгортку, яка використовує інший клас / рамку внутрішньо.
  2. Створіть допоміжний метод, який приймає потрібні аргументи кондуктора і постачає той, що виправлений, повертаючи реальний об'єкт, не вказуючи аргумент, який вам не цікавий.
  3. Наслідуйте проблемний клас і поставте власний, приємніший конструктор.
  4. Зрозумійте, що передача додаткового аргументу насправді не така вже й велика угода.

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

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


1

Враховуючи описаний сценарій, ви можете подумати про це з точки зору часткового застосування функції.

Конструктор - це функція (принаймні теоретично; в C # ви можете створити "заводську функцію", яка викликає конструктор):

Func<double, Point, Flavour, Circle> MakeCircle = (r, p, f) => new Circle(r, p, f);

для часткового застосування буде достатньо наступного:

public static Func<T1, T2, R> Partial<T1, T2, T3, R>(this Func<T1, T2, T3, R> func, T3 t3)
   => (t1, t2) => func(t1, t2, t3);

Тепер ви можете отримати конструктор, який вимагає лише 2 параметрів:

Func<double, Point, Circle> MakeCardboardCircle = Circle.Partial(Flavours.Cardboard)

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

Circle c = MakeCardboardCircle(1.0, new Point(0, 0)))

До речі, це явно еквівалентно варіанту 2 вище, лише з більш функціональної точки зору.

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