Tản mạn về tính bất biến (immutability) trong lập trình

Chắc hẳn với các bạn quan tâm đến lập trình nói chung và lập trình hàm nói riêng đều đã từng 1 lần nghe về tính bất biến. Vậy tính bất biến là gì, nó có ảnh hưởng gì đến việc lập trình và nó ảnh hưởng như thế nào đến kiến trúc và khả năng mở rộng, gây lỗi của phần mềm. Bài viết sau sẽ trình bày sơ lược về tính bất biến, ưu và nhược điểm đồng thời giới thiệu một số kĩ thuât, thư viện mà mọi người có thể áp dụng ngay trong những project hiện tại.

1, Tính bất biến, định nghĩa và quy tắc 

Tính bất biến trên các tài liệu được định nghĩa như sau

Trong lập trình hướng đối tượng và hàm, đối tượng bất biến (tiếng Anh: immutable object hay unchangeable object) là một đối tượng mà trạng thái của nó không thể bị thay đổi sau khi được tạo ra.

Điều này có nghĩa là gì. Đơn giản với 1 object, giá trị khi khởi tạo của của nó sẽ được giữ nguyên trong suốt vòng đời ứng dụng. Điều đó tương ứng với việc bạn sẽ phải tạm biệt với các toàn tử, gán, cập nhật, thay đổi property, thay đổi dữ liệu hay bất cứ điều gì làm thay đổi object.

Tạm biệt các toán tử quen thuộc

Đến đây có người sẽ hỏi “Ủa thể rồi lúc cần phải thay đổi thì ta làm gì ? Cơ bản thì lập trình là thay đổi các trạng thái để ra kết quả mà !!!

Well câu trả lời khá đơn giản, muốn có trạng thái mới thì đưa ra biến mới chứ đừng thay đổi biến cũ 

Vậy vì sao lại phải làm như vậy

Chắc hẳn các anh em đã có kinh nghiệm lập trình đã không ít lần phải debug những con lỗi, những dòng code khi mà các bạn phải quan sát sự thay đổi của 1 biến qua từng dòng một. Một điều kiện thỏa mãn với một biến ở dòng 30 thì đến dòng thứ 50 lại không thỏa mãn …  sự thay đổi của các biến qua thời gian về cơ bản làm tăng độ phức tạp của phần mềm và độ khó khi gỡ lỗi đồng thời khiến các luồng hoạt động trở nên khó lường hơn. Vậy để giảm độ phức tạp của nó, một giải pháp tốt là chúng ta không nên thay đổi nó từ đầu. Đó là lý do người ưu tiên việc thỏa mãn tính bất biến khi có thể.

Quá nhức đầu khi đọc các biến liên tục thay đổi

2, Các kĩ thuật để đảm bảo tính bất biến 

2.1, Copy object 

Một trong những cách nhiều người nghĩ đến đầu tiên khi nói về tính bất biến là copy object ra rồi thay đổi. Một cách đơn giản nhưng hiệu quả. Ưu điểm của nó là đơn giản, không làm thay đổi giá trị của biến trước đó nhưng vẫn được sử dụng. Tuy nhiên cách này có một nhược điểm chết người đó là việc tiêu tốn về tài nguyên bao gồm bộ nhớ và CPU. (Ở đây chúng ta nói về các ngôn ngữ lập trình không support sẵn tính bất biến mà ta phải dùng các thư viện, kỹ thuật để hỗ trợ)

Về cơ bản việc copy các object là việc check qua tất cả các thành phần của object, mảng cho đến level khi biến đó là các kiểu dữ liệu không tham chiếu số, chuỗi … sau đó dựng lại 1 object tương tự. Việc này tiêu tốn CPU và việc tạo ra 1 object mới cũng tiêu tốn bộ nhớ tương tự object cũ. Việc này gây tiêu tốn tài nguyên với các object lớn hoặc trên môi trường server khi có nhiều request,

Về mặt khác việc copy ra rồi thay đổi, bản thân lập trình viên vẫn sử dụng các toán tử làm thay đổi giá trị của 1 biến cho trước (ở đây là thay đổi 1 object/mảng đã được clone từ trước) cơ bản việc này đã vi phạm tính bất biến. Con người là một mắt xích khó lường khi phát triển phần mềm, việc này có thể dẫn đến việc các bạn sử dụng nhầm khi quen tay ở những trường hợp khác nếu không hiểu mục tiêu của việc copy lại.

Hơn nữa việc clone object sẽ thay đổi tham chiếu của các thành phần con của object/array. Điều này có thể gây ra ảnh hưởng không nhỏ đến performance trong một số trường hợp khi mà không có sự thay đổi về giá trị nhưng lại dẫn đến việc re-render do sự thay đổi về vùng nhớ.

2.2, Tạo ra biến mới mỗi khi có sự thay đổi 

Có một cách khác để đảm bảo tính bất biến là mỗi khi cần có sự thay đổi, ta tạo ra 1 biến mới sử dụng spread operator hoặc Object Assign. Cách này khác việc clone object ở chỗ, phần bộ nhớ mới được tạo ra sẽ chỉ là phần được thay đổi. Còn bản thân các thuộc tính còn lại đều được map vào bộ nhớ của object cũ.

Sử dụng spread operator and object assign để đảm bảo tính bất biến

Với cách này ta vừa đảm bảo được việc không thay đổi giá trị của bất kì biến nào sau khi được khởi tạo, vừa tạo ra được biến mới với các giá trị thay đổi để sử dụng. Tuy nhiên khi ta muốn thay đổi giá trị ở các tầng object ở sâu bên trong. Việc đọc logic sẽ vô cùng rối 

Sử dụng spread operator với object nhiều tầng

Tuy nhiên sẽ có các thư viện hỗ trợ việc này một cách dễ dàng hơn

3, Các thư viện trong javascript sử dụng cho mục đích Immutability 

3.1 Lodash

Một thư viện nổi tiếng với việc này có lẽ là lodash

https://lodash.com/

Việc cài đặt khá đơn giản, sử dụng NPM hoặc Yarn để thêm package là chúng ta có thể bắt đầu clone object và thay đổi nó

3.1 Immutability Helpers

Đây là một thư viện đơn giản được khuyên dùng bởi react 

https://github.com/kolodny/immutability-helper

Các cú pháp của thư viện này sẽ hỗ trợ việc update trong nhiều tầng của object đồng thời hạn chế các nhược điểm khi sử dụng spread operator 

3.2 Immutable-js

Bạn có một project lớn, muốn cả team áp dụng việc bất biến cho dữ liệu. Hãy sử dụng Immutable-js

https://immutable-js.com/

Đây là một thư viện lớn, hỗ trợ rất nhiều cấu trúc dữ liệu dưới dạng Immutable Object. Việc sử dụng thư viện này sẽ đảm bảo quy tắc bất biến được áp dụng trong toàn bộ hệ thống.

Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *