Nếu bạn đã từng sử dụng Ruby on Rails, chắc hẳn bạn đã bắt gặp những mối quan tâm. Bất cứ khi nào bạn bắt đầu một dự án Rails mới, bạn sẽ nhận được app/controllers/concerns
và app/models/concerns
. Nhưng là gì? Và tại sao những người từ cộng đồng Rails đôi khi lại nói xấu về họ?
Tổng quan nhanh
Mối quan tâm của Rails là bất kỳ mô-đun nào mở rộng ActiveSupport::Concern
mô-đun. Bạn có thể hỏi - mối quan tâm khác với các mô-đun như thế nào? Sự khác biệt chính là mối quan tâm của Rails cho phép bạn thực hiện một chút phép thuật, như vậy:
# app/models/concerns/trashable.rb
module Trashable
extend ActiveSupport::Concern
included do
scope :existing, -> { where(trashed: false) }
scope :trashed, -> { where(trashed: true) }
end
def trash
update_attribute :trashed, true
end
end
Bạn thấy từ đó bao gồm. Nó là một chút cacbohydrat của Rails rắc lên mô-đun Ruby. Cái gì ActiveSupport::Concern
cho bạn là nó cho phép bạn đặt mã mà bạn muốn đánh giá bên trong khối được bao gồm. Ví dụ:bạn muốn trích xuất logic chuyển vào thùng rác khỏi mô hình của mình. included
cho phép bạn thực hiện những gì chúng tôi đã làm và sau đó bao gồm mối quan tâm của mô hình của bạn như vậy:
class Song < ApplicationRecord
include Trashable
has_many :authors
# ...
end
Khá tiện dụng và ngây thơ ở điểm này, phải không? Mô hình bị mất một chút trọng lượng và hiện tượng va chạm có thể được sử dụng lại trên các mô hình khác, không chỉ mô hình Song của chúng tôi. Ồ, mọi thứ có thể trở nên phức tạp. Hãy đi sâu vào để tìm hiểu.
Một ví dụ cổ điển về Mixin
Trước khi đi sâu hơn vào các mối quan tâm, hãy thêm một lời giải thích khác về chúng. Khi bạn thấy include SomeModule
hoặc extend AnotherModule
, chúng được gọi là mixin. mixin là một bộ mã có thể được thêm vào các lớp khác. Và, như tất cả chúng ta đều biết từ tài liệu TheRuby, một mô-đun là một tập hợp các phương thức và hằng số. Vì vậy, những gì chúng tôi đang làm ở đây là bao gồm các mô-đun với các phương thức và hằng số vào các lớp khác nhau để chúng có thể sử dụng chúng.
Đó chính xác là những gì chúng tôi đã làm với Trashable
bận tâm. Chúng tôi đã trích xuất logicaround chung chuyển một đối tượng mô hình vào một mô-đun. Mô-đun này sau đó có thể được đưa vào những nơi khác. Vì vậy, mixin là một mẫu thiết kế không chỉ được sử dụng trong Ruby và Rails.
Để hiểu rõ hơn về điều này, chúng ta sẽ điểm qua một số ưu và nhược điểm của việc sử dụng usethem. Hy vọng rằng, bằng cách làm này, chúng tôi có thể hiểu được khi nào hoặc có nên sử dụng các mối quan tâm hay không.
Tôi có tất cả
Khi bạn quyết định trích xuất nội dung nào đó cần quan tâm, chẳng hạn như Trashable
lo lắng, bạn có quyền truy cập vào tất cả các chức năng của bất cứ nơi nào Trashable
được bao gồm. Điều này mang lại sức mạnh to lớn, nhưng như Richard Schneeman đã nói trong bài đăng trên blog của mình về chủ đề - "với sức mạnh tuyệt vời có khả năng tạo ra những đoạn mã phức tạp". ở đó trong mối quan tâm của bạn.
Nếu chúng ta xem xét Trashable
một lần nữa:
module Trashable
extend ActiveSupport::Concern
included do
scope :existing, -> { where(trashed: false) }
scope :trashed, -> { where(trashed: true) }
end
def trash
update_attribute :trashed, true
end
end
Logic của mối quan tâm dựa trên thực tế là trashed
trường tồn tại ở bất cứ nơi nào mà mối quan tâm được bao gồm. Đúng? Không có vấn đề gì, đây là những gì chúng tôi muốn. Nhưng, những gì tôi thấy xảy ra là mọi người bị cám dỗ để lôi kéo những thứ khác từ mô hình vào mối quan tâm. Để vẽ một bức tranh về cách điều này có thể xảy ra, hãy tưởng tượng rằng Song
mô hình có một phương thức khác featured_authors
:
class Song < ApplicationRecord
include Trashable
has_many :authors
def featured_authors
authors.where(featured: true)
end
# ...
end
class Album < ApplicationRecord
include Trashable
has_many :authors
def featured_authors
authors.where(featured: true)
end
# ...
end
Để minh họa rõ hơn, tôi đã thêm một Album
mô hình cũng bao gồm Trashable
Sau đó, hãy nói rằng chúng tôi muốn thông báo cho các tác giả nổi bật của bài hát và album khi họ được chuyển vào thùng rác. Mọi người sẽ bị cám dỗ đặt logic này trong mối quan tâm như vậy:
module Trashable
extend ActiveSupport::Concern
included do
scope :existing, -> { where(trashed: false) }
scope :trashed, -> { where(trashed: true) }
end
def trash
update_attribute :trashed, true
notify(featured_authors)
end
def notify(authors)
# ...
end
end
Ngay tại đây, mọi thứ đang bắt đầu phức tạp một chút. Vì chúng tôi băm logic bên ngoài mô hình Bài hát của mình, chúng tôi có thể bị cám dỗ để đưa thông báo vào Trashable
bận tâm. Trong đó, một cái gì đó "không ổn" xảy ra. featured_authors
được lấy từ Song
người mẫu. OK, giả sử xem xét yêu cầu vượt qua này và kiểm tra CI.
Sau đó, một vài tháng nữa, một yêu cầu mới được đặt ra mà nhà phát triển cần thay đổi cách chúng tôi trình bày featured_authors
cho các bài hát. Ví dụ, một yêu cầu mới muốn chỉ hiển thị các tác giả nổi bật từ Châu Âu. Thông thường, nhà phát triển sẽ tìm nơi các tác giả nổi bật được xác định và chỉnh sửa.
class Song < ApplicationRecord
include Trashable
has_many :authors
def featured_authors
authors.where(featured: true).where(region: 'Europe')
end
# ...
end
class Album < ApplicationRecord
include Trashable
has_many :authors
# ...
end
Điều này hoạt động tốt ở bất cứ nơi nào chúng tôi giới thiệu tác giả, nhưng sau khi chúng tôi triển khai sản xuất, những người từ các nơi khác trên thế giới sẽ không nhận được thông báo nữa về các bài hát của họ. Những sai lầm như thế này rất dễ mắc phải khi sử dụng các mối quan tâm. Ví dụ trên là một ví dụ đơn giản và nhân tạo, nhưng những ví dụ "còn nguyên sơ" có thể rất phức tạp.
Điều rủi ro ở đây là mối quan tâm (mixin) biết rất nhiều về mô hình mà nó được bao gồm. Nó được gọi là phụ thuộc vòng tròn . Song
và Album
phụ thuộc vào Trashable
để chuyển vào thùng rác, Trashable
phụ thuộc vào cả hai để featured_authors
Định nghĩa. Điều tương tự cũng có thể nói đối với thực tế là trashed
các trường cần tồn tại trong cả hai mô hình để có Trashable
mối quan tâm làm việc.
Đây là lý do tại sao một câu lạc bộ không quan tâm có thể chống lại, và các buổi hòa nhạc ủng hộ là dành cho. Tôi muốn nói, đầu tiên phiên bản của Trashable
là một trong những tôi muốn có trong cơ sở mã của tôi. Hãy xem cách chúng tôi có thể làm cho phiên bản thứ hai có thông báo tốt hơn.
Các bạn đến từ đâu
Nhìn lại Trashable
của chúng tôi với thông báo, chúng ta phải làm gì đó. Một điều khác xảy ra khi sử dụng mối quan tâm là chúng ta có xu hướng làm KHÔ quá mức. cái này):
module Authorable
has_many :authors
def featured_authors
authors.where(featured: true)
end
end
Sau đó, Song
của chúng tôi và Album
sẽ trông như thế này:
class Song < ApplicationRecord
include Trashable
include Authorable
# ...
end
class Album < ApplicationRecord
include Trashable
include Authorable
# ...
end
Chúng tôi đã làm khô mọi thứ, nhưng bây giờ yêu cầu đối với các tác giả nổi bật từ Châu Âu không được đáp ứng. Để mọi thứ tồi tệ hơn, bây giờ là Trashable
mối quan tâm và các mô hình phụ thuộc vào Authorable
. Cái quái gì thế? Chính xác là câu hỏi của tôi khi tôi đang giải quyết những mối quan tâm cách đây một thời gian. Thật khó để tìm ra các ưu điểm đến từ đâu.
Giải pháp của tôi cho tất cả những điều này là giữ featured_authors
càng gần mô hình càng tốt. notify
phương pháp không nên là một phần của Trashable
quan tâm ở tất cả. Mỗi mô hình nên tự xử lý vấn đề đó, đặc biệt nếu chúng có xu hướng thông báo cho các nhóm con khác nhau. Hãy xem cách làm điều đó ít đau đớn hơn:
# Concerns
module Trashable
extend ActiveSupport::Concern
included do
scope :existing, -> { where(trashed: false) }
scope :trashed, -> { where(trashed: true) }
end
def trash
update_attribute :trashed, true
end
end
module Authorable
has_many :authors
# Other useful methods that relate to authors across models.
# If there are none, ditch the concern.
end
# Models
class Song < ApplicationRecord
include Trashable
include Authorable
def featured_authors
authors.where(featured: true).where(region: 'Europe')
end
# ...
end
class Album < ApplicationRecord
include Trashable
include Authorable
def featured_authors
authors.where(featured: true)
end
# ...
end
Những mối quan tâm như thế này có thể kiểm soát được và không quá phức tạp. Tôi đã bỏ qua notify
chức năng mà tôi đã mô tả trước đó vì đó có thể là một chủ đề cho một ngày khác.
Trùm cuối cùng
Đối với Basecamp, những người tạo ra Rails, các mối quan tâm liên quan đến các mối quan tâm khác hoàn toàn tốt như DHH đã minh họa trong một tweeta cách đây:
Bằng cách nhìn vào ảnh chụp màn hình mã, bạn có thể đang há hốc miệng kinh ngạc hoặc kinh hãi. Tôi cảm thấy không có sự xen kẽ ở đây. Nếu tôi có cơ hội chỉnh sửa đoạn mã này, tôi sẽ hình dung nó như là "Cuộc chiến với trùm mối quan tâm cuối cùng". Nhưng bên cạnh đó, điều thú vị ở đây là có những bình luận nói rằng bản hòa tấu nào liên quan đến cái nào. Hãy xem:
# ...
include Subscribable # Depends on Readable
include Eventable # Depends on Recordables
# ...
Đặt những nhận xét như thế này có thể hữu ích, nhưng nó vẫn được thiết lập để thực hiện một số thứ sơ sài, đặc biệt nếu bạn là người mới sử dụng codebase. Là người mới và không nhận thức được tất cả các "gotchas" mà một mã có chắc chắn có thể khiến bạn đi xuống vòng xoáy đi xuống hiện đại.
Một cái gì đó như thế này là những gì DHH đã chia sẻ trong acomment bên trong cuộc thảo luận. DHH trả lời rằng họ không có nhiều tài liệu viết, họ hiếm khi thuê nên nhóm của họ rất quen thuộc với những tài liệu này.
Nhưng có một đội ngũ giàu kinh nghiệm hiểu rõ về cơ sở mã cũng như một lập luận để sử dụng chúng là một điều kỳ lạ và không mạnh mẽ. Tôi đoán nó là một cảm giác có sử dụng chúng hay không. Bạn cảm thấy thoải mái hơn với nhiều phần thừa kế mà mô-đun cung cấp hay bạn thích bố cục hơn? Cuộc gọi của bạn.
Kết luận
Như chúng ta đã thấy, mối quan tâm không gì khác hơn là các mô-đun cung cấp một số tổ hợp cú pháp hữu ích để trích xuất và KHÔ mã của bạn. Nếu bạn có nhiều công cụ hữu ích hơn, có thể bạn không nên liên hệ với các mối quan tâm ngay lập tức. Hành vi xử lý tệp đính kèm và logic chuyển vào thùng rác mà chúng tôi đã chỉ ra trong các ví dụ có thể là những ứng cử viên tốt để trích xuất thành các mô-đun (mối quan tâm).
Hy vọng rằng bạn có thể nhìn thấy những điều tốt và xấu có thể xảy ra khi xử lý các phần mềm điều khiển và mô-đun nói chung. Hãy nhớ rằng không có mã nào là hoàn hảo. Và cuối cùng, làm thế nào bạn có thể học được điều gì tốt và điều gì không tốt cho bạn nếu bạn không thử và có thể thất bại hoặc thành công?
Không có giải pháp nào là hoàn hảo và tôi hy vọng bạn phải hiểu những mối quan tâm của Rails khi thực hiện mọi việc trong bài đăng trên blog. Như mọi khi, hãy sử dụng phán đoán của bạn và nhận thức được những khuyết điểm của chúng.
Cho đến ngày tiếp theo, hãy cổ vũ!
Tái bút. Nếu bạn muốn đọc các bài đăng của Ruby Magic ngay khi chúng xuất hiện trên báo chí, hãy đăng ký bản tin Ruby Magic của chúng tôi và không bao giờ bỏ lỡ một bài đăng nào!