5 nguyên lý SOLID trong JavaScript | Học JavaScript

Nguyên lý SOLID trong JavaScript hay bất cứ ngôn ngữ lập trình nào khác đều liên quan chặt chẽ với các Design Pattern (Mẫu thiết kế). Điều quan trọng là chúng ta phải biết các mẫu thiết kế vì đó là một chủ đề cực kỳ “hot” trong một buổi phỏng vấn. Nếu bạn biết chúng, bạn sẽ dễ dàng hiểu các mô hình lập trình, mô hình kiến ​​trúc và các tính năng ngôn ngữ phức tạp hơn như Reactive programming, Flux architecture (Redux), Generators trong JavaScript,…

Nguyên lý SOLID là gì?

5 nguyên lý SOLID trong JavaScript5 nguyên lý SOLID trong JavaScript

Nguyên lý SOLID được đúc kết từ kinh nghiệm lập trình của kỹ sư phần mềm nổi tiếng Robert C. Martin. SOLID là sự kết hợp của các từ viết tắt:

  • S — Single responsibility principle
  • O — Open closed principle
  • L — Liskov substitution principle
  • I — Interface segregation principle
  • D — Dependency Inversion principle

5 nguyên lý SOLID trong JavaScript này sẽ giúp bạn cách viết mã tốt hơn. Mặc dù chúng đến từ lập trình hướng đối tượng. Tôi biết thật là táo bạo khi gọi JavaScript là ngôn ngữ hướng đối tượng. Nhưng chả sao cả, JavaScript cũng chỉ là công cụ để lập trình còn tư duy về thiết kế phần mềm vẫn là ở bạn.

Nào, bắt đầu thôi!

S — Single responsibility principle

Đây có lẽ là một trong những nguyên lý SOLID dễ hiểu nhất và đồng thời cũng là nguyên lý bị hiểu lầm nhiều nhất.

A module should be responsible for only one actor. As a consequence, it has only one reason to change

Mỗi lớp chỉ nên chịu trách nhiệm về một nhiệm vụ cụ thể nào đó mà thôi.

class

TodoList

{

constructor

() {

this

.items = [] } addItem(text) {

this

.items.push(text) } removeItem(index) {

this

.items = items.splice(index,

1

) } toString() {

return

this

.items.toString() } save(filename) { fs.writeFileSync(filename,

this

.toString()) } load(filename) { } }

Code language:

JavaScript

(

javascript

)

Mặc dù ngay từ cái nhìn đầu tiên, lớp TodoList này có vẻ ổn, nhưng nó lại vi phạm nguyên lý trên. Có nhiều thứ được viết trong lớp này. Tôi đã viết thêm một lớp nữa để quản lý việc thực thi với cơ sở dữ liệu.

class

TodoList

{

constructor

() {

this

.items = [] } addItem(text) {

this

.items.push(text) } removeItem(index) {

this

.items = items.splice(index,

1

) } toString() {

return

this

.items.toString() } }

class

DatabaseManager

{ saveToFile(data, filename) { fs.writeFileSync(filename, data.toString()) } loadFromFile(filename) { } }

Code language:

JavaScript

(

javascript

)

Do đó, đoạn mã trở nên dễ mở rộng hơn. Tất nhiên, nó không quá rõ ràng khi chúng ta xem xét các “mẩu” code nhỏ. Khi áp dụng cho một kiến ​​trúc phức tạp, nguyên lý này có nhiều ý nghĩa hơn.

O — Open closed principle

5 nguyên lý SOLID trong JavaScript5 nguyên lý SOLID trong JavaScript

Modules should be open for extension but closed for modification

Khi triển khai các tính năng mới, thay vì sửa đổi code đã tồn tại, chúng ta nên mở rộng/kế thừa.

class

Coder

{

constructor

(fullName, language, hobby, education, workplace, position) {

this

.fullName = fullName

this

.language = language

this

.hobby = hobby

this

.education = education

this

.workplace = workplace

this

.position = position } }

class

CoderFilter

{ filterByName(coders, fullName) {

return

coders.filter(

coder

=> coder.fullName === fullName) } filterBySize(coders, language) {

return

coders.filter(

coder

=> coder.language === language) } filterByHobby(coders, hobby) {

return

coders.filter(

coder

=> coder.hobby === hobby) } }

Code language:

JavaScript

(

javascript

)

Vấn đề với CoderFilter là nếu chúng ta muốn lọc theo bất kỳ thuộc tính mới nào khác, chúng ta phải thay đổi mã của CodeFilter. Hãy giải quyết vấn đề này bằng cách tạo một hàm filterByProp.

const

filterByProp =

(

array, propName, value

) =>

array.filter(

element

=> element[propName] === value)

Code language:

JavaScript

(

javascript

)

L — Liskov substitution principle

Một nguyên lý với cái tên khó hiểu nhất. Nó có nghĩa là gì?

If you have a function, that works for a base type, it should work for a derived type

Các đối tượng của class cha có thể được thay thế bởi các đối tượng của các class con mà không làm thay đổi tính đúng đắn của chương trình.

class

Rectangle

{

constructor

(width, height) {

this

._width = width

this

._height = height }

get

width() {

return

this

._width }

get

height() {

return

this

._height }

set

width(value) {

this

._width = value }

set

height(value) {

this

._height = value } getArea() {

return

this

._width *

this

._height } }

class

Square

extends

Rectangle

{

constructor

(size) {

super

(size, size) } }

const

square =

new

Square(

2

) square.width =

3

console

.log(square.getArea())

Code language:

JavaScript

(

javascript

)

Bạn thử đoán xem những gì sẽ được in ra trong Console. Nếu câu trả lời của bạn là 6 thì bạn đúng rồi đấy. Tất nhiên, câu trả lời mong muốn ở đây là 9. Chúng ta có thể thấy một sự vi phạm cổ điển của nguyên lý thay thế Liskov.

Để khắc phục, bạn có thể xác định Square theo cách này:

class

Square

extends

Rectangle

{

constructor

(size) {

super

(size, size) }

set

width(value) {

this

._width =

this

._height = value }

set

height(value) {

this

._width =

this

._height = value } }

Code language:

JavaScript

(

javascript

)

I — Interface segregation principle

Clients should not be forced to depend upon interfaces that they do not use

Thay vì viết một interface cho một mục đích chung chung, chúng ta nên tách thành nhiều interface nhỏ cho các mục đích riêng. Không nên bắt buộc client phải implement các method mà client không cần đến.

Tuy nhiên, trong JavaScript không hề có interface. Có một cách để bắt chước hành vi, nhưng tôi không nghĩ là có nhiều ý nghĩa. Hãy thích nghi tốt hơn nguyên lý này với thế giới JavaScript.

5 nguyên lý SOLID trong JavaScript5 nguyên lý SOLID trong JavaScript

Hãy xác định một lớp abstract Phone sẽ đóng vai trò của interface trong trường hợp của chúng ta:

class

Phone

{

constructor

() {

if

(

this

.constructor.name ===

'Phone'

)

throw

new

Error

(

'Phone class is absctract'

) } call(number) {} takePhoto() {} connectToWifi() {} }

Code language:

JavaScript

(

javascript

)

Chúng ta có thể sử dụng nó để định nghĩa iPhone không?

class

IPhone

extends

Phone

{ call(number) { } takePhoto() { } connectToWifi() { } }

Code language:

JavaScript

(

javascript

)

Được rồi, nhưng đối với Nokia 3310 cũ, giao diện này sẽ vi phạm nguyên lý trên:

class

Nokia3310

extends

Phone

{ call(number) { } takePhoto() { } connectToWifi() { }

Code language:

JavaScript

(

javascript

)

D — Dependency Inversion principle

High-level modules should not depend on low-level modules

Các module cấp cao không nên phụ thuộc vào các module cấp thấp, cả hai nên phụ thuộc vào abstraction.

class

FileSystem

{ writeToFile(data) { } }

class

ExternalDB

{ writeToDatabase(data) { } }

class

LocalPersistance

{ push(data) { } }

class

PersistanceManager

{ saveData(db, data) {

if

(db

instanceof

FileSystem) { db.writeToFile(data) }

if

(db

instanceof

ExternalDB) { db.writeToDatabase(data) }

if

(db

instanceof

LocalPersistance) { db.push(data) } } }

Code language:

JavaScript

(

javascript

)

Trong trường hợp này, mô-đun cấp cao PersistanceManager phụ thuộc vào các mô-đun cấp thấp, đó là FileSystem, ExternalDBLocalPersistance.

Để tránh sự cố trong trường hợp đơn giản này, chúng ta có thể nên làm điều gì đó như sau:

class

FileSystem

{ save(data) { } }

class

ExternalDB

{ save(data) { } }

class

LocalPersistance

{ save(data) { } }

class

PersistanceManager

{ saveData(db, data) { db.save(data) } }

Code language:

JavaScript

(

javascript

)

Video chia sẻ: SOLID – Những nguyên lý sống còn

Trong video này, chúng ta cũng sẽ bàn đến mối quan hệ giữa các khái niệm và kỹ thuật như:

  • SOLID
  • Design Pattern
  • Refactoring
  • Clean Code
  • Automation Test

Tất cả những khái niệm này, tưởng chừng như rời rạc, nhưng thực ra lại có mối quan hệ gắn bó rất mật thiết. Mỗi khái niệm, kỹ thuật đều có mục đích và nhiệm vụ riêng của nó, đóng góp chung vào chất lượng của sản phẩm.

Kết luận

Giá trị của 5 nguyên lý SOLID trong JavaScript là không rõ ràng. Nhưng nếu bạn tự hỏi mình ‘Tôi có vi phạm nguyên lý SOLID không?’ khi bạn thiết kế kiến ​​trúc của phần mềm, tôi hứa rằng chất lượng và khả năng mở rộng mã của bạn sẽ tốt hơn rất nhiều.

Cảm ơn bạn đã theo dõi bài viết!

Các bạn có thể tham khảo các bài viết hay về JavaScript tại đây.

Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.

Chia sẻ