Когда использовать черты, в отличие от наследования и состава?

9

Существует три распространенных способа, AFAIK, для реализации возможности повторного использования, когда дело доходит до ООП.

  1. Наследование: обычно представляет собой отношения (утка это птица)
  2. Состав: как правило, для обозначения отношения (автомобиль имеет двигатель)
  3. Черты (например, ключевое слово trait в PHP): ... не совсем уверен в этом

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

Extrakun
источник
Вы можете объяснить немного больше о чертах, но могут ли эти черты быть просто еще одним словом для композиций?
Триларион
1
Мне бы очень хотелось знать почему. Не использовал их один раз.
Энди

Ответы:

6

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

По сути, вы хотите использовать черты, когда вы делаете уроки с различными комбинациями функций. Эта ситуация возникает чаще всего для людей, пишущих гибкие библиотеки для других. Например, вот объявление класса модульного теста, который я недавно написал с использованием ScalaTest :

class TestMyClass
  extends WordSpecLike
  with Matchers
  with MyCustomTrait
  with BeforeAndAfterAll
  with BeforeAndAfterEach
  with ScalaFutures

Раздел рамки испытаний имеют массу различных вариантов конфигурации, и каждая команда имеет разные предпочтения о том , как они хотят , чтобы делать вещи. Помещая параметры в черты (которые смешиваются при использовании withв Scala), ScalaTest может предложить все эти опции без необходимости создавать имена классов, например WordSpecLikeWithMatchersAndFutures, или тонну логических флагов времени выполнения, подобных WordSpecLike(enableFutures, enableMatchers, ...). Это позволяет легко следовать принципу « открыто / закрыто» . Вы можете добавлять новые функции и новые комбинации функций, просто добавив новую черту. Это также облегчает следование принципу сегрегации интерфейса , потому что вы можете легко поместить функции, которые не всегда необходимы, в черту.

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

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

Инъекционное Dependency рамки достижение многих из тех же целей во время компиляции на основе ввода программиста, но в основном обходной путь для языков программирования без надлежащей поддержки признака. Черты привносят эти зависимости в область средства проверки типов компилятора с более чистым синтаксисом и более простым процессом сборки, что делает более четкое различие между зависимостями времени компиляции и времени выполнения.

Карл Билефельдт
источник
Привет, это отличный ответ. Могу я спросить, как решить, когда использовать черты и когда использовать внедрение зависимостей. который, кажется, достичь того же.
Extrakun
Проницательное наблюдение. Смотрите мое редактирование.
Карл Билефельдт
Спасибо, что написали это. Мне любопытно, что вы ответили, что черты подобны композиции @KarlBielefeldt. Ваш класс может использоваться везде, где нужна эта черта, и вы все равно будете страдать от той же хрупкости, что и с обычным интерфейсом. Так что это похоже на подтип и наследование, нет? Я также не уверен, насколько он похож на DI ... если у вас есть определенные классы, которые расширяют черты, то не будут ли эти конкретные зависимости определены в той части иерархии, где определен этот класс, а не в верхней / входной точке код? Спасибо!
Allstar
Черты могут использоваться точно так же, как интерфейсы, для их свойств наследования, но это не то, что делает их уникальными. withОператор , что отличает их, и withявляется операцией композиции. И да, черты заменяют DI без необходимости определения в точке входа кода. Это одна из вещей, которая делает их предпочтительными по моему мнению.
Карл Билефельдт