В чем разница между миксинами и наследованием?
Я пытаюсь понять Mixins в контексте scala. В частности, я хотел знать разницу между концепциями наследования и Mixins. Wiki говорит, что существует важная разница между понятиями mixins и наследования, и поэтому я хотел ее понять.
В определении Mixin в wiki говорится:
Класс mixin действует как родительский класс, содержащий требуемые функциональные возможности. Подкласс может затем наследовать или просто повторно использовать эту функциональность , но не как средство специализации. Как правило, mixin будет экспортировать желаемую функциональность в дочерний класс, не создавая жесткую, единственную связь "есть". Здесь важное различие между понятиями mixins и inheritance заключается в том, что дочерний класс все еще может наследовать все функции родительского класса, но семантика о ребенке "является своего рода" родителем не обязательно обязательно применяется.
В приведенном выше определении я не могу понять утверждения, выделенные жирным шрифтом. что это означает, что
- Подкласс может наследовать функциональность в mixin, но не как средство специализации
- В mixins ребенок наследует все функции родительского класса, но семантика о том, что ребенок "является видом", не обязательно должен применяться родитель. - Как ребенок может продлить родителя, а не обязательно своего рода Родитель? Есть ли такой пример.
Заранее благодарим за любые разъяснения по поводу вышеизложенного.
Ответы
Ответ 1
Я не уверен, что правильно понял ваш вопрос, но если бы я это сделал, вы спрашиваете, как что-то может наследовать, не имея в действительности ничего общего с наследованием.
Микшины, однако, не наследуются - на самом деле это больше похоже на динамическое добавление набора методов в объект. В то время как наследование говорит: "Это нечто другое", миксины говорят: "Этот объект имеет некоторые черты этого другого". Вы можете видеть это в ключевом слове, используемом для объявления mixins: trait
.
Чтобы наглядно украсть пример с домашней страницы Scala:
abstract class Spacecraft {
def engage(): Unit
}
trait CommandoBridge extends Spacecraft {
def engage(): Unit = {
for (_ <- 1 to 3)
speedUp()
}
def speedUp(): Unit
}
trait PulseEngine extends Spacecraft {
val maxPulse: Int
var currentPulse: Int = 0
def speedUp(): Unit = {
if (currentPulse < maxPulse)
currentPulse += 1
}
}
class StarCruiser extends Spacecraft
with CommandoBridge
with PulseEngine {
val maxPulse = 200
}
В этом случае StarCruiser
не является CommandoBridge
или PulseEngine
; он имеет их, однако, и получает методы, определенные в этих чертах. Это Spacecraft
, как вы можете видеть, потому что он наследует от этого класса.
Стоит отметить, что когда trait
расширяет class
, если вы хотите сделать что-то with
, это свойство, оно должно расширить этот класс. Например, если у меня был class Dog
, я не мог бы иметь Dog with PulseEngine
, если только Dog
extended Spacecraft
. Таким образом, это не совсем похоже на добавление методов; однако он все еще схож.
Ответ 2
Признак (который называется mixin при смешивании с классом) похож на интерфейс в Java (хотя существует много различий), где вы может добавить дополнительные функции в класс, не обязательно имея отношение "является". Или вы можете сказать, что в целом черты объединяют функции, которые могут использоваться несколькими независимыми классами.
Чтобы предоставить вам пример из библиотеки Scala, Заказ [A] - это trait
, который обеспечивает реализацию для некоторых базовых операций сравнения (например, <
, <=
, >
, >=
) для классов, которые могут иметь данные с естественным упорядочением.
Например, скажем, у вас есть свой класс Number
и подклассы EvenNumber
и OddNumber
, как показано ниже.
class Number(val num : Int) extends Ordered[Number] {
override def compare(that : Number) = this.num - that.num
}
trait Half extends Number {
def half() = num / 2
}
trait Increment extends Number {
def increment() = num + 1
}
class EvenNumber(val evenNum : Int) extends Number(evenNum) with Half
class OddNumber(val oddNum : Int) extends Number(oddNum) with Increment
В приведенном выше примере классы EvenNumber
и OddNumber
share являются отношениями с Number
, но EvenNumber
не имеет отношения "is" с Half
ни OddNumber
share "является" отношением с Increment
.
Еще один важный момент - хотя класс Number
использует синтаксис extends Ordered
, это означает, что Number
имеет неявное отношение с суперклассом Ordered
, то есть Any
.
Ответ 3
Я думаю, что это очень зависит от использования. Scala, являющийся языком мультипарадигмы, делает его мощным, а также немного запутанным.
Я думаю, что Mixins очень эффективны при правильном использовании.
Микшины следует использовать для введения поведения и уменьшения болиров.
Признак в Scala может иметь реализации, и возникает соблазн расширить их и использовать их.
Черты могут использоваться для наследования. Его также можно назвать mixins, однако, на мой взгляд, это не лучший способ использования поведения mixin
. В этом случае вы можете думать о чертах как Java Abstract Classes. В результате вы получаете подклассы, которые являются "типом" суперкласса (черта).
Однако черты могут использоваться как proper mixins
. Теперь использование признака как mixin
зависит от реализации, которая "как вы ее смешиваете". В основном это простой вопрос, чтобы спросить себя. Это "Является ли подкласс признака действительно kind
признака или является поведением в поведении признаков, которое уменьшает шаблон".
Как правило, это лучше всего реализовать путем смешивания признаков с объектами, а не расширения признака для создания новых классов.
Например, рассмотрим следующий пример:
//All future versions of DAO will extend this
trait AbstractDAO{
def getRecords:String
def updateRecords(records:String):Unit
}
//One concrete version
trait concreteDAO extends AbstractDAO{
override def getRecords={"Here are records"}
override def updateRecords(records:String){
println("Updated "+records)
}
}
//One concrete version
trait concreteDAO1 extends AbstractDAO{
override def getRecords={"Records returned from DAO2"}
override def updateRecords(records:String){
println("Updated via DAO2"+records)
}
}
//This trait just defines dependencies (in this case an instance of AbstractDAO) and defines operations based over that
trait service{
this:AbstractDAO =>
def updateRecordsViaDAO(record:String)={
updateRecords(record)
}
def getRecordsViaDAO={
getRecords
}
}
object DI extends App{
val wiredObject = new service with concreteDAO //injecting concrete DAO to the service and calling methods
wiredObject.updateRecords("RECORD1")
println(wiredObject.getRecords)
val wiredObject1 = new service with concreteDAO1
wiredObject1.updateRecords("RECORD2")
println(wiredObject1.getRecords)
}
concreteDAO
- это признак, который распространяется на AbstractDAO
- это наследование
val wiredObject = new service with concreteDAO
-
Это правильное поведение смесителя
Так как служебная черта задает mixin
для AbstractDAO
. Было бы просто неправильно Service
продлить concreteDAO
в любом случае, потому что Service
требуется AbstractDAO
, это не тип AbstractDAO
.
Вместо этого вы создаете экземпляры Service
с разными миксинами.
Ответ 4
Я думаю, что речь идет о фактической иерархии классов. Например, Dog
является типом Animal
, если он распространяется из класса (наследование). Его можно использовать везде, где применим параметр Animal
.