Mixins по сравнению с составом в scala

В мире Java (более точно, если у Вас нет множественного наследования/mixins) эмпирическое правило довольно просто: "Польза возражает составу по наследованию классов".

Я хотел бы знать, изменяется ли это, если Вы также рассматриваете mixins, особенно в scala?
mixins считают способом множественного наследования или большей композицией классов?
Существует ли также "Состав объекта пользы по композиции классов" (или наоборот) инструкция?

Я видел некоторые примеры, когда люди используют (или злоупотребление) mixins, когда объектный состав мог также сделать задание, и я не всегда уверен, какой лучше. Мне кажется, что можно достигнуть весьма схожих вещей с ними, но существуют некоторые различия также, некоторые примеры:

  • видимость - с mixins, все становится частью общедоступного API, который не имеет место с составом.
  • многословие - в большинстве случаев mixins является менее подробным и немного легче использовать, но оно не всегда имеет место (например, если Вы также используете сам типы в сложных иерархиях),

Я знаю, что короткий ответ, "Он зависит", но вероятно существует некоторая типичная ситуация, когда это или это лучше.

Некоторые примеры инструкций, которые я мог придумать до сих пор (принимающий у меня есть две черты A и B и A, хотят использовать некоторые методы от B):

  • Если Вы хотите расширить API с методами от B затем mixins, иначе составом. Но не помогает, не является ли класс/экземпляр, который я создаю, частью общедоступного API.
  • Если Вы хотите использовать некоторые шаблоны, которым нужен mixins (например, Наращиваемый Шаблон Черты) затем, это - легкое решение.
  • Если у Вас есть круговые зависимости затем mixins с сам, типы могут помочь. (Я стараюсь избегать этой ситуации, но это не всегда легко),
  • Если Вы хотите некоторые динамические, решения во время выполнения, как сделать, состав затем возражает составу.

Во многих случаях mixins, кажется, легче (и/или менее подробным), но я совершенно уверен, что у них также есть некоторые ловушки, как "Класс бога" и другие, описанные в двух artima статьях: часть 1, часть 2 (BTW мне кажется, что большинство других проблем не важно/не настолько серьезное для scala).

У Вас есть больше подсказок как они?

76
задан Sandor Murakozi 6 August 2010 в 09:43
поделиться

2 ответа

Многие проблемы, с которыми люди сталкиваются с добавками, можно предотвратить в Scala, если вы только добавляете абстрактные черты в определения своих классов, а затем добавляете соответствующие конкретные черты в объект. время создания. Например,

trait Locking{
   // abstract locking trait, many possible definitions
   protected def lock(body: =>A):A
}

class MyService{
   this:Locking =>
}

//For this time, we'll use a java.util.concurrent lock
val myService:MyService = new MyService with JDK15Locking 

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

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

40
ответ дан 24 November 2019 в 11:23
поделиться

Другие отличия, которые вы не упомянули:

  • Классы признаков не имеют независимого существования:

( Programming Scala )

Если вы обнаружите, что конкретный признак чаще всего используется в качестве родительского для других классов, так что дочерние классы ведут себя как родительский признак, тогда рассмотрите возможность определения признака вместо этого как класс, чтобы сделать эту логическую взаимосвязь более ясной.
(Мы сказали, что ведет себя как , а не как , потому что первое является более точным определением наследования, основанным на принципе подстановки Лискова - см., Например, [Martin2003]).)

[Martin2003]: Роберт К. Мартин, Гибкая разработка программного обеспечения: принципы, шаблоны и практики, Prentice-Hall, 2003 г.

  • миксины ( trait ) не имеют параметров конструктора.

Отсюда совет , взятый из Programming Scala :

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

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

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

10
ответ дан 24 November 2019 в 11:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: