Các Anti-Pattern nên tránh để code không biến thành đống rác

Cách đây không lâu, mình có giới thiệu về Design Pattern, những mẫu thiết kế code giúp giải quyết vấn đề, giúp code dễ bảo trì, dễ mở rộng hơn.

Kì này, ta sẽ nói về anti-pattern. Khác với design pattern, anti pattern cũng là những cách thiết kế để giải quyết vấn đề, nhưng sử dụng nó lại… gây ra nhiều vấn đề hơn.

Một anti-pattern được nhiều người biết đó là hút thuốc lào thay cho thuốc lá, cách này cai được thuốc lá nhưng sẽ gây ra nhiều vấn đề vệ sinh và môi trường hơn.

Trong bài này, mình sẽ chia sẻ những anti-pattern chúng ta hay .. lỡ để nhầm vào code, hậu quả và cách giải quyết nó nhé!

Từ hồi dùng thứ này bố bỏ hẳn thuốc lá nhá
Từ hồi dùng thứ này bố bỏ hẳn thuốc lá nhá

Thật ra mà nói, số lượng anti-pattern nhiều không đếm xuế, tính từ tầm planning, thiết kế architecture hệ thống, thiết kế object/module, cho tới lúc viết code.

Ngồi kể chắc phải hết cả cuốn sách mới xong (có nguyên 1 cuốn sách về AntiPattern). Do vậy, mình chỉ giới thiệu những Anti Pattern  phổ biến nhé!

Có nguyên cả 1 cuốn sách về AntiPattern

Hard-code, Magic String và Number

Đây là 3 Anti Pattern, nhưng do chúng na ná giông giống nhau nên mình gom lại luôn:

  • Hard-code tức là… code cứng 1 số giá trị, 1 số logic cần thay đổi vào thẳng trong code (database connection, 1 số config).
  • Magic string và magic number tức là code cứng 1 con số, 1 string ảo diệu nào đó, mà không ghi rõ số/string đó từ đâu ra, nó là gì

Ví dụ như đoạn code dưới đây vừa hard code vừa dùng magic number

// Hard code DB Connection
const DB_CONNECTION = 'localhost://4090;username=hoangdeptrai;password=codedao'
// Magic number: 4 là gì, tại sao dùng 4 mà không phải số khác
DB.connect(DB_CONNECTION, 'db_codedao_blog', 4)

view raw
hard_core.js
hosted with ❤ by GitHub

Cách giải quyết: Anti Pattern này rất dễ xử lý. Chỉ cần không hardcode những giá trị config (mà đọc từ file config hoặc biết môi trường), tách các magic number ra thành biến riêng hoặc viết thêm comment là được.

// Đọc DB_CONNECTION từ biến môi trường hoặc file Config
const DB_CONNECTION = Config.Get('DB_CONNECTION') || env['DB_CONNECTION']
const DB_CONNECTION_POLL = 4 // Dùng 4 vì lý do blah blah
const DB_NAME = 'db_codedao_blog' // Tách thành biên để biết đây là tên DB
DB.connect(DB_CONNECTION, DB_NAME, DB_CONNECTION_POLL)

view raw
hard_core_fix.js
hosted with ❤ by GitHub

Code sau khi sửa dễ hiểu hơn nhiều phải không nào!

God Class (The Blob) – Class siêu to không lồ

Đây là một pattern hay gặp ở những bạn sinh viên đang làm đồ án, hoặc ở các dự án quá cũ, do dev non tay viết.

Gob Class tức là Class siêu to khổng lồ Thần Thánh, làm gì cũng được nên gọi là God. Tình trạng này xảy ra khi mấy ông dev gom quá nhiều tính năng vào 1 class.

Class này có những cái tên như Helper, Utils, Controller, Main… và rất bự (có khi tới 3-5000 dòng code). Mỗi lần chỉnh sửa, thêm bớt tính năng là 1 cực hình.

Class siêu to khổng lồ chức năng vẹo gì cũng có

Cách giải quyết

  • Tuân thủ nguyên tắc Single Responsibility trong SOLID, mỗi class chỉ nên giữ 1 trách nhiệm
  • Refactor dần lại code, tách class thành các class nhỏ hơn, gom nhóm các function/data hay dùng chung thành 1 class riêng

Code theo phong cách Copy và Paste

Các bạn đừng lầm tưởng AntiPattern này là copy code trên mạng về bỏ vào code của mình nhé!

Đây là pattern theo kiểu viết code 1 lần, những lần sau cần dùng thì copy nguyên đoạn code cũ qua, sửa sửa lại 1 chút cho chạy được.

// Code bỏ kí tự đặc biệt và viết hoa
let input = 'abd4%$#@#';
input = input.trim().replace(/[^a-zA-Z ]/g, "").toUpperCase()
// Đoạn code khác …
// Copy lần 1
const username = 'ahihi-de-em-di'
const trimmedUsername = username.trim().replace(/[^a-zA-Z ]/g, "").toUpperCase()
// …Copy tiếp logic qua đoạn khác
const id = 'jro@091ksa'
return id.trim().replace(/[^a-zA-Z ]/g, "").toUpperCase()

view raw
copy_paste.js
hosted with ❤ by GitHub

Về lâu dài, điều này sẽ làm số lượng code của dự án bị phình lên. Code lặp lại nhiều, những khi cần chỉnh sửa hoặc fix bug sẽ phải fix ở rất nhiều chỗ. Nếu quên hoặc sót sẽ để bị sót bug.

Cách giải quyết

  • Cách đơn giản nhất vẫn là tách đoạn code cần sử dụng thành hàm riêng, library riêng để dùng
  • Lưu ý: Có nhiều khi 3,4 hàm khác nhau quá thì đừng nên ráng viết thành 1 hàm rồi tái sử dụng, code sẽ còn… rối hơn tách riêng ra.
// Tách thành function riêng
// Trim and upper case
function chimUp(input) {
// Có bug chỉ cần sửa 1 chỗ
return input.trim().replace(/[^a-zA-Z ]/g, "").toUpperCase()
}
// Có thể dễ dàng dùng mà không cần copy paste
let input = chimUp('abd4%$#@#')
const username = 'ahihi-de-em-di'
const trimmedUsername = chimUp(username)
// …Copy tiếp logic qua đoạn khác
const id = 'jro@091ksa'
return chimUp(id)

view raw
copy_paste_fix.js
hosted with ❤ by GitHub

Premature Optimization

Optimize code (Tối ưu code) là việc chỉnh sửa/viết lại code nhằm giảm dung lượng, hạn chế input/output, tăng tốc độ thực thi, giảm lượng phần cứng cần sử dụng.

Đôi khi, việc optimize code quá sớm (chưa biết chạy chậm chỗ nào, otpimize đoạn nào) là hoàn toàn không cần thiết, nó còn làm code phức tạp hơn, khó đọc, khó debug hơn.

Cách giải quyết

  • Không optimize vội hay optimize quá sớm. Hãy hỏi xem code đó có cần, có đáng để optimize hay không
  • Khi code chạy chậm, hãy dùng profiler để xác định đoạn code gây chậm, sau khi optimize thì dùng profiler để đo lại trước
  • Các bạn có thể xem lại bài Luận về Optimize Code của mình.

Spaghetti Code

Spaghetti Code để chỉ code rối như canh hẹ, lộn, … rối như mấy cọng mì spaghetti.  Đây là những dòng code móc nối nhiều module/class với nhau, flow đi vòng vèo, cực kì khó đọc và khó sửa.

Nguyên nhân là do team code mà không có design cụ thể, do developer lười nên code đại. Hoặc do requirement đổi liên tục, chồng chéo nhau, nhưng các module và design không được update, nên cũng gọi nhau chồng chéo luôn!

Cách giải quyết

  • Đây là Anti Pattern khó giải quyết triệt để nhất! Vì nó không chỉ liên quan tới code, mà còn liên quan tới thiết kế của các module trong hệ thống
  • Cách dễ nhất là đập bỏ và viết lại khi đã hiểu rõ logic ban đầu, nhưng sẽ tốn nhiều thời gian, có thể thiếu requirement
  • Nên refactor code dần dần, tách thành từng phần nhỏ. Có thể design lại các module nếu cần

Tạm kết

Đấy, kể sơ sơ vài cái Anti Pattern mà bài viết đã dài quá trời rồi. Đọc xong các bạn có thấy nhột nhột, thấy quen quen không?

Đừng lo, ai rồi cũng mắc phải những anti pattern này lúc mới học, thiếu kinh nghiệm thôi! Qua thời gian, trình độ và kinh nghiệm tăng lên thì bạn sẽ không dùng nhầm những Anti Pattern như vậy nữa.

Nếu quan tâm, các bạn có thể đọc thêm về các Anti Pattern này theo những nguồn phía dưới nhé.

 

Nguồn tham khảo thêm:

One thought on “Các Anti-Pattern nên tránh để code không biến thành đống rác”

  1. Trong front-end có khái niệm Smart & Dumb component. Vậy ở trường hợp này, tập trung logic vào Smart component có biến nó thành God class, và trở thành anti-pattern k anh ?

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s