الفرق بين الميراث من السمات والشرح النوع الذاتي

StackOverflow https://stackoverflow.com/questions/2224932

سؤال

في سكالا، رأيت البنيات

trait T extends S

و

trait T { this: S =>

تستخدم لتحقيق أشياء مماثلة (وهي الأساليب المجردة في S يجب تعريفه قبل إنشاء مثيل). ما الفرق بينهما؟ لماذا تستخدم واحدة على الآخر؟

هل كانت مفيدة؟

المحلول

كنت أستخدم الأنواع الذاتية لإدارة الاعتماد: هذه السمة تتطلب سمة أخرى يتم خلطها في. وأستطيع استخدام الميراث لتحسين سمة أو واجهة أخرى.

تماما كمثال:

trait FooService

trait FooRemoting { this : FooService => }
trait FooPersistence { this : FooService => }

object Services extends FooService with FooRemoting with FooPersistence

الآن، إذا كانت Fooremoting و FooPersistence قد تورثها كلاهما موروث من Fooservice، فإن Fooservice لديه أعضاء وطرق، كيف تبدو الخدمات؟

في حين للميراث، سيكون لدينا شيء مثل:

trait Iterator[T] {
  def hasNext : boolean
  def next : T
}

trait InfiniteIterator[T] extends Iterator[T] {
  def hasNext = true
}

نصائح أخرى

تتيح لك التعليقات التوضيحية ذاتية التعبير عن التبعيات الدورية. على سبيل المثال:

trait A extends B
trait B { self: A => }

هذا غير ممكن مع الميراث البسيط.

منذ طرح السؤال الذي صادفته عبر هذه الوظائف:

spiros tzavellas. يتحدث عن استخدام سمة باعتبارها الواجهة العامة والنوع الذاتي كمساعد يجب خلطها من خلال فئة التنفيذ.

في الختام، إذا أردنا نقل أسلوب التطبيقات داخل الصفات، فإننا نخاطر بملامح واجهة تلك الصفات مع الطرق المجردة التي تدعم تنفيذ الأساليب الملموسة ولا علاقة لها بالمسؤولية الرئيسية عن السمة. الحل لهذه المشكلة هو نقل تلك الطرق المجردة في الصفات الأخرى وتأكل السمات معا باستخدام التعليقات التوضيحية ذات النوع الذاتي والميراث المتعدد.

علي سبيل المثال:

trait PublicInterface { this: HelperTrait =>
  // Uses helperMethod
}

trait HelperTrait {
  def helperMethod = // ...
}

class ImplementationClass extends PublicInterface with HelperTrait

جولة في سكالا يناقش استخدام التعليقات التوضيحية ذات النوع الذاتي مع أعضاء نوع مجردة - من المفترض أنه غير ممكن extend عضو من نوع مجردة (؟)

الجواب هو "دائرية". ولكن ليس فقط.

خط التعليق التوضيحي النوع الذاتي بالنسبة لي المشكلة الأساسية للميراث: ما ترثه لا يمكنه استخدام ما أنت عليه. مع نوع الذات، كل شيء يصبح سهلا.

النمط الخاص بي هو ما يلي ويمكن اعتباره كعك كعكة مفرط:

trait A { self: X => def a = reuseme}
trait B { self: X => def b = a }
class X extends A with B { def reuseme=null }

يمكنك تنفجر فصلك في سلوكيات متعددة يمكن استدعاؤها من أي مكان في التجميع، مع البقاء مكتوبة نظيفة. لا حاجة للاتذار المؤلم في كثير من الأحيان (وغير صحيح) تم تحديدها مع نمط الكعكة.

نصف (إن لم يكن مجمل) من أطر Java di DI Colfoluted في السنوات العشر الأخيرة قد كرس للقيام بذلك، بالطبع دون الكتابة. لا يزال الناس يستخدمون Java في هذا المجال يفقدون بوضوح وقتهم: "Scala Ouakbar".

أعرف أن هذا السؤال قديم ولكن أود إضافة بعض التوضيح والأمثلة.

هناك ثلاث فروق رئيسية بين ميراث السمات وأنواعها الذاتية.

دلالات

الميراث هي واحدة من العلاقات مع اقتران نموذج كائن، إذا تمتد ب، وهذا يعني أن A هو B.

دعنا نقول أن لدينا الكود التالي،

trait Animal {
  def stop():Unit = println("stop moving")
}

class Dog extends Animal {
  def bark:String = "Woof!"
}

val goodboy:Dog = new Dog

goodboy.bark
// Woof!

نحن نقول أن الكلب يكون حيوان. يمكننا إرسال الرسائل bark و stop ل goodboy لأن الكلب، فهو يفهم كلتا الطريقتين.

لنفترض الآن أن لدينا سمة جديدة،

trait Security {
  this: Animal =>
  def lookout:Unit = { stop(); println("looking out!") }
}

هذا الوقت الأمن ليس حيوان، وهذا جيد لأنه سيكون غير صحيح شبه صحيح إذا أكرنا أن الأمان هو حيوان، فهي مفاهيم مختلفة، يمكن استخدامها معا.

حتى الآن يمكننا إنشاء نوع جديد من الكلاب،

val guardDog = new Dog with Security

guardDog.lookout
// stop moving
// looking out!

guardDog هو كلب، حيوان وأمن. انها تفهم stop, bark و lookout لأن كلب مع الأمن.

ولكن ماذا يحدث إذا قمنا بإنشاء كلب جديد مثل هذا؟

val guardDog2:Dog = new Dog with Security
guardDog2.lookout // no such method!

guardDog2 هو مجرد كلب، لذلك لا يمكننا الاتصال lookout طريقة. (Okok، إنه كلب مع أمن، لكننا نرى مجرد كلب)

التبعيات الدورية

تتيح لنا الأنواع الذاتية إنشاء تبعيات دورية بين الأنواع.

trait Patient {
  this: Reader =>
  def isQuite:Boolean = isReading
  def isSlow:Boolean = true
}

trait Reader {
  this: Patient =>
  def read():Unit = if(isSlow) println("Reading Slow...") else println("Reading Fast...")
  def isReading = true
}

val person = new Patient with Reader

لا تجميع التعليمات البرمجية التالية.

trait Patient extends Reader { /** code **/}

trait Reader extends Patient { /** code **/ }

هذا النوع من الكود شائع جدا في حقن التبعية (نمط الكعكة).

براعه

أخيرا وليس آخرا، الذي يستخدم سماتنا يمكن أن يقرر الترتيب الذي يتم استخدامه فيه، بحيث لا يمكن تختلف النتيجة النهائية على هذه الرسوم الناتجة هي نفسها.

مع الميراث الطبيعي لا يمكننا القيام بذلك، يتم إصلاح العلاقات بين الصفات والفئات.

trait Human {
  def isGoodForSports:Boolean
}

trait Programmer extends Human {
  def readStackOverflow():Unit = println("Reading...")
  override def isGoodForSports: Boolean = false
}

trait Sportsman extends Human {
  def play():Unit = println("Playing something")
  override def isGoodForSports: Boolean = true
}

val foo = new Programmer with Sportsman
foo.isGoodForSports
// true

val bar = new Sportsman with Programmer
bar.isGoodForSports
// false

آمل أن يكون هذا مفيدا.

على الرغم من أنه لا يجيب على سؤالك، إلا أنني كنت أحاول فهم التعليقات التوضيحية ذات النوع الذاتي وتوضع أساسا في الإجابات، وانتهى بطريقة أو بأخرى ركوب الدراجات من خلال اختلافات لسؤالك، والذي يركز على استخدام التعليقات التوضيحية ذات النوع الذاتي للقول التبعيات.

لذلك أنا هنا نشر وصف لحالة الاستخدام حيث يتم توضيح التعليقات التوضيحية ذات النوع الذاتي بشكل جيد، وهي شيء مثل حالة آمنة من النوع من "هذا" باعتبارها فرعية:

http://programming-scala.labs.oreilly.com/ch13.html#nselypeannotationsandabstracttypememem.

على أمل أن يكون من المفيد لأولئك الذين ينتهي بهم الأمر على هذا السؤال بالصدفة (، مثلي، لم يكن لديهم وقت لقراءة كتاب Scala قبل البدء في استكشاف :-))

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top