Một sự kiện là một thông báo mô tả sự xuất hiện từ quá khứ gần đây.
Một triển khai điển hình của một hệ thống hướng sự kiện sử dụng một bộ điều phối sự kiện và các hàm xử lý (hoặc thuê bao ). Bộ điều phối cung cấp API để xử lý các sự kiện (jQuery bind
) và một phương thức để xuất bản một sự kiện tới những người đăng ký của nó ( trigger
trong jQuery). Khi bạn đang nói về các sự kiện IO hoặc UI, thường cũng có một vòng lặp sự kiện , phát hiện các sự kiện mới như nhấp chuột và chuyển chúng đến bộ điều phối. Trong vùng đất JS, trình điều phối và vòng lặp sự kiện được trình duyệt cung cấp.
Đối với mã tương tác trực tiếp với người dùng - phản ứng với phím bấm và nhấp chuột - lập trình hướng sự kiện (hoặc một biến thể của nó, chẳng hạn như lập trình phản ứng chức năng ) là gần như không thể tránh khỏi. Bạn, lập trình viên, không biết người dùng sẽ nhấp vào khi nào hoặc ở đâu, do đó, hãy xuống khung GUI hoặc trình duyệt để phát hiện hành động của người dùng trong vòng lặp sự kiện và thông báo mã của bạn. Loại cơ sở hạ tầng này cũng được sử dụng trong các ứng dụng mạng (cf NodeJS).
Ví dụ của bạn, trong đó bạn nêu ra một sự kiện trong mã của mình thay vì gọi hàm trực tiếp có một số sự đánh đổi thú vị hơn, mà tôi sẽ thảo luận dưới đây. Sự khác biệt chính là nhà xuất bản của một sự kiện ( makeItSnow
) không chỉ định người nhận cuộc gọi; đó là kết nối ở nơi khác (trong cuộc gọi đến bind
trong ví dụ của bạn). Điều này được gọi là lửa và quên : makeItSnow
thông báo với thế giới rằng tuyết đang rơi, nhưng nó không quan tâm ai đang lắng nghe, điều gì xảy ra tiếp theo hoặc khi nó xảy ra - nó chỉ đơn giản là phát thông điệp và phủi bụi.
Vì vậy, cách tiếp cận theo hướng sự kiện sẽ tách người gửi tin nhắn từ người nhận. Một lợi thế này cho bạn là một sự kiện nhất định có thể có nhiều trình xử lý. Bạn có thể liên kết một gritRoads
chức năng với sự kiện tuyết của bạn mà không ảnh hưởng đến shovelSnow
trình xử lý hiện có . Bạn có sự linh hoạt trong cách ứng dụng của bạn được nối dây; để tắt một hành vi, bạn chỉ cần xóa bind
cuộc gọi thay vì tìm kiếm mã để tìm tất cả các trường hợp của hành vi.
Một ưu điểm khác của lập trình hướng sự kiện là nó cung cấp cho bạn một nơi nào đó để đặt các mối quan tâm xuyên suốt. Bộ điều phối sự kiện đóng vai trò của Người hòa giải và một số thư viện (như Sáng hơn ) sử dụng một đường ống dẫn để bạn có thể dễ dàng cắm các yêu cầu chung như ghi nhật ký hoặc chất lượng dịch vụ.
Tiết lộ đầy đủ: Sáng hơn được phát triển tại Huddle, nơi tôi làm việc.
Ưu điểm thứ ba của việc tách người gửi sự kiện khỏi người nhận là nó mang lại cho bạn sự linh hoạt khi bạn xử lý sự kiện. Bạn có thể xử lý từng loại sự kiện trên luồng của chính nó (nếu bộ điều phối sự kiện của bạn hỗ trợ nó) hoặc bạn có thể đưa các sự kiện được nêu lên lên một nhà môi giới tin nhắn như RabbitMQ và xử lý chúng với quy trình không đồng bộ hoặc thậm chí xử lý hàng loạt qua đêm. Người nhận sự kiện có thể ở một quy trình riêng hoặc trên một máy riêng. Bạn không phải thay đổi mã làm tăng sự kiện để làm điều này! Đây là ý tưởng lớn đằng sau kiến trúc "microservice": các dịch vụ tự trị giao tiếp bằng các sự kiện, với phần mềm trung gian nhắn tin là xương sống của ứng dụng.
Đối với một ví dụ khá khác biệt về phong cách hướng sự kiện, hãy tìm đến thiết kế hướng theo miền , trong đó các sự kiện miền được sử dụng để giúp tách biệt các tập hợp. Ví dụ: hãy xem xét một cửa hàng trực tuyến giới thiệu các sản phẩm dựa trên lịch sử mua hàng của bạn. Cần Customer
phải cập nhật lịch sử mua hàng khi ShoppingCart
được trả tiền. Tổng ShoppingCart
hợp có thể thông báo cho Customer
bằng cách đưa ra một CheckoutCompleted
sự kiện; các Customer
sẽ được cập nhật trong một giao dịch riêng biệt để đáp ứng với sự kiện này.
Nhược điểm chính của mô hình hướng sự kiện này là không xác định. Bây giờ khó hơn để tìm mã xử lý sự kiện vì bạn không thể điều hướng đến nó bằng IDE của mình; bạn phải tìm ra nơi sự kiện bị ràng buộc trong cấu hình và hy vọng rằng bạn đã tìm thấy tất cả các trình xử lý. Có nhiều thứ để giữ trong đầu của bạn bất cứ lúc nào. Các quy ước kiểu mã có thể giúp ở đây (ví dụ: đặt tất cả các cuộc gọi bind
vào một tệp). Vì lợi ích của bạn, điều quan trọng là chỉ sử dụng một bộ điều phối sự kiện và sử dụng nó một cách nhất quán.
Một nhược điểm khác là khó tái cấu trúc các sự kiện. Nếu bạn cần thay đổi định dạng của một sự kiện, bạn cũng cần thay đổi tất cả những người nhận. Điều này trở nên trầm trọng hơn khi những người đăng ký một sự kiện ở trên các máy khác nhau, bởi vì bây giờ bạn cần đồng bộ hóa các bản phát hành phần mềm!
Trong một số trường hợp nhất định, hiệu suất có thể là một mối quan tâm. Khi xử lý một tin nhắn, người điều phối phải:
- Tra cứu các trình xử lý chính xác trong một số cấu trúc dữ liệu.
- Xây dựng một đường ống xử lý tin nhắn cho mỗi xử lý. Điều này có thể liên quan đến một loạt các phân bổ bộ nhớ.
- Tự động gọi các trình xử lý (có thể sử dụng sự phản chiếu nếu ngôn ngữ yêu cầu).
Điều này chắc chắn là chậm hơn so với một cuộc gọi chức năng thông thường, chỉ liên quan đến việc đẩy một khung mới trên ngăn xếp. Tuy nhiên, tính linh hoạt mà kiến trúc hướng sự kiện mang lại cho bạn giúp việc cách ly và tối ưu hóa mã chậm dễ dàng hơn nhiều. Có khả năng gửi công việc tới bộ xử lý không đồng bộ là một chiến thắng lớn ở đây, vì nó cho phép bạn phục vụ yêu cầu ngay lập tức trong khi công việc khó khăn được xử lý ở chế độ nền. Trong mọi trường hợp, nếu bạn tương tác với DB hoặc vẽ nội dung trên màn hình thì chi phí của IO sẽ hoàn toàn chi phí cho việc xử lý tin nhắn. Đó là một trường hợp tránh tối ưu hóa sớm.
Tóm lại, các sự kiện là một cách tuyệt vời để xây dựng phần mềm kết nối lỏng lẻo, nhưng chúng không phải là không có chi phí. Nó sẽ là một sai lầm, ví dụ, để thay thế mọi cuộc gọi chức năng trong ứng dụng của bạn bằng một sự kiện. Sử dụng các sự kiện để làm cho các bộ phận kiến trúc có ý nghĩa.
$(document).bind('snow', shovelShow)
. Không cần phải bọc nó trong một chức năng ẩn danh.