Có những tình huống nào mà bạn nên thích một lớp không phải chữ hoa chữ thường không?
Martin Odersky cho chúng ta một điểm khởi đầu tốt trong khóa học Nguyên tắc lập trình hàm trong Scala (Bài giảng 4.6 - Khớp mẫu) mà chúng ta có thể sử dụng khi phải chọn giữa lớp và lớp ca. Chương 7 của Scala By Ví dụ có cùng một ví dụ.
Giả sử, chúng tôi muốn viết một trình thông dịch cho các biểu thức số học. Để giữ cho mọi thứ đơn giản ban đầu, chúng tôi giới hạn bản thân chỉ với các số và + phép toán. Những expres- sions như vậy có thể được biểu diễn như một hệ thống phân cấp lớp, với một lớp cơ sở trừu tượng Expr làm gốc, và hai lớp con Number và Sum. Sau đó, biểu thức 1 + (3 + 7) sẽ được biểu diễn dưới dạng
Tổng mới (Số mới (1), Tổng mới (Số mới (3), Số mới (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
}
Hơn nữa, thêm một lớp Prod mới không đòi hỏi bất kỳ thay đổi nào đối với mã hiện có:
class Prod(e1: Expr, e2: Expr) extends Expr {
def eval: Int = e1.eval * e2.eval
}
Ngược lại, thêm một phương thức mới yêu cầu sửa đổi tất cả các lớp hiện có.
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(")")
}
}
Vấn đề tương tự được giải quyết với các lớp trường hợp.
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
Thêm một phương pháp mới là một thay đổi cục bộ.
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(")")
}
}
}
Thêm một lớp Prod mới yêu cầu có khả năng thay đổi tất cả các đối sánh mẫu.
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) => ...
}
}
Chuyển biên từ videolecture 4.6 Khớp mẫu
Cả hai thiết kế này đều hoàn toàn ổn và việc lựa chọn giữa chúng đôi khi là vấn đề về phong cách, nhưng tuy nhiên, vẫn có một số tiêu chí quan trọng.
Một tiêu chí có thể là, bạn có thường xuyên tạo các lớp biểu thức con mới hơn hay bạn thường xuyên tạo các phương thức mới hơn? Vì vậy, đó là một tiêu chí xem xét khả năng mở rộng trong tương lai và khả năng mở rộng hệ thống của bạn.
Nếu những gì bạn làm chủ yếu là tạo các lớp con mới, thì thực sự giải pháp phân rã hướng đối tượng có ưu thế hơn. Lý do là nó rất dễ dàng và là một thay đổi cục bộ để chỉ tạo một lớp con mới với phương thức eval , trong đó như trong giải pháp hàm, bạn phải quay lại và thay đổi mã bên trong phương thức eval và thêm một trường hợp mới với nó.
Mặt khác, nếu những gì bạn làm là tạo ra nhiều phương thức mới, nhưng bản thân hệ thống phân cấp lớp sẽ được giữ tương đối ổn định, thì việc so khớp mẫu thực sự có lợi. Bởi vì, một lần nữa, mỗi phương thức mới trong giải pháp đối sánh mẫu chỉ là một thay đổi cục bộ , cho dù bạn đặt nó trong lớp cơ sở, hoặc thậm chí có thể nằm ngoài hệ thống phân cấp lớp. Trong khi một phương thức mới, chẳng hạn như hiển thị trong phân rã hướng đối tượng sẽ yêu cầu một số gia tăng mới là mỗi lớp con. Vì vậy, sẽ có nhiều phần hơn, mà bạn phải chạm vào.
Vì vậy, vấn đề của khả năng mở rộng này trong hai chiều, nơi bạn có thể muốn thêm các lớp mới vào một hệ thống phân cấp, hoặc bạn có thể muốn thêm các phương thức mới, hoặc có thể cả hai, đã được đặt tên là vấn đề biểu thức .
Hãy nhớ rằng: chúng ta phải sử dụng điều này như một điểm khởi đầu chứ không phải như tiêu chí duy nhất.