- vừa được xem lúc

Ứng Dụng Nguyên Tắc SOLID Trong Ruby on Rails

0 0 1

Người đăng: Hữu Ngọc Tiên Sinh

Theo Viblo Asia

Trong phát triển phần mềm, đặc biệt là lập trình hướng đối tượng (OOP), việc viết mã dễ hiểu, dễ bảo trì và dễ mở rộng là điều sống còn. Đây là lý do tại sao nguyên tắc SOLID ra đời.

SOLID là một tập hợp các nguyên tắc thiết kế phần mềm giúp bạn xây dựng các hệ thống dễ quản lý, mở rộng và kiểm thử. Bộ nguyên tắc này được giới thiệu bởi Robert C. Martin (Uncle Bob), và là kim chỉ nam cho lập trình viên chuyên nghiệp.

Trong bài viết này, mình sẽ:

  • Giải thích từng nguyên tắc trong SOLID
  • Cung cấp ví dụ thực tế áp dụng trong Ruby on Rails

SOLID là gì?

SOLID là một tập hợp 5 nguyên tắc quan trọng trong lập trình hướng đối tượng giúp:

  • Code dễ hiểu, dễ mở rộng, dễ kiểm thử
  • Giảm rủi ro bug khi thêm tính năng mới
  • Tăng tuổi thọ phần mềm

SOLID là từ viết tắt của 5 nguyên tắc:

  • S - Single Responsibility Principle (Nguyên tắc Trách nhiệm Duy nhất)
  • O - Open/Closed Principle (Nguyên tắc Mở/Rộng nhưng Đóng/Sửa đổi)
  • L - Liskov Substitution Principle (Nguyên tắc Thay thế Liskov)
  • I - Interface Segregation Principle (Nguyên tắc Tách giao diện)
  • D - Dependency Inversion Principle (Nguyên tắc Đảo ngược Sự phụ thuộc)

1. Single Responsibility Principle (SRP)

Mỗi class chỉ nên có một lý do để thay đổi.

Hãy tưởng tượng bạn có một đầu bếp làm cả: nấu ăn, rửa chén, đi chợ. Nếu có thay đổi gì ở việc đi chợ → bạn phải sửa cả đầu bếp!

Giải pháp: Tách riêng từng vai trò ra.

Anti-pattern:

# app/models/order.rb
class Order < ApplicationRecord def calculate_total # Tính tổng tiền end def send_invoice_email # Gửi email hoá đơn end
end

Ở đây, class Order vừa xử lý nghiệp vụ tính tổng tiền, vừa gửi email => vi phạm SRP.

Refactor theo SRP:

# app/services/order_calculator.rb
class OrderCalculator def initialize(order) @order = order end def total # Tính tổng tiền end
end # app/mailers/order_mailer.rb
class OrderMailer < ApplicationMailer def invoice_email(order) # Gửi email hóa đơn end
end

→ Mỗi class làm đúng một việc duy nhất, dễ test, dễ thay đổi.

2. Open/Closed Principle (OCP)

Class nên có thể mở rộng được nhưng không phải với việc sửa đổi code cũ.

Khi bạn muốn thêm tính năng mới, bạn nên thêm code, không sửa code cũ. Điều này giúp tránh làm hỏng các logic đang hoạt động tốt.

Anti-pattern:

# app/models/payment.rb
class Payment def process(type) if type == "paypal" process_paypal elsif type == "stripe" process_stripe end end
end

→ Cứ mỗi lần thêm phương thức thanh toán mới, ta phải sửa process., dễ gây lỗi.

Refactor theo OCP (dùng Strategy Pattern):

# app/strategies/payment_strategy.rb
class PaymentStrategy def process raise NotImplementedError end
end class PayPalPayment < PaymentStrategy def process # Xử lý PayPal end
end class StripePayment < PaymentStrategy def process # Xử lý Stripe end
end # app/services/payment_processor.rb
class PaymentProcessor def initialize(strategy) @strategy = strategy end def process @strategy.process end
end

→ Giờ chỉ cần tạo class mới, không cần đụng code cũ.

3. Liskov Substitution Principle (LSP)

Subclasses phải có thể thay thế superclass mà không làm sai hành vi chương trình.

Nếu bạn thay thế object của class cha bằng object của class con mà hệ thống vẫn hoạt động bình thường → bạn tuân thủ LSP.

Anti-pattern:

class Bird def fly # bay end
end class Penguin < Bird def fly raise "Penguins can't fly!" end
end

→ Dùng Penguin thay cho Bird làm chương trình lỗi ngay.

Refactor đúng LSP:

class Bird; end class FlyingBird < Bird def fly # bay end
end class Penguin < Bird # Không có fly
end

→ Giờ Penguin không bị ép "bay" nữa.

4. Interface Segregation Principle (ISP)

Không nên bắt class phụ thuộc vào những method mà nó không dùng.

Đừng bắt một đối tượng phải "làm mọi thứ", nên tách nhỏ trách nhiệm thành các module riêng biệt.

Ruby không có interface như Java, nhưng có thể áp dụng qua các role/module nhỏ gọn.

Tách interface bằng module:

module Exportable def export_csv # xuất CSV end
end module Printable def print_pdf # xuất PDF end
end class Invoice include Exportable
end class Report include Printable
end

→ Mỗi class chỉ sử dụng đúng module nó cần.

5. Dependency Inversion Principle (DIP)

High-level module không nên phụ thuộc vào low-level module, cả hai nên phụ thuộc vào abstraction.

Thay vì hard-code việc sử dụng một class cụ thể → hãy inject nó từ bên ngoài, điều này giúp code dễ thay đổi, dễ test (mocking).

Anti-pattern:

class ReportService def generate pdf = PDFGenerator.new pdf.render end
end

→ ReportService bị dính chặt với PDFGenerator.

Refactor với Dependency Injection:

class ReportService def initialize(generator) @generator = generator end def generate @generator.render end
end class PDFGenerator def render # render PDF end
end class HTMLGenerator def render # render HTML end
end # Sử dụng:
report = ReportService.new(PDFGenerator.new)
report.generate

→ ReportService giờ có thể dùng bất kỳ định dạng nào.

Tổng kết

Nguyên tắc Ý nghĩa ngắn gọn Mục tiêu
SRP Mỗi class làm một việc Dễ bảo trì
OCP Mở rộng không sửa code cũ Tránh lỗi khi thêm tính năng
LSP Dùng class con thay cho cha không lỗi Tăng tính kế thừa
ISP Chỉ dùng những gì cần Tránh rối code
DIP Phụ thuộc vào abstraction Dễ test, mở rộng linh hoạt

Nguyên tắc SOLID không chỉ là lý thuyết, mà là công cụ mạnh mẽ giúp bạn:

  • Viết code rõ ràng, dễ đọc
  • Dễ dàng mở rộng, bảo trì
  • Tăng tính module hóa và kiểm thử

Trong Rails, SOLID thường được thể hiện qua service objects, form objects, decorators, và concerns, giúp bạn tránh nhồi nhét logic vào model/controller.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Docker: Chưa biết gì đến biết dùng (Phần 3: Docker-compose)

1. Mở đầu. . .

0 0 138

- vừa được xem lúc

Tích hợp VNPAY vào Rails

Xin chào 500 ae năm mới nhé. Tiếp nối câu chuyện về Thanh toán online mà mình có chia sẽ ở 2 bài trước, mọi người chưa đọc thì có thể vào xem ở đây nhé.

1 1 104

- vừa được xem lúc

Tìm hiểu Adapter Pattern trong Rails

. Nếu là một web developer chắc hẳn chúng ta đã không ít lần đọc qua về các Design patterns hay cách áp dụng chúng để làm cho code trở nên hướng đối tượng hơn, dễ đọc, dễ hiểu, dễ maintain, dễ mở rộng, … Các design patterns được áp dụng khá nhiều trong các Rails projects như Service Object, Decorato

0 0 54

- vừa được xem lúc

Sử dụng Searchkick để tìm kiếm thông minh trên Rails và Elasticsearch

Bạn đã bao giờ tự hỏi, ứng dụng web của mình có thể mở rộng quy mô bằng cách học được các từ khóa mà người dùng tìm kiếm? Có giải pháp nào cung cấp công cụ tìm kiếm tự động nhanh chóng với chỉ 1 từ khóa bất kì? Thật may khi có Searchkick và Elasticsearch là các công cụ hỗ trợ công việc tìm kiếm trở

0 0 112

- vừa được xem lúc

Những sai lầm bạn có thể mắc phải khi code Rails

. Chào các bạn, chào các bạn. Đừng vội đóng tab nha.

0 0 55

- vừa được xem lúc

Một số lưu ý cải thiện performance khi làm việc với Rails

Khi làm việc với ruby on rails chắc hẳn chúng ta sẽ làm việc với active record rất nhiều. Tuy nhiên có nhiều điều có thể ta vẫn chưa thực sự hiểu, ví dụ như ActiveRecord execute SQL query như thế nào? Và cũng còn khá nhiều lập trình viên khác cũng không để ý tới điều này.

0 0 118