2010-09-10 27 views
5

Trước khi bạn nói với tôi rằng đã có một câu hỏi tương tự, vâng, tôi biết, tôi đã đọc it. Nhưng câu hỏi ở đó tập trung vào khi, tôi quan tâm đến lý do tại sao.Một lý do chính đáng để sử dụng mẫu thiết kế của Khách truy cập là gì?

Tôi hiểu cách hoạt động của mọi thứ. Ví dụ: động vật, chó, mèo luôn hoạt động như một nét duyên dáng.

Vấn đề là mã này

int main() 
{ 
    Cat c; 
    Sound theSound; 
    c.letsDo(&theSound); 
} 

dường như không tự nhiên đối với tôi. Tại sao? Tôi có nghĩa là, vâng, theo cách này tôi có các mô hình Chó và Mèo không phân biệt (lần đầu tiên tôi sử dụng từ đó trong tiếng Anh btw) vì implentation thực được ẩn dưới lớp Âm thanh nhưng không phải chỉ là một cách để cân nhắc mã của bạn? Không phải là đa hình đủ để làm một cái gì đó như thế này?

Với tôi sự khác biệt là với đa hình, bạn phải chỉnh sửa từng lớp (nhưng mô hình vẫn giữ nguyên, phải không?) Trong khi bạn chỉ cần chỉnh sửa một lớp với mẫu thiết kế của khách truy cập.

Trả lời

8

Mẫu khách truy cập cho phép bạn làm điều gì đó, mà chỉ đơn giản dựa vào tính đa hình không: làm việc với các trường hợp sử dụng không lường trước được. Nếu bạn đang viết một thư viện, đây là một điểm quan trọng. Hãy để tôi xây dựng:

Hãy xem xét một ví dụ cổ điển cho việc sử dụng mẫu khách truy cập, cụ thể là, thao tác trên các nút của một số cây abstract syntax. Để thêm một số chi tiết, nói rằng, bạn vừa viết một thư viện phân tích cú pháp cho SQL, nó lấy các chuỗi, phân tích chúng và trả về một AST cho những thứ mà nó tìm thấy trong đầu vào. Trừ khi bạn có thể dự đoán tất cả các trường hợp sử dụng tiềm năng mã khách hàng của bạn có thể có cho một AST như vậy, bạn phải cung cấp một cách "chung" để đi bộ AST. Cung cấp chức năng truy cập giống như DOM (getNodeType, getParentNode, getPreviousNode) là một cách. Vấn đề ở đây là, điều này đặt một gánh nặng lên các khách hàng của thư viện của bạn, bởi vì họ cần tự mình thực hiện công việc. Thậm chí nhiều hơn, họ cần phải biết chi tiết hơn, mà con trỏ để làm theo cho từng loại nút có thể:

void 
walk_tree(AstNode* node) 
{ 
    switch(node->getNodeType()) { 
    case SELECT_NODE: 
     for(AstNode* child = node->getFirstChild(); child; child = child->getNextNode()) { 
      walk_tree(child); 
     } 
     break; 
    ... 
    } 
} 

Mẫu khách truy cập di chuyển gánh nặng này từ khách hàng vào thư viện.

+0

Chính xác, hành vi có thể được thực hiện bởi bên thứ ba. –

+0

Ok, nhưng nếu bạn định nghĩa trình phân tích cú pháp cho SQL thì bạn đã có định nghĩa ngữ pháp chưa? Trường hợp sử dụng bất ngờ ở đâu? Trên thực tế kể từ khi chúng tôi đang nói về phân tích cú pháp, không phải là một trình tạo phân tích cú pháp một ví dụ thích hợp hơn? Ở đó bạn có một ngữ pháp tùy ý, do đó bạn phải định nghĩa một lớp tập hợp cây chung. – dierre

+0

Nó không phải về cấu trúc của SQL. Đó là về cách khách hàng có thể sử dụng kết quả/đầu ra của thư viện của bạn. Ví dụ trình phân tích cú pháp được sử dụng đơn giản vì nó hơi "cổ điển". BTW, nếu bạn có một API hoàn toàn chung (tùy ý "DOM" giống như các nút) mà không có một cấu trúc cố định, bạn có thể tốt hơn với cách tiếp cận chung accessor thay vì mô hình khách truy cập. – Dirk

3

Giả sử bạn có một số nội dung cơ bản được xác định trong thư viện bạn không sở hữu và bạn cần phải mở rộng nó. Giống như:

// In base lib: 
interface ISomething { 
    void DoSomething(); 
} 

class Something1 : ISomething { 
    // ... 
} 

class Something2 : ISomething { 
    // ... 
} 

Polymorphism phép bạn xác định những điều mới mẻ mà bạn có thể thực hiện các thao tác trên:

// In your lib: 
class MySomething : ISomething { 
} 

Và bây giờ là lib cơ sở có thể làm việc với MySomething của bạn như thể nó đã được định nghĩa nó. Những gì nó không cho phép bạn làm là thêm hoạt động mới. DoSomething là điều duy nhất chúng tôi có thể thực hiện với số ISomething. Mẫu khách truy cập địa chỉ đó.

Nhược điểm là sử dụng mẫu khách truy cập chi phí bạn có khả năng xác định các loại mới như chúng tôi vừa hiển thị.Thực tế là hầu hết các ngôn ngữ cho phép bạn thêm hoạt động hoặc loại dễ dàng, nhưng không phải cả hai, được gọi là expression problem.

Mẫu khách truy cập là một mô hình tuyệt vời, nhưng tôi chưa bao giờ thực sự tìm thấy nhu cầu cho nó ngoài việc triển khai trình biên dịch.

+0

Bạn không thể thêm loại mới vì Khách truy cập của bạn sẽ vẫn là thành phần của thư viện, phải không? – dierre

+1

Lớp khách truy cập sẽ được định nghĩa trong lib cơ sở và sẽ có một bộ phương thức cố định, một cho mỗi loại. Bạn không thể thêm các kiểu mới trong lib của mình vì bạn không thể thay đổi khách truy cập và bạn không thể di chuyển khách truy cập vào thư viện của mình vì các loại trong lib cơ sở cần tham chiếu nó trong phương thức 'accept()' của chúng. – munificent

+0

đã nhận được. cảm ơn! – dierre

1

Tôi đã sử dụng mẫu khách truy cập khi tôi có một cây đối tượng và cần in nội dung theo nhiều cách. Comma-sep, XML, bất cứ điều gì. Thay vì thêm vào lớp cây một phương thức in mới cho mỗi định dạng đầu ra, tôi đã sử dụng mẫu khách truy cập và tạo các lớp CommaSepVisitor, XMLVisitor và HTMLVisitor. Mã cây không bao giờ thay đổi khi tôi thêm nhiều loại khách truy cập nên tôi không bao giờ giới thiệu lỗi. Bản thân các vị khách cũng dễ viết.

2

Mẫu khách truy cập rất hữu ích.

Có ít nhất ba lý do lớn để sử dụng nó:

  1. Giảm gia tăng của các mã mà chỉ hơi khác nhau khi thay đổi cấu trúc dữ liệu.

  2. Áp dụng cùng một tính toán cho một số cấu trúc dữ liệu, mà không thay đổi mã thực hiện tính toán.

  3. Thêm thông tin vào thư viện kế thừa mà không thay đổi mã cũ.

Vui lòng xem an article I've written about this.

Chúc mừng

+0

tnx, tôi sẽ đọc nó. – dierre

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