Sau khi bạn hoàn thành hướng dẫn về Rails và khởi động ứng dụng của riêng mình, mọi thứ trở nên khó hiểu. Giống như logic chung không CRUD của bạn sẽ đi đến đâu? Làm thế nào để thu hút người theo dõi từ Twitter phù hợp với MVC? Hỏi hai người và bạn nhận được bốn câu trả lời. Hoặc chủ đề của bạn biến thành một đám người thông minh lăng mạ nhau hàng giờ, trong khi bạn đập đầu vào bàn. Dù bằng cách nào, bạn vẫn phải đau đầu.
Bạn không thể tạo ứng dụng mà bạn hằng mơ ước mà không có một số logic chung, không phải Rails. Vậy bạn đặt mã của mình ở đâu mà vẫn giữ mọi thứ đơn giản?
Nơi bắt đầu dễ dàng
Khi tôi có logic cảm thấy liên quan đến mô hình ActiveRecord hiện có, tôi sẽ bắt đầu bằng cách đưa nó vào mô hình đó. Ví dụ:nếu tôi có Game
và tôi muốn nhập một loạt trò chơi từ tệp CSV, tôi sẽ đặt phương pháp đó ngay vào Game
lớp:
class Game < ActiveRecord::Base
def self.parse_from_csv(csv_string)
games = []
CSV.parse(csv_string, quote_char: "'") do |row|
games << Game.from_csv_row(row) if (row[0] == 'G')
end
games
end
def self.from_csv_row(row)
Game.new({
dgs_game_id: row[1],
opponent_name: row[2],
created_at: row[4],
updated_at: row[4],
})
end
end
Bạn có tất cả thông tin mà phương pháp của bạn cần ngay trong tầm tay. Nó có thể dễ dàng kiểm tra. Và đó có thể là nơi mà một cộng tác viên mới sẽ tìm kiếm logic đó trước tiên.
Nhưng nếu bạn tiếp tục thêm và thay đổi mô hình đó, nó sẽ trở nên lớn và phức tạp. Các phần khác nhau của mô hình sẽ tương tác theo những cách kỳ lạ. Bạn càng thay đổi nhiều, bạn càng khó thay đổi.
Trong trường hợp đó, bạn có thể muốn cấu trúc lại mã đó thành mô hình không phải ActiveRecord.
Các mô hình Non-ActiveRecord
Chỉ vì nó nằm trong ứng dụng Rails, không có nghĩa là nó phải kế thừa từ Active / Action-bất cứ điều gì.
Bạn có thể viết mã Ruby của riêng mình, trong các đối tượng Ruby thuần túy và sử dụng chúng trong ứng dụng Rails của bạn. Những đối tượng này vẫn có thể được gọi là mô hình vì chúng đang mô hình hóa một phần vấn đề của bạn. Họ chỉ không có cơ sở dữ liệu ActiveRecord lưu trữ dữ liệu của họ.
Lần tiếp theo tôi làm việc trên trình phân tích cú pháp CSV trò chơi đó, Game
lớp học đã trở nên quá lớn. Vì vậy, tôi đã chuyển logic trình phân tích cú pháp thành GameCSVParser
của riêng nó lớp học.
Toàn bộ cam kết nằm ở đây, nhưng đây là lớp không phải ActiveRecord trông như thế nào:
class GameCSVParser
def initialize(csv_string)
@rows = CSV.parse(csv_string, quote_char: "'")
end
def games
game_rows.map { |row| Game.new(game_attributes(row)) }
end
private
def game_rows
@rows.select { |row| is_game_row?(row) }
end
def game_attributes(row)
{
dgs_game_id: row[1],
opponent_name: row[2],
created_at: row[4],
updated_at: row[4],
}
end
def is_game_row?(row)
row[0] == 'G'
end
end
Tôi sẽ đi ngay để tạo một đối tượng Ruby đơn giản mới nếu logic mà tôi đang thêm không liên quan đến bất kỳ mô hình ActiveRecord cụ thể nào. Hoặc nếu mã có vẻ như là một phần của thứ chưa tồn tại trong ứng dụng. Nếu không, chúng chủ yếu bật lên thông qua tái cấu trúc.
Với các đối tượng Ruby đơn giản, bạn có thể viết bất cứ thứ gì. Nhưng biết bạn có thể viết bất cứ điều gì không giúp bạn định hướng. Bạn cần những phương pháp nào? Tất cả các đối tượng mới của bạn sẽ tương tác như thế nào?
Nhiều ứng dụng Rails sử dụng cùng một danh mục của các đối tượng Ruby thuần túy. Các danh mục này là các mẫu bạn có thể làm theo để viết mã mà các nhà phát triển khác sẽ nhận ra. Bạn có thể đã nghe nói về một vài trong số họ rồi.
Đối tượng dịch vụ, người trình bày và công việc
Không có gì đặc biệt về đối tượng phục vụ, người thuyết trình và công việc. Chúng chỉ là các đối tượng Ruby đơn giản hoạt động theo một cách cụ thể dễ nhận biết.
Ví dụ: Yêu cầu lại công việc là một lớp Ruby thuần túy có perform
và một @queue
:
class FetchGamesForPlayer
@queue = :default
def self.perform(player_id)
player = Player.scoped_by_id(player_id).ready_for_fetching.first
player && player.fetch_new_games!
end
end
perform
được gọi khi công việc được chạy.
Một người thuyết trình là một đối tượng Ruby thuần túy với mã chỉ có ý nghĩa bên trong một khung nhìn:
class UserPresenter
def show_related_users?
@user.related.count > 3
end
end
Nó cũng có thể bao gồm trình trợ giúp chế độ xem của Rails hoặc lấy một vài đối tượng khác nhau và coi chúng như một đối tượng thống nhất để tạo sự thuận tiện cho chế độ xem.
Một đối tượng dịch vụ là một đối tượng Ruby đơn giản đại diện cho một quá trình bạn muốn thực hiện. Ví dụ:viết bình luận trên một bài đăng có thể:
- Để lại nhận xét.
- Gửi thư thông báo cho tác giả của bài đăng.
Một đối tượng dịch vụ có thể làm cả hai và giữ logic đó ngoài bộ điều khiển của bạn.
Có rất nhiều đối tượng dịch vụ ở đây. Có đầy đủ các ví dụ.
Đối với các quy trình đơn giản, tôi không bận tâm đến các đối tượng dịch vụ. Nhưng nếu bộ điều khiển bắt đầu quá nặng, chúng là nơi tốt để đặt thêm logic đó.
Bạn có thể sử dụng các mẫu này để tổ chức logic kinh doanh của riêng mình. Chúng chỉ là các đối tượng Ruby đơn thuần, nhưng chúng là các đối tượng Ruby có chung một hương vị nhất định, có tên và bạn có thể nói chuyện với các nhà phát triển khác.
Bạn bắt đầu từ đâu?
Có rất nhiều nơi khác nhau mà logic nghiệp vụ không phải Rails của bạn có thể đi. Có thể khó để lựa chọn. Vì vậy, đây là những gì tôi làm:
- Nếu logic chủ yếu liên quan đến một lớp hiện có, ngay cả khi đó là mô hình ActiveRecord, tôi sẽ đưa nó vào lớp đó.
- Nếu nó không phù hợp với một lớp hiện có, tôi sẽ tạo một lớp Ruby đơn giản mới để giữ logic.
- Nếu có vẻ như logic phải là một phần của thứ gì đó chưa tồn tại, tôi sẽ tạo một lớp Ruby đơn giản mới cho nó.
- Nếu tôi quay lại mã sau và mô hình trở nên quá phức tạp hoặc mã không còn ý nghĩa trong mô hình đó nữa, tôi sẽ cấu trúc lại nó thành lớp Ruby đơn giản của riêng nó.
- Nếu mã chỉ có ý nghĩa trong một chế độ xem, tôi sẽ thêm mã đó vào một người trợ giúp hoặc tạo một người thuyết trình.
- Nếu mã không cần chạy trong một yêu cầu HTTP hoặc phải chạy trong nền, thì mã sẽ hoạt động.
- Nếu tôi đang kết hợp nhiều mô hình hoặc giai đoạn khác nhau của một quy trình và điều đó khiến bộ điều khiển quá khó hiểu, tôi sẽ đưa nó vào một đối tượng dịch vụ.
Còn bạn thì sao? Mã của bạn đi đâu? Và bạn có mẫu nào ngoài những mẫu này mà bạn thấy hữu ích không? Để lại nhận xét và cho tôi biết.
Và nếu bạn chưa có quy trình, hãy thử quy trình của tôi. Xem nó phù hợp với bạn như thế nào. Không có cách hoàn hảo để viết mã, nhưng khi bạn gặp khó khăn, quy trình như thế này sẽ giúp bạn bắt đầu.