Разве плохо создавать классы, единственной целью которых является неявное преобразование в другой класс?

10

Представьте себе ситуацию, когда мы используем библиотеку, которая позволяет вам создавать Circleобъекты, где вы можете указать радиус и центр круга, чтобы определить его. Однако по какой-то причине он также принимает обязательный flavourпараметр. Теперь предположим, что мне действительно нужно использовать Circleв моем собственном приложении, но для целей моего приложения я могу установить вкус Flavours.Cardboardкаждый раз.

Для того, чтобы «решить» это, я создаю свой собственный Circleкласс в другом пространстве имен, который только принимает radiusи в centerкачестве параметров, но имеет неявный преобразователь к внешней библиотеке Circleклассу , который просто создает Circle(this.radius, this.center, Flavours.Cardboard)объект. Поэтому везде, где мне нужен другой тип Circle, я разрешаю автоматическое преобразование.

Каковы последствия создания такого класса? Есть ли лучшие решения? Будет ли иметь какое-то значение, если бы мое приложение было API, построенным поверх этой внешней библиотеки, предназначенной для использования другими программистами?

user3002473
источник
Это похоже на вариацию шаблона адаптера , хотя в данном случае он имеет сомнительную ценность (это всего лишь удобство, позволяющее избежать установки параметра).
Роберт Харви
5
Почему бы не создать MakeCircle функцию ?
user253751
1
@immibis Тэй был моей первой мыслью. Когда я делаю игры, я часто создаю функцию, makePlayerаналогичную той, которая сама принимает только координаты для размещения игрока, но делегирует гораздо более сложный конструктор.
Carcigenicate

Ответы:

13

Хотя это не всегда плохо, довольно редко неявные преобразования являются вашим лучшим вариантом.

Есть проблемы.

  1. Неявные преобразования не очень читабельны.
  2. Поскольку вы создаете новый объект, любые изменения в исходном объекте не будут видны тем, в который вы преобразуетесь, что приведет к ошибкам.
  3. Если вы выбрасываете объект, это лишний мусор для очистки.
  4. Если есть проблема, это произойдет, когда произойдет преобразование, а не когда объект будет создан, что делает ошибки более трудными для отслеживания.

В общем, есть лучшие решения.

  1. Сделайте свою собственную тонкую оболочку, которая использует другой класс / фреймворк для внутреннего использования.
  2. Создайте вспомогательный метод, который принимает требуемые аргументы конструктора и предоставляет фиксированный, возвращая реальный объект без указания аргумента, который вас не интересует.
  3. Унаследуйте от класса задачи и предоставьте свой собственный, более хороший конструктор.
  4. Поймите, что передача дополнительного аргумента на самом деле не такая уж большая проблема.

Лично я считаю, что №2 проще всего реализовать и наименее обременителен в дизайне. С другими могут быть все в порядке, учитывая ситуацию и то, что еще вы пытаетесь сделать с этими классами.

Неявное преобразование является последним средством, и оно мне действительно стоит того, когда у меня есть функторы стиля C ++, которые я пытаюсь создать - объекты стратегии, которые я неявно преобразую в типы делегатов.

Telastyn
источник
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 выше, просто с более функциональной точки зрения.

ла-юмба
источник