Sử dụng mẫu khách truy cập với phân cấp đối tượng lớn


12

Bối cảnh

Tôi đã sử dụng với một hệ thống phân cấp các đối tượng (một cây biểu thức) một mẫu khách truy cập "giả" (giả, như trong nó không sử dụng công văn kép):

 public interface MyInterface
 {
      void Accept(SomeClass operationClass);
 }

 public class MyImpl : MyInterface 
 {
      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }
 }

Thiết kế này, tuy nhiên có thể nghi ngờ, khá thoải mái vì số lần triển khai MyInterface là đáng kể (~ 50 trở lên) và tôi không cần thêm các hoạt động bổ sung.

Mỗi triển khai là duy nhất (đó là một biểu thức hoặc toán tử khác nhau) và một số là các vật liệu tổng hợp (nghĩa là các nút toán tử sẽ chứa các nút toán tử / lá khác).

Traversal hiện được thực hiện bằng cách gọi thao tác Chấp nhận trên nút gốc của cây, lần lượt gọi Chấp nhận trên mỗi nút con của nó, lần lượt ... và cứ thế ...

Nhưng đã đến lúc tôi cần thêm một thao tác mới , chẳng hạn như in ấn đẹp:

 public class MyImpl : MyInterface 
 {
      // Property does not come from MyInterface
      public string SomeProperty { get; set; }

      public void Accept(SomeClass operationClass)
      {   
           operationClass.DoSomething();
           operationClass.DoSomethingElse();
           // ... and so on ...
      }

      public void Accept(SomePrettyPrinter printer)
      {
           printer.PrettyPrint(this.SomeProperty);
      }
 }    

Về cơ bản tôi thấy hai tùy chọn:

  • Giữ nguyên thiết kế, thêm một phương thức mới cho hoạt động của tôi vào mỗi lớp dẫn xuất, với chi phí bảo trì (không phải là một tùy chọn, IMHO)
  • Sử dụng mẫu Khách truy cập "thật", với chi phí có thể mở rộng (không phải là một tùy chọn, như tôi dự kiến ​​sẽ có nhiều triển khai hơn trên đường đi ...), với khoảng hơn 50 quá tải của phương thức Truy cập, mỗi phương thức phù hợp với một triển khai cụ thể ?

Câu hỏi

Bạn có muốn giới thiệu và sử dụng mẫu Khách truy cập không? Có mô hình nào khác có thể giúp giải quyết vấn đề này không?


1
Có lẽ một chuỗi các nhà trang trí sẽ thích hợp hơn?
MattDavey

Một số câu hỏi: làm thế nào để thực hiện khác nhau? cấu trúc của hệ thống phân cấp là gì? và nó luôn luôn có cùng cấu trúc? bạn luôn cần phải đi qua cấu trúc theo cùng một thứ tự?
jk.

@MattDavey: vì vậy bạn muốn giới thiệu và có một người trang trí cho mỗi lần thực hiện và vận hành?
T. Fabre

2
@ T.Fabre thật khó để nói. Có hơn 50 người triển khai MyInterface.. tất cả các lớp đó có triển khai duy nhất DoSomethingDoSomethingElsekhông? Tôi không thấy nơi khách truy cập của bạn thực sự đi qua hệ thống phân cấp - nó trông giống như một facadelúc này ..
MattDavey

phiên bản của C # là gì bạn có lambdas không? hay linq? theo ý của bạn
jk.

Câu trả lời:


13

Tôi đã sử dụng mẫu khách truy cập để thể hiện các cây biểu hiện trong hơn 10 năm qua trên sáu dự án quy mô lớn bằng ba ngôn ngữ lập trình và tôi rất hài lòng với kết quả này. Tôi tìm thấy một vài điều làm cho việc áp dụng mô hình dễ dàng hơn nhiều:

Không sử dụng quá tải trong giao diện của khách truy cập

Đặt kiểu vào tên phương thức, nghĩa là sử dụng

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
}

thay vì

IExpressionVisitor {
    void Visit(IPrimitiveExpression expr);
    void Visit(ICompositeExpression expr);
}

Thêm một phương thức "không xác định" vào giao diện khách truy cập của bạn.

Nó sẽ giúp người dùng không thể sửa đổi mã của bạn:

IExpressionVisitor {
    void VisitPrimitive(IPrimitiveExpression expr);
    void VisitComposite(ICompositeExpression expr);
    void VisitExpression(IExpression expr);
};

Điều này sẽ cho phép họ xây dựng các triển khai của riêng mình IExpressionIVisitor"hiểu" các biểu thức của họ bằng cách sử dụng thông tin loại thời gian chạy trong việc thực hiện VisitExpressionphương thức bắt tất cả của họ .

Cung cấp một triển khai IVisitorgiao diện không làm gì mặc định

Điều này sẽ cho phép người dùng cần xử lý một tập hợp con các loại biểu thức xây dựng khách truy cập của họ nhanh hơn và làm cho mã của họ miễn nhiễm với bạn thêm nhiều phương thức vào IVisitor. Ví dụ: viết một khách truy cập thu thập tất cả các tên biến từ biểu thức của bạn trở thành một nhiệm vụ dễ dàng và mã sẽ không bị hỏng ngay cả khi bạn thêm một loạt các loại biểu thức mới vào IVisitorsau này.


2
Bạn có thể làm rõ lý do tại sao bạn nói Do not use overloads in the interface of the visitor?
Steven Evers

1
Bạn có thể giải thích lý do tại sao bạn không đề xuất và sử dụng quá tải? Tôi đã đọc ở đâu đó (trên oodesign.com, thực sự) rằng nó không thực sự quan trọng cho dù tôi có sử dụng quá tải hay không. Có bất kỳ lý do cụ thể tại sao bạn thích thiết kế đó?
T. Fabre

2
@ T.Fabre Không quan trọng về tốc độ, nhưng nó quan trọng về khả năng đọc. Phương pháp giải quyết bằng hai trong ba ngôn ngữ mà tôi đã triển khai điều này ( Java và C #) yêu cầu bước thời gian chạy để chọn trong số các tình trạng quá tải tiềm năng, làm cho mã có số lượng quá tải lớn khó đọc hơn một chút. Tái cấu trúc mã cũng trở nên dễ dàng hơn, bởi vì việc chọn phương thức bạn muốn sửa đổi trở thành một nhiệm vụ không quan trọng.
dasblinkenlight

@SnOrfus Vui lòng xem câu trả lời của tôi cho T.Fabre ở trên.
dasblinkenlight

@dasblinkenlight C # hiện cung cấp động để cho phép bộ thực thi quyết định nên sử dụng phương thức quá tải nào (không phải vào thời gian biên dịch). Có còn lý do tại sao không sử dụng quá tải?
Tintenfiisch
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.