Является ли преобразование объектов неизбежностью реальности, когда необходимо разработать модульную архитектуру?

Распространено мнение, что приведение объектов является плохой практикой, и его следует избегать, например Почему следует следует избегать приведения? вопрос получил несколько ответов с вескими аргументами:

  1. Джерри Коффин:
    #P2#
  2. Эрик Липперт:
    #P3#

Переходя к моему вопросу, недавно я начал изучать исходный код известного проекта с открытым исходным кодом AutoFixture изначально был разработан Марком Симаном, что я очень ценю.

Одним из основных компонентов библиотеки является интерфейс ISpecimenBuilder которые определяют какой-то абстрактный метод:

object Create(object request, ISpecimenContext context);

Как вы можете видеть, тип параметра запроса является объектом, и поэтому он принимает совершенно разные типы, разные реализации интерфейса обрабатывают разные запросы по их типу времени выполнения, проверяя, является ли это чем-то, с чем они имеют дело, в противном случае возвращая какое-то представление без ответа.

Кажется, что дизайн интерфейса не соответствует «хорошей практике», согласно которой приведение объектов должно использоваться редко.

Я думал про себя, есть ли лучший способ разработать этот контракт таким образом, чтобы победить все приведения, но не смог найти никакого решения.
Очевидно, что параметр объекта можно заменить каким-то маркерным интерфейсом, но он не будет избавьте нас от проблемы приведения, я также подумал, что можно использовать некоторые варианты шаблона посетителя, как описано здесь, но он не кажется очень масштабируемым, у посетителя должны быть десятки различных методов, так как существует так много разных реализаций интерфейса, способных обрабатывать разные типы запросов.

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

Подводя итог, является ли приведение объектов и очень общие контракты неизбежностью реальности, когда есть необходимость в разработке модульной и расширяемой архитектуры?


person YuvShap    schedule 03.02.2019    source источник
comment
Этот вопрос, как и тот, на который вы ссылались, вероятно, слишком широк, чтобы быть здесь по теме. Я думаю, что вам может быть лучше на обмене стека разработки программного обеспечения, но сначала проверьте их часто задаваемые вопросы (я не уверен на 100%).   -  person Wai Ha Lee    schedule 04.02.2019
comment
Ответ действительно в цитате. That's not to say there's never a reason to do such a conversion, but anytime it happens, it should prompt the question of whether you could re-design the code so the correct type was used throughout.   -  person Kevin Gosse    schedule 04.02.2019
comment
Из ответов на другой вопрос вы можете ответить на свой вопрос в зависимости от вашего дизайна и того, как вы создаете свою модульную и расширяемую архитектуру. Вы всегда можете знать, какой тип будет у вас под рукой, и поэтому не будет необходимости в кастинге...   -  person Icepickle    schedule 04.02.2019
comment
@KevinGosse, как написано, я задал себе вопрос, но не смог найти способ изменить код, это не значит, что другие не могут дать другие предложения, которые я надеюсь получить.   -  person YuvShap    schedule 04.02.2019
comment
@YuvShap xkcd.com/1170... Вы действительно должны выяснить, какое ограничение заставило авторов использовать object, а затем посмотрите, не применима ли эта причина в вашем конкретном случае(ах) использования... (Существует очень большая вероятность того, что авторы уже потратили много времени на поиск альтернативного решения, и это единственное, что сработало до сих пор)   -  person Alexei Levenkov    schedule 04.02.2019


Ответы (1)


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

Если бы сегодня мне пришлось писать AutoFixture с нуля, я бы определенно сделал некоторые вещи по-другому. В частности, я бы не стал разрабатывать повседневный API вокруг чего-то вроде ISpecimenBuilder. Скорее, я бы разработал API для обработки данных на основе концепции функторов и монад, как здесь.

Этот дизайн полностью основан на дженериках, но требует статически типизированных стандартных блоков (также описанных в статье), известных во время компиляции.

Это тесно связано с тем, как работает что-то вроде QuickCheck. Когда вы пишете тесты на основе QuickCheck, вы должны предоставить генераторы для всех ваших собственных пользовательских типов. Haskell не поддерживает приведение значений во время выполнения, а вместо этого полагается исключительно на обобщения и некоторую автоматизацию времени компиляции. Конечно, дженерики в Haskell более мощные, чем в C#, поэтому вы не обязательно сможете перенести знания, полученные из Haskell, в C#. Однако это предполагает, что можно писать код полностью, не полагаясь на приведение типов во время выполнения.

Однако AutoFixture поддерживает определяемые пользователем типы без необходимости написания пользователем собственных генераторов. Это делается с помощью .NET Reflection. В .NET API Reflection не имеет типов; все методы создания объектов и вызова членов принимают object в качестве входных данных и возвращают object в качестве выходных данных.

Любое приложение, библиотека или фреймворк, основанные на Reflection, должны будут выполнять некоторое приведение во время выполнения. Я не вижу, как это обойти.

Можно ли писать генераторы данных без Reflection? Я не пробовал следующее, но, возможно, можно было бы принять стратегию, при которой можно было бы написать «код» для генератора данных непосредственно в IL и использовать Reflection emit для динамической компиляции сборки в памяти, которая содержит генераторы.

Это немного похоже на то, как Контейнер Hiro работает, IIRC. Я предполагаю, что вокруг этой концепции можно разработать другие типы универсальных фреймворков, но я редко вижу, как это делается в .NET.

person Mark Seemann    schedule 14.02.2019
comment
Большое спасибо за ваш ответ, тот факт, что AutoFixture основан на отражении, объясняет, почему возвращаемый тип ISpecimenBuilder является объектом, однако я не понял, как это влияет на типы входных параметров. Почему Тот факт, что тип первого параметра (запроса) является объектом, зависит от отражения, если отражение используется для генерации вывода. Должна быть другая причина для того, чтобы определить запрос таким, какой он есть, и теоретически это может быть любая другая абстракция, так какова мотивация определить его как максимально возможную абстракцию в языке? я что-то упускаю? - person YuvShap; 18.02.2019
comment
@YuvShap Прошло много времени, поэтому я не помню всех подробностей, но, хотя я уже программировал более десяти лет, когда разрабатывал AutoFixture, были вещи, которые я не знал, как делать. Одна проблема заключалась в том, что я хотел иметь возможность запрашивать различные вещи, такие как Type, PropertyInfo или ParameterInfo, а также свои собственные пользовательские типы. Общим для них является только тип Object. В эти дни я, возможно, решил определить пользовательский тип суммы, закодированный как посетитель, но тогда я не осознавал, что могу это сделать. - person Mark Seemann; 19.02.2019
comment
спасибо за подробное объяснение, которое имеет большой смысл. - person YuvShap; 19.02.2019