Vakasız bir sınıfı tercih etmeniz gereken durumlar var mı?
Martin Odersky, Scala'daki İşlevsel Programlama İlkeleri (Ders 4.6 - Örüntü Eşleştirme) dersinde, sınıf ve durum sınıfı arasında seçim yapmamız gerektiğinde kullanabileceğimiz iyi bir başlangıç noktası veriyor . Scala By Example'un 7. bölümü aynı örneği içermektedir.
Diyelim ki aritmetik ifadeler için bir yorumlayıcı yazmak istiyoruz. Başlangıçta işleri basit tutmak için kendimizi sadece sayılar ve + işlemlerle sınırlandırıyoruz. Bu tür ifadeler, kök olarak bir soyut temel sınıf İfade ve iki alt sınıf Sayı ve Toplam ile bir sınıf hiyerarşisi olarak temsil edilebilir. Daha sonra 1 + (3 + 7) ifadesi şu şekilde temsil edilir:
yeni Toplam (yeni Sayı (1), yeni Toplam (yeni Sayı (3), yeni Sayı (7)))
abstract class Expr {
def eval: Int
}
class Number(n: Int) extends Expr {
def eval: Int = n
}
class Sum(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval + e2.eval
}
Ayrıca, yeni bir Prod sınıfı eklemek, mevcut kodda herhangi bir değişiklik gerektirmez:
class Prod(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval * e2.eval
}
Buna karşılık, yeni bir yöntem eklemek, mevcut tüm sınıfların değiştirilmesini gerektirir.
abstract class Expr {
def eval: Int
def print
}
class Number(n: Int) extends Expr {
def eval: Int = n
def print { Console.print(n) }
}
class Sum(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval + e2.eval
def print {
Console.print("(")
print(e1)
Console.print("+")
print(e2)
Console.print(")")
}
}
Aynı problem vaka sınıfları ile çözüldü.
abstract class Expr {
def eval: Int = this match {
case Number(n) => n
case Sum(e1, e2) => e1.eval + e2.eval
}
}
case class Number(n: Int) extends Expr
case class Sum(e1: Expr, e2: Expr) extends Expr
Yeni bir yöntem eklemek yerel bir değişikliktir.
abstract class Expr {
def eval: Int = this match {
case Number(n) => n
case Sum(e1, e2) => e1.eval + e2.eval
}
def print = this match {
case Number(n) => Console.print(n)
case Sum(e1,e2) => {
Console.print("(")
print(e1)
Console.print("+")
print(e2)
Console.print(")")
}
}
}
Yeni bir Prod sınıfı eklemek, potansiyel olarak tüm desen eşleşmelerinin değiştirilmesini gerektirir.
abstract class Expr {
def eval: Int = this match {
case Number(n) => n
case Sum(e1, e2) => e1.eval + e2.eval
case Prod(e1,e2) => e1.eval * e2.eval
}
def print = this match {
case Number(n) => Console.print(n)
case Sum(e1,e2) => {
Console.print("(")
print(e1)
Console.print("+")
print(e2)
Console.print(")")
}
case Prod(e1,e2) => ...
}
}
Videolecture 4.6 Örüntü Eşleştirme'den transkript
Bu tasarımların her ikisi de mükemmeldir ve aralarında seçim yapmak bazen bir stil meselesidir, ancak yine de önemli olan bazı kriterler vardır.
Kriterlerden biri, daha sık yeni ifade alt sınıfları mı oluşturuyorsunuz yoksa daha sık yeni yöntemler mi oluşturuyorsunuz? Yani bu, gelecekteki genişletilebilirliğe ve sisteminizin olası genişletme geçişine bakan bir kriterdir.
Yaptığınız şey çoğunlukla yeni alt sınıflar oluşturmaksa, aslında nesneye yönelik ayrıştırma çözümünün üstünlüğü vardır. Bunun nedeni, bir eval yöntemiyle yeni bir alt sınıf oluşturmanın çok kolay ve çok yerel bir değişiklik olmasıdır ; burada işlevsel çözümde olduğu gibi, eval yönteminin içindeki kodu değiştirip yeni bir durum eklemeniz gerekir. ona.
Öte yandan, yaptığınız şey çok sayıda yeni yöntem yaratmaksa, ancak sınıf hiyerarşisinin kendisi nispeten sabit tutulacaksa, model eşleştirme aslında avantajlıdır. Çünkü, yine, desen eşleştirme çözümündeki her yeni yöntem , ister temel sınıfa, ister sınıf hiyerarşisinin dışına bile koysanız , yalnızca yerel bir değişikliktir . Nesne yönelimli ayrıştırmada göster gibi yeni bir yöntem, her alt sınıf için yeni bir artış gerektirecektir. Yani dokunmanız gereken daha fazla parça olacaktır.
Dolayısıyla, bir hiyerarşiye yeni sınıflar eklemek isteyebileceğiniz veya yeni yöntemler veya belki her ikisini birden eklemek isteyebileceğiniz iki boyutta bu genişletilebilirliğin problematiği ifade problemi olarak adlandırılmıştır .
Unutmayın: Bunu bir başlangıç noktası olarak kullanmalıyız ve tek kriter gibi değil.