Design pattern: SOLID Design Principles

Chào các bạn, Hôm nay mình quay lại với series các bài viết về Design Pattern trong C#. Vậy Design pattern là cái gì? Tại sao chúng ta lại áp dụng Design Pattern?

Design pattern là cái gì

Chắc hẳn các bạn sinh viên IT khi đi học đều biết đến khái niệm lập trình hướng đối tượng (OOP) và các tính chất của lập trình hướng đối tượng. Biết được những khái niệm và các vận dụng OPP thì có thể code ngon lành cành đào, app chạy vù vù. Nhưng trong môi trường thực tế một phần mềm tốt cần có một số yêu cầu khác như đặt tên đúng cách, format code gọn gàng, dễ đọc. Nhưng quan trọng nhất là khả năng bảo trì được ( maintainable ).  Bất kì phần code nào mà không có khả năng bảo trì để thích nghi với những yêu cầu thay đổi chóng mặt từ khách hàng thì bạn sẽ bị sếp chửi tơi bời vì trễ deadline và tới một giai đoạn nào đó chỉ có đập đi làm lại.  Từ đó ra đời khái niệm Design Pattern.

Design Pattern là giải pháp tối ưu hóa, tái sử dụng và mở rộng cho các vấn đề lập trình. Nó là khuôn mẫu được đúc kết từ quá trình phát triển phần mềm để giải quyết các vấn đề nan giải trong lập trình và được nhiều người chứng minh.

Tại sao phải sử dụng Design Pattern?

  • Giúp bạn tái sử dụng code và mở rộng một cách dễ dàng. Nó là khuôn mẫu đã được kiểm chứng. Là kim chỉ nam giúp bạn giải quyết các vấn đề trong lập trình.
  • Tăng tốc độ phát triển phần mềm vì khi áp dụng design pattern có thể tránh các lỗi lớn, dễ dàng nâng cấp và bảo trì.
  • Giúp các thành viên team có thể hiểu code của nhau một cách nhanh chóng.

Để follow theo series bài viết về design pattern thì bạn phải có kiến thức về OOP. Cấu trúc series bài viết bao gồm: Tìm hiểu về nguyên tắc SOLID sau đó đi vào các loại Design Pattern. Có 3 nhóm chính như sau: Creational Pattern, Structural Pattern, Behavioral Pattern. Cấu trúc series bài viết được tóm tắt theo hình bên dưới.

design pattern structure

SOLID Design Principles

Trước khi đi vào các loại Design Pattern thì chúng ta nên tìm hiểu về nguyên tắc SOLID trong lập trình. Đã có khá nhiều bài viết nói về nguyên tắc này nên mình không đi sâu vào khái niệm nó là gì mà thay vào đó mình sẽ làm ví dụ nhiều hơn để hiểu về nó. SOLID là viết tắt của 5 nguyên tắc sau:

Single Responsibility Principle (SRP)

solid - single responsibility principle

Nguyên tắc nói về khả năng đơn nhiệm, nói một các dễ hiểu là mỗi class nên chỉ chịu trách nhiệm về một nhiệm vụ cụ thể nào đó mà thôi. Để hiểu rõ hơn thì chúng ta sẽ đi vào ví dụ:

ví dụ 1:

Ở ví dụ 1. Class Journal có thuộc tính là các entry, nhiệm vụ của nó là lưu trữ, thêm và xóa các entry. Ngoài ra còn có các phương thức lưu lại Journal, load Journal từ file hoặc uri.  Việc class có 2 trách nhiệm sẽ phát sinh các vấn đề như sau: Giả sử có 2 app sử dụng class Journal. App 1 làm nhiệm vụ lưu trữ các entry trong Journal. App 2 làm nhiệm vụ save và load Journal. Nếu một ngày có sự thay đổi trong cách lưu trữ Entry hoặc Journal thì class Journal đã bị thay đổi. Chúng ta bắt buộc phải build và test lại cả 2 ứng dụng. Class Journal này đã vi phạm nguyên tắc SRP trong SOLID.  Để refactor đoạn code này, chúng ta sẽ chia ra 2 class như sau.

Có vẻ tốt hơn nhiều. Mỗi class làm đúng nhiệm vụ của nó.

Xác định trách nhiệm (responsibility)

Trong ngữ cảnh của SRP, để xác định trách nghiệm thì chúng ta dựa trên lý do thay đổi. Nếu chúng ta nghĩ một class có nhiều hơn một động cơ để thay đổi thì có nghĩa chúng có nhiều hơn một trách nhiệm. Rất khó để chúng ta thấy được điều đó. Mình sẽ đi tiếp đến ví dụ thứ 2:

Ở ví dụ thứ 2 chúng ta có 1 interface IModel. Trong IModel có 2 nhiệm vụ: nhiệm vụ đầu tiên là quản lý kết nối Dial()Hangup(), nhiệm vụ thứ 2 là gửi data Send()Recv(). Vì thế chúng ta nên tách chúng ra.

sold srp

Nhưng trong ví dụ này, Ứng dụng không thay đổi vì lý do nó có 2 trách nhiệm.  Việc quyết định tách ra là không cần thiết, sẽ làm ứng dụng thêm phức tạp thêm. Cũng có nhiều ý kiến trái chiều về SRP. Đây là nguyên tắc dễ  hiểu nhất và cũng là nguyên tắc khó áp dụng nhất.  Sau đây là một số ví dụ cần phải quyết định đến việc tách nó ra:

  • Persistence
  • Validation
  • Notification
  • Error Handling
  • Logging
  • Class Selection / Instantiation
  • Formatting
  • Parsing
  • Mapping

Open/Closed Principle

solid open closed principle

Nguyên tắc này đơn giản chỉ là “những class nên mở để có thể mở rộng và đóng cho việc thay đổi“. Để hiểu rõ hơn thì mình sẽ đi thẳng vào ví dụ cho các bạn hình dung dễ hơn.

Mình có một kịch bản như thế này: Ứng dụng đang quản một danh sách các sản phẩm ( Product). Mỗi sản phẩm có các thuộc tính như Màu sắc ( Color ) và kích thức ( Size ) và giá tiền ( Price ).  Để khách hàng dễ dàng tiếp cận sản phẩm thì ứng dụng yêu cầu một tính năng lọc sản phẩm theo màu sắc. Mình sẽ hiện thực lại đoạn code theo yêu cầu trên như sau:

Đoạn code trên filter ngon lành theo Color. Và rồi một ngày đẹp trời sếp có thêm yêu cầu filter theo Size.  Theo cách mọi người hay làm là mở đoạn code trong ProductFilter thêm một phương thức FilterBySize(). Đoạn code được add vào class ProductFilter như sau:

Sau khi thêm và chạy, test lại (đừng quên test lại FilterByColor()), ProductFilter vẫn hoạt động hoàn hảo. Rồi một ngày đẹp trời khác, sếp bảo: “Hey boy, Tôi muốn thêm filter theo giá tiền, cái này giống cái cũ, làm nhanh nhé”. rồi một ngày đẹp trời nữa “Thêm cho tôi filter theo Color và Size luôn nhé” . Mỗi ngày đẹp trời như thế là bạn phải mở ProductFilter ra thêm vào và phải test lại để đảm bảo tất cả các phương thức vẫn hoạt động ok. Nhiều ngày đẹp trời như thế thì sẽ trở thành ác mộng cho bạn. Có một pattern mà bạn có thể áp dụng trong tình huống này đó là Specification pattern. Phải refactor ngay đi trước khi mọi thứ dần tồi tệ hơn. =)).

Nếu muốn thêm filter theo giá tiền trong khoảng min đến max thì chỉ cần viết thêm 1 class kế thừa interface ISpecification như sau:

Cách dùng

Chúng ta có thể viết lọc theo nhiều điều kiện AND hoặc OR nếu muốn. vẫn đảm bảo không Modify class cũ mà vẫn mở rộng được ứng dụng. Đó là nguyên tắc Open Closed Principle.

Lời kết

Ở bài viết này chúng ta đã tìm hiểu 2 nguyên tắc trong số 5 nguyên tắc của SOLID. Những nguyên tắc còn lại sẽ được khám ở những bài viết tiếp theo. Chúc các bạn áp dụng thành công tinh thần của 2 nguyên tắc này trong dự án của bạn.

 

Xem thêm bài viết tại: http://levinh.net/

Tài liệu tham khảo:

https://github.com/levinhtxbt/design-patterns/tree/master/DesignPatterns/SOLID

https://en.wikipedia.org/wiki/SOLID

https://www.dofactory.com/net/design-patterns

https://deviq.com/single-responsibility-principle/

2 Comments


  1. Mình không thấy class OrSpericification ??

    Reply

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.