Tính ổn định của khách truy cập so với tính linh hoạt của thể hiện


8

Tôi đang làm việc trên một ứng dụng GUI tạo tệp cấu hình. Tôi có một hệ thống phân cấp lớp cho mô hình cấu hình và tôi sử dụng một cây đối tượng của hệ thống phân cấp đó trong một số bối cảnh khác nhau. Hiện tại, tôi sử dụng mẫu Khách truy cập để tránh gây ô nhiễm các lớp mô hình của mình bằng mã cụ thể theo ngữ cảnh.

interface IConfigurationElement {
    void acceptVisitor(IConfigurationElementVisitor visitor);
}

Trong phiên bản trước, tôi đã sử dụng chuỗi instanceofđiều kiện thay vì Khách truy cập. So sánh hai cách tiếp cận tôi thấy các giao dịch sau.

Khách thăm quan

  • Nó là dễ dàng hơn và an toàn hơn để thêm mới IConfigurationElement. Chỉ cần thêm một khai báo mới IConfigurationElementVisitorvà trình biên dịch sẽ tạo ra lỗi cho tất cả các cài đặt của khách truy cập. Với instanceofcác chuỗi bạn phải nhớ tất cả các vị trí bạn phải mở rộng với thành phần cấu hình mới. Về cơ bản instanceofvi phạm nguyên tắc DRY vì nó sao chép logic ở một số nơi.
  • Mẫu khách truy cập hiệu quả hơn một chuỗi các instanceofđiều kiện

ví dụ

  • Ưu điểm lớn của instanceofnó là tính linh hoạt của nó. Ví dụ, instanceof cho phép tôi xác định các giải pháp đặc biệt cho các tập hợp con khác nhau của các IConfigurationElementtriển khai cần được xử lý tương tự trong một số trường hợp. Ngược lại, Khách truy cập buộc tôi phải thực hiện một phương thức cho mỗi lớp triển khai mỗi lần.

Có một giải pháp chung cho loại vấn đề này? Tôi có thể điều chỉnh Khách truy cập bằng cách nào đó không, vì vậy tôi có thể cung cấp giải pháp chung cho một số trường hợp?


Tôi rất muốn giới thiệu với bạn điều này hướng dẫn viết blog mà so sánh phong cách truy cập khác nhau (instanceof vv) và cung cấp câu trả lời rất thú vị, nó giúp tôi, tôi hy vọng nó woks cho bạn quá: apocalisp.wordpress.com/2009/08/21/...
AndreasScheinert

Câu trả lời:


1

Bạn có thể sử dụng khách truy cập với instanceOf

giao diện:

interface Visitable {
  void accept(Object visitor);
}
interface AVisitor {
  void visitA(A a);
}
interface BVisitor {
  void visitB(B b);
}
interface CVisitor {
  void visitB(C c);
}

Lượt truy cập:

class C implements Visitable {
  public void accept(Object visitor) {
    if (visitor instanceof CVisitor) {
      ((BVisitor)vistor).visitC(this);
    }
  }
}

class B implements Visitable {
  public void accept(Object visitor) {
    if (visitor instanceof BVisitor) {
      ((BVisitor)vistor).visitB(this);
    }
  }
}

class A extends B implements Visitable {
  public void accept(Object visitor) {
    super.accept(visitor);
    if (visitor instanceof AVisitor) {
      ((AVisitor)vistor).visitA(this);
    }
  }
}

Khách:

class PrintBs implements BVisitor {
  public void visitB(B b) {
    system.out.println(b);
  }
}

class PrintAs implements AVisitor {
  public void visitA(A a) {
    system.out.println(a);
  }
}

class PrintCs implements CVisitor {
  public void visitC(C c) {
    system.out.println(c);
  }
}
class PrintAsAndCs implements CVisitor, AVisitor{
  public void visitA(A a) {
    system.out.println(a);
  }
  public void visitC(C c) {
    system.out.println(c);
  }
}

mỗi lớp chỉ biết về các giao diện liên quan của nó, vì vậy, việc thêm vistors mới hoặc truy cập yêu cầu thay đổi mọi thứ trong danh mục đó (khách truy cập / có thể truy cập) (đối với khách truy cập, nó không yêu cầu thay đổi bất cứ điều gì, vì có thể truy cập, nó yêu cầu tạo giao diện khách truy cập mới, nhưng một lần nữa, không thay đổi các đối tượng hiện có).

Theo cách này, không có chuỗi thử nghiệm thể hiện và khách truy cập cho tập hợp con thậm chí không cần biết về các loại bên ngoài tập hợp con này.

Câu hỏi là phải làm gì với tình huống A mở rộng B (và B cũng có thể truy cập được), trong trường hợp đó, bạn có thể thêm super.accept (khách truy cập) vào chấp nhận (do đó sẽ là chuỗi ngắn của instanceof-s, nhưng onlu như miễn là hệ thống phân cấp sâu, và nó không quá sâu đối với vấn đề, abd bạn không cần phải viết toàn bộ bằng tay).


Nếu tôi hiểu bạn chính xác, bạn đề xuất xóa IConfigurationElementVisitorvà chỉ kiểm tra các loại khách truy cập cụ thể trong Visitable. Trong khi đây là một giải pháp có thể tôi thấy một số nhược điểm. Đầu tiên, điều này sẽ loại bỏ tính ổn định của Khách truy cập mà tôi đã nói và thứ hai tôi sẽ đưa kiến ​​thức về khách truy cập vào Visitabletriển khai của mình , điều cần tránh là một phần lý do để sử dụng Khách truy cập ở nơi đầu tiên.
Julian Lương

Không phải trong Visitable (đó chỉ là giao diện đơn giản mà không chấp nhận (Khách truy cập đối tượng)), nhưng trong việc triển khai Visitable. Vì vậy, mỗi Class chỉ kiểm tra cho khách truy cập của nó, vì vậy nó chỉ biết về thực tế là có giao diện để truy cập nó. Tôi sẽ sửa đổi câu trả lời của tôi để làm cho điều này rõ ràng.
user470365

Ok, cách tiếp cận của bạn cho phép có khách truy cập chỉ truy cập các kiểu con cụ thể của cấu trúc của tôi. Đây là một ý tưởng tốt đẹp. Những gì tôi muốn là một chút khác nhau (tôi có lẽ nên cập nhật mô tả của tôi). Tôi muốn một thời gian biên dịch đảm bảo rằng tất cả các yếu tố trong cấu trúc của tôi đã truy cập nhưng đồng thời tôi muốn đối xử bình đẳng với một số yếu tố và một số yếu tố khác nhau. Nó giống như @AndreasScheinert đặt nó. Về cơ bản tôi muốn khớp mẫu với cảnh báo của trình biên dịch nếu khớp không đầy đủ (như khớp của Scala với các lớp trường hợp).
Julian Lương

0

Vâng, vâng, bạn có thể. Nắm bắt sự tương đồng của nhiều yếu tố cấu hình bằng cách gán vai trò cho chúng. Bạn có thể kết thúc bằng cá thể, nhưng không phải theo cách vi phạm nguyên tắc DRY, mà là cách xoay quanh việc gõ tĩnh của Java.

class ConfigurationElementA implements IConfigurationElement, ICommittable {
}

class ConfigurationElementB implements IConfigurationElement, IVerifiable {
}

class Visitor {
    void accept(IConfigurationElement element) {
        if (element instanceof ICommittable) {
            // ...
        }

        // Note: not a chain of instanceofs.

        if (element instanceof IVerifiable) {
            // ...
        }
    }
}

Nói cách khác, bạn cho phép khách truy cập chấp nhận các yếu tố cấu hình một cách khái quát và hành động theo các nhóm thực hiện thông qua các vai trò. Lưu ý rằng bạn có thể đi cụ thể như bạn cần bằng cách mô hình hóa các yếu tố cấu hình phù hợp.

Bạn có thể nhận ra ở đây mẫu RoleInterface của Martin Fowler .


1
Về cơ bản, bạn sử dụng các giao diện để xác định các tập hợp con IConfigurationElementcó thể được xử lý cụ thể dựa trên thành viên đã đặt. Thật không may, ngay khi bạn phân biệt các đối tượng với instanceoftrình biên dịch của mình sẽ không thể giúp bạn nếu bạn quên cập nhật một trong những khách truy cập của mình. Giải pháp của bạn có lợi ích là tất cả các instanceoftoán tử được nhúng trong một loại phổ biến, giúp tìm kiếm chúng bằng cách tìm kiếm loại.
Julian Lương

Đề xuất của tôi xuất phát từ hai quan sát về trường hợp sử dụng của bạn: Thứ nhất, bạn muốn tránh sự phổ biến của acceptchữ ký phương thức. Đối với điều này, tôi đã đề xuất một cách tiếp cận tất cả, một cách tiếp cận mà bạn có thể điều chỉnh cho phù hợp với nhu cầu của mình, đi ít nhiều cụ thể xung quanh IConfigurationElementviệc thực hiện của bạn . Thứ hai, bạn muốn tính linh hoạt của thể hiện, có lẽ (theo trường hợp đầu tiên) bởi vì bạn có những đặc điểm chung trong các lớp thực hiện - nếu không, không có lý do gì để tránh acceptsự tăng sinh bắt đầu. Đó là nơi tôi đề nghị RoleInterfacesử dụng instanceofkhác nhau.
Mihai Danila

(Bạn vẫn có thể tránh instanceof: Thực IConfigurationElementhiện triển khai IRoleEnabledvà yêu cầu khách truy cập truy cập từng thành phần cấu hình dưới dạng gọi vật phẩm được kích hoạt vai trò visitRoleEnabledvisitRoleEnabledtrong mỗi lớp thực hiện gọi lại cho khách truy cập cho từng vai trò được triển khai. Nhưng với điều này, chúng tôi bắt đầu thực hiện hoang dã instanceofkhi nó thực sự hoạt động tốt.)
Mihai Danila

0

Tôi có thể nghĩ về một số giải pháp tiềm năng:

  1. tạo một phương thức riêng trong Visitortriển khai và có nhiều visitphương thức trong Visitorthực hiện gọi phương thức riêng đó.

  2. nếu ở trên được lặp lại ở nhiều nơi, bạn có thể xem xét việc tạo một lớp trừu tượng thực hiện Visitorvà chuyển hướng một tập hợp con của các visittriển khai thành một protected abstractphương thức chung .

  3. tạo nhiều Visitorgiao diện:

    interface AOrB extends IConfigurationElementVisitor {
        <Result> Result accept(AOrBVisitor<Result> visitor);
    
        interface AOrBVisitor<Result> {
            Result visit(A a);
            Result visit(B b);
        }
    }
    
    interface ThisCouldBeOfTypeB extends IConfigurationElementVisitor {
        <Result> Result accept(ThisCouldBeOfTypeBVisitor<Result> visitor);
    
        interface ThisCouldBeOfTypeBVisitor<Result> {
            Result visit(B b);
            Result visit(ThisCouldBeOfTypeB visitable);
        }
    }
    
    class A implements AOrB, ThisCouldBeOfTypeB {...}
    
    class B implements AOrB, ThisCouldBeOfTypeB {...}
    
    class C implements ThisCouldBeOfTypeB {...}

Tôi nghĩ 3 là những gì bạn đang tìm kiếm. nó là đa hình tĩnh 100% và tạo cảnh báo trình biên dịch nếu một loại không được xử lý. Nhưng nhược điểm duy nhất tôi có thể nghĩ đến 3 là việc duy trì việc Visitable*triển khai có thể trở nên phức tạp nếu có nhiều Visitable*giao diện khác nhau .

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.