2011-12-23 31 views

Trả lời

41

Có, bạn có lẽ nên bắt đầu bằng cách khớp mẫu thay vì kiểu khách truy cập. Xem này interview with Martin Odersky (nhấn mạnh của tôi):

Vì vậy, công cụ phù hợp cho công việc thực sự phụ thuộc vào hướng bạn muốn gia hạn. Nếu bạn muốn mở rộng với dữ liệu mới, bạn chọn phương thức tiếp cận hướng đối tượng cổ điển theo phương pháp ảo . Nếu bạn muốn để giữ cho dữ liệu được cố định và mở rộng bằng các thao tác mới, thì các mẫu phù hợp hơn nhiều. Thực tế, có một mẫu thiết kế - không phải là nhầm lẫn với mẫu phù hợp — trong lập trình hướng đối tượng được gọi là mẫu khách truy cập, có thể đại diện cho một số điều chúng tôi làm với một kiểu đối tượng theo cách hướng đối tượng, dựa trên phương pháp ảo công văn. Nhưng trong thực tế sử dụng mô hình khách truy cập là rất cồng kềnh. Bạn không thể thực hiện được nhiều thứ rất dễ dàng với mẫu phù hợp. Bạn kết thúc với khách truy cập rất nặng. Và nó cũng chỉ ra rằng với công nghệ VM hiện đại, đó là cách không hiệu quả hơn so khớp mẫu. Vì cả hai lý do này, tôi nghĩ rằng có một vai trò nhất định đối với mẫu phù hợp.

EDIT: Tôi nghĩ điều này đòi hỏi một chút giải thích tốt hơn và ví dụ. Mẫu khách truy cập thường được sử dụng để truy cập tất cả các nút trong một cây hoặc tương tự, ví dụ một cây cú pháp trừu tượng (AST). Sử dụng một ví dụ từ xuất sắc Scalariform. Scala mã định dạng Scalariform bằng cách phân tích Scala và sau đó đi qua AST, viết nó ra. Một trong những phương thức được cung cấp có AST và tạo ra một danh sách đơn giản tất cả các mã thông báo theo thứ tự. Phương pháp được sử dụng cho việc này là:

private def immediateAstNodes(n: Any): List[AstNode] = n match { 
    case a: AstNode    ⇒ List(a) 
    case t: Token     ⇒ Nil 
    case Some(x)     ⇒ immediateAstNodes(x) 
    case xs @ (_ :: _)    ⇒ xs flatMap { immediateAstNodes(_) } 
    case Left(x)     ⇒ immediateAstNodes(x) 
    case Right(x)     ⇒ immediateAstNodes(x) 
    case (l, r)     ⇒ immediateAstNodes(l) ++ immediateAstNodes(r) 
    case (x, y, z)     ⇒ immediateAstNodes(x) ++ immediateAstNodes(y) ++ immediateAstNodes(z) 
    case true | false | Nil | None ⇒ Nil 
} 

def immediateChildren: List[AstNode] = productIterator.toList flatten immediateAstNodes 

Đây là công việc có thể được thực hiện bởi mô hình khách truy cập trong Java, nhưng được thực hiện chính xác hơn bằng kết hợp mẫu trong Scala. Trong Scalastyle (Checkstyle for Scala), chúng tôi sử dụng một hình thức sửa đổi của phương pháp này, nhưng với một sự thay đổi tinh tế. Chúng tôi cần phải đi qua cây, nhưng mỗi kiểm tra chỉ quan tâm đến các nút nhất định. Ví dụ, đối với EqualsHashCodeChecker, nó chỉ quan tâm đến phương thức equals và hashCode được định nghĩa. Chúng tôi sử dụng các phương pháp sau đây:

protected[scalariform] def visit[T](ast: Any, visitfn: (Any) => List[T]): List[T] = ast match { 
    case a: AstNode    => visitfn(a.immediateChildren) 
    case t: Token     => List() 
    case Some(x)     => visitfn(x) 
    case xs @ (_ :: _)    => xs flatMap { visitfn(_) } 
    case Left(x)     => visitfn(x) 
    case Right(x)     => visitfn(x) 
    case (l, r)     => visitfn(l) ::: visitfn(r) 
    case (x, y, z)     => visitfn(x) ::: visitfn(y) ::: visitfn(z) 
    case true | false | Nil | None => List() 
} 

Thông báo chúng tôi đang đệ quy gọi visitfn(), không visit(). Điều này cho phép chúng tôi sử dụng lại phương pháp này để đi qua cây mà không cần sao chép mã. Trong số EqualsHashCodeChecker, chúng tôi có:

private def localvisit(ast: Any): ListType = ast match { 
    case t: TmplDef  => List(TmplClazz(Some(t.name.getText), Some(t.name.startIndex), localvisit(t.templateBodyOption))) 
    case t: FunDefOrDcl => List(FunDefOrDclClazz(method(t), Some(t.nameToken.startIndex), localvisit(t.localDef))) 
    case t: Any   => visit(t, localvisit) 
} 

Vì vậy, bản mẫu duy nhất ở đây là dòng cuối cùng trong khớp mẫu. Trong Java, mã trên cũng có thể được thực hiện như là một mẫu khách truy cập, nhưng trong Scala nó có ý nghĩa để sử dụng khớp mẫu. Cũng lưu ý rằng mã trên không yêu cầu sửa đổi cấu trúc dữ liệu đang được duyệt qua, ngoài việc xác định unapply(), điều này sẽ tự động xảy ra nếu bạn đang sử dụng các lớp chữ hoa chữ thường.

+0

Nice. Đây là đoạn mã ví dụ tốt nhất mà tôi có thể tìm thấy mẫu khách truy cập trong Scala với chức năng truy cập được chia tách từ mã 'hành động' cụ thể. – Core

+0

"Mẫu khách truy cập thường được sử dụng để truy cập mọi nút trong cây hoặc tương tự" - IMHO đây là quan niệm sai lầm phổ biến. Mẫu khách truy cập không liên quan gì đến việc trả tiền cho một số lượt truy cập vào một số nút, mặc dù tên của nó. Mục đích ban đầu trong GoF là có ngữ nghĩa ADT nghiêm ngặt, khi trình biên dịch yêu cầu bạn xử lý chính xác tất cả các kiểu con đã khai báo. Nó chỉ định các tập con phụ đã đóng và không cho phép thêm các kiểu phụ không chính thức. – beefeather

+0

Điều gì sẽ xảy ra khi bạn có hàng trăm loại nút và bạn cần phải truy cập từng loại nút với mã khác nhau? –

Các vấn đề liên quan