mẫu trình lắng nghe sự kiện trong api - những gì nên thêm cùng một người nghe hai lần làm gì?


8

Khi thiết kế API cung cấp giao diện nghe sự kiện, có vẻ như có hai cách xử lý các cuộc gọi để thêm / xóa người nghe:

  1. Nhiều cuộc gọi đến addListener sẽ chỉ thêm một người nghe (như thêm nó vào một tập hợp); có thể được loại bỏ bằng một lệnh gọi đến removeListener.

  2. Nhiều cuộc gọi đến addListener sẽ thêm một người nghe mỗi lần (như thêm nó vào danh sách); phải được cân bằng bởi nhiều lệnh gọi đến removeListener.

Tôi đã tìm thấy một ví dụ về mỗi: (1) - Cuộc gọi DOM addEventListener trong trình duyệt chỉ thêm người nghe một lần, âm thầm bỏ qua các yêu cầu để thêm cùng một người nghe lần thứ hai và (2) - .onHành vi jQuery thêm người nghe nhiều lần .

Hầu hết các API người nghe khác dường như sử dụng (2), chẳng hạn như trình nghe sự kiện SWT và Swing. Nếu (1) được chọn, cũng có câu hỏi liệu nó có nên thất bại âm thầm hay có lỗi khi có yêu cầu thêm cùng một người nghe hai lần.

Trong các triển khai của mình, tôi có xu hướng gắn bó với (2) vì nó cung cấp giao diện loại thiết lập / phá vỡ sạch hơn và phát hiện ra các lỗi trong đó "thiết lập" vô tình được thực hiện hai lần và phù hợp với hầu hết các triển khai tôi đã thấy.

Điều này dẫn tôi đến câu hỏi của tôi - Có một kiến ​​trúc cụ thể hoặc thiết kế cơ bản nào khác cho vay tốt hơn cho việc thực hiện khác không? (tức là: tại sao mô hình khác tồn tại?)


1
Để làm rõ: xem xét addListener(foo); addListener(foo); addListener(bar);. Có phải trường hợp số 1 của bạn thêm một foovà một bar, hoặc chỉ bar(nghĩa là barghi đè lên foonhư người nghe)? Trong trường hợp # 2, sẽ foobắn hai lần, hoặc một lần?
apsillers

Việc triển khai trường hợp # 1 mà tôi quen thuộc thường đi theo tham chiếu - nếu foo== bar, thì nó sẽ ghi đè lên, nếu không, nó sẽ có một foovà một barlà người nghe. Nếu nó luôn ghi đè, nó sẽ không phải là một tập hợp, mà là một đối tượng duy nhất đại diện cho một người quan sát.
Giảm

2
(2) sẽ không tiết lộ bất kỳ lỗi nào miễn là bạn không cấm thêm cùng một người nghe hai lần - và sau đó không có sự khác biệt thực sự với (1).
Doc Brown

Sự lựa chọn nên phụ thuộc vào yêu cầu của người dùng API của bạn, vì vậy thông thường tốt nhất là hỏi một trong số họ. Khi bạn đưa ra quyết định thiết kế và một trong những người dùng sử dụng API của bạn và cho bạn biết thiết kế không phù hợp với anh ta, bạn có cơ hội thay đổi quyết định đó sau này không?
Doc Brown

@DocBrown - trong trường hợp cụ thể đó là lý do tôi đặt câu hỏi, chúng tôi không có nhiều lựa chọn để thay đổi. Tôi biết rằng không phải là một vấn đề lớn khi sử dụng tùy chọn này hay tùy chọn khác, vì vậy câu hỏi mang nhiều ý nghĩa hơn - có bất kỳ lý do nào dựa trên kiến ​​trúc / thiết kế / độ tin cậy (ví dụ: bên cạnh sở thích của người dùng) để chọn một mẫu khác ?
Giảm

Câu trả lời:


2

Nếu bạn gặp một số sự kiện mà bạn đang gặp vấn đề với việc quản lý thêm / xóa, tôi sẽ bắt đầu thêm ID.

Thêm một người nghe trả về một ID, lớp đã thêm nó theo dõi ID cho người nghe, nó đã được thêm vào và khi cần xóa chúng, nó sẽ xóa người nghe bằng / các ID duy nhất đó.

Điều này đặt người tiêu dùng vào tầm kiểm soát để họ có thể tuân thủ Nguyên tắc tối thiểu ngạc nhiên trong hành vi.

Điều này có nghĩa là thêm cùng một hai lần thêm hai lần, cung cấp một ID khác nhau cho mỗi ID và xóa bằng ID chỉ xóa một liên kết với ID đó. Bất cứ ai sử dụng API đều mong đợi hành vi này khi họ nhìn thấy các hạt cát.

Một sự bổ sung nữa khi vi phạm YAGNI sẽ là một GetIds nơi bạn trao cho người nghe và nó trả về một danh sách ID được liên kết với người nghe đó nếu nó có khả năng kiểm tra sự bình đẳng phù hợp, mặc dù điều đó phụ thuộc vào ngôn ngữ của bạn , loại bình đẳng, bình đẳng giá trị, vv? bạn phải cẩn thận ở đây vì bạn có thể trả lại ID mà người tiêu dùng không nên xóa hoặc can thiệp, do đó việc tiếp xúc này trở nên nguy hiểm và không được khuyến khích và không cần thiết, nhưng GetID là một chương trình bổ sung có thể nếu bạn cảm thấy may mắn.


Không nhất thiết phải có vấn đề khi có vấn đề với việc thêm / xóa (vì câu hỏi nên áp dụng cho bất kỳ chương trình nào, không chỉ với chương trình tôi đang làm việc), mặc dù tôi chắc chắn thấy mẫu này sẽ làm rõ cụ thể API như thế nào sử dụng tùy chọn # 2 (hoặc một biến thể nhỏ của tùy chọn này, trong đó loại bỏ bằng id thay vì theo người nghe)
Giảm

Có một thuê bao trả về một đối tượng có thể được sử dụng cho unsubsciption là, IMHO, các cách tiếp cận đúng. Trong các khung hướng đối tượng có IDisposable, Autocloseablehoặc giao diện khác như vậy, đối tượng hủy đăng ký nên triển khai giao diện đó theo kiểu an toàn luồng (luôn luôn có thể - nếu không có gì khác bằng cách đặt thuê bao trong chính đối tượng hủy đăng ký và phương thức hủy đăng ký của nó làm mất hiệu lực tham khảo và thỉnh thoảng có yêu cầu đăng ký quét danh sách đăng ký để đăng ký chết).
supercat

3

Đầu tiên, tôi sẽ chọn một cách tiếp cận trong đó thứ tự mà người nghe được thêm vào chính xác là thứ tự họ sẽ được gọi khi các sự kiện liên quan được kích hoạt. Nếu bạn quyết định đi theo (1), điều đó có nghĩa là bạn sử dụng một bộ được đặt hàng, không chỉ là một bộ.

Thứ hai, bạn nên làm rõ mục tiêu thiết kế chung: API của bạn sẽ tuân theo chiến lược "sớm gặp sự cố" hay chiến lược "tha thứ lỗi"? Điều này phụ thuộc vào môi trường sử dụng và các tình huống sử dụng API của bạn. Nói chung, (phát triển chủ yếu các ứng dụng trên máy tính) Tôi thích "sập sớm", nhưng đôi khi tốt hơn là chịu đựng một số loại lỗi để giúp việc sử dụng API trơn tru hơn. Các yêu cầu, ví dụ, trong các ứng dụng nhúng hoặc ứng dụng máy chủ có thể khác nhau. Có lẽ bạn thảo luận điều này với một trong những người dùng API tiềm năng của bạn?

Đối với chiến lược "sụp đổ sớm", hãy sử dụng (2), nhưng cấm thêm cùng một người nghe hai lần, ném ngoại lệ nếu người nghe được thêm lại. Cũng ném một ngoại lệ nếu một người cố gắng loại bỏ một người nghe không có trong danh sách.

Nếu bạn nghĩ rằng chiến lược "tha thứ lỗi" phù hợp hơn trong trường hợp của bạn, bạn có thể

  • bỏ qua việc thêm hai lần của cùng một người nghe vào danh sách - đó là tùy chọn (1) - hoặc

  • nối nó vào danh sách như trong (2), vì vậy nó sẽ được gọi hai lần khi các sự kiện được kích hoạt

  • hoặc bạn chắp thêm nó, nhưng đừng gọi cùng một người nghe hai lần trong trường hợp kích hoạt sự kiện

Lưu ý rằng việc loại bỏ người nghe phải tương ứng với điều đó - nếu bạn bỏ qua việc thêm hai lần, bạn cũng nên bỏ qua việc loại bỏ hai lần. Nếu bạn cho phép cùng một người nghe được thêm hai lần, thì rõ ràng mục nào trong hai mục nghe sẽ bị xóa khi một cuộc gọi removeListener(foo). Lần cuối cùng trong ba viên đạn có lẽ là cách tiếp cận ít bị lỗi nhất trong số những gợi ý đó, vì vậy trong trường hợp chiến lược "tha thứ lỗi", tôi sẽ thực hiện theo cách đó.


Chiến lược chính của tôi cho đến nay là 'sử dụng một mẫu tương tự với môi trường xung quanh' - tức là: mẫu phổ biến nhất được sử dụng trong phần còn lại của mã / ngôn ngữ mà tôi đang tích hợp. Chiến lược 'sụp đổ sớm' chắc chắn sẽ gây ra nhiều vấn đề tiềm ẩn, nhưng tôi chưa bao giờ thực sự thấy điều này được sử dụng trong thực tế, vì vậy nó có thể gây ngạc nhiên cho người dùng API (mặc dù tôi càng nghĩ về nó, tôi càng xem xét nó một bất ngờ 'tốt' vì nó sẽ giúp bắt lỗi)
Giảm

@Chris: Tôi khá chắc chắn rằng bạn đã thấy "sụp đổ sớm" rất nhiều. Ví dụ, trong hầu hết các ngôn ngữ chính hiện đại, bạn sẽ có ngoại lệ khi bạn cố gắng viết ra khỏi giới hạn mảng, cố gắng chuyển đổi chuỗi thành số không thể chuyển đổi, v.v.
Doc Brown

Tôi đã đề cập đến nó một cách cụ thể trong bối cảnh của mẫu người nghe
Kreas
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.