Lập trình viên viết ra những phần mềm, những ứng dụng, phần mềm tốt đòi hỏi lập trình viên phải cần có những kĩ năng, kinh nhiệm, từng chải qua nhiều dự án. Để viết được những phần mềm như vậy bạn cần học nhiều thứ và điều bạn cần học trong số đó là tìm hiểu nguyên tắc SOLID.

SOLID là gì?

Hiểu một cách đơn giản, những nguyên tắc SOLID giúp cho các lập trình viên phát triển, xây dựng ra các phần mềm dễ dàng và clean hơn, dễ dàng bảo trì và sửa đổi khi quy mô của phần mềm phát triển lớn hơn, phức tạp hơn.

SOLID là viết tắt của 5 chữ cái đầu trong 5 nguyên tắc này:

  • Single responsibility priciple
  • Open/Closed principle
  • Liskov substitution principe
  • Interface segregation principle
  • Dependency inversion principle

Vì sao chúng ta cần áp dụng SOLID trong phần mềm

Mỗi loại design pattern hoặc principle được tổng hợp lại nhằm mục đích để giải quyết những bài toán thường gặp hay những bài toán lặp đi lặp lại trong quá trình thực hiện phát triển phần mềm. Nguyên tắc SOLID sinh ra với mục đích tương tự như vậy, chúng giúp phần mềm hoạt động tốt hơn.

Khi áp dụng SOLID vào việc phát triển phần mềm, ứng dụng thì phần mềm các bạn tạo ra sẽ dễ hiểu, dễ duy trì và linh hoạt hơn.

Quá trình ra đời của SOLID?

Lập trình hướng đối tượng (object oriented programming – OOP) là một mô hình lập trình được sử dụng nhiều hiện nay. Các tính chất hướng đối tượng đáng chú ý như là:

  • Tính trừu tượng
  • Tính đóng gói
  • Tính kế thừa
  • Tính đa hình

Những tính chất của hướng đối tượng này giúp chúng ta xây dựng được các chương trình giải quyết được nhiều vấn đề cụ thể khác nhau. Hầu hết lập trình viên đều đã biết các tính chất này của OOP, nhưng cách thức để phối hợp các tính chất này với nhau để tăng hiệu quả của ứng dụng thì không phải ai cũng nắm được. Một trong những chỉ dẫn(hướng dẫn) để giúp chúng ta sử dụng được OOP hiệu quả hơn đó là thực hiện theo nguyên tắc SOLID.

5 nguyên tắc của SOLID

1. Nguyên tắc trách nhiệm đơn lẻ (Single Responsibility Principle)

Có thế có nhiều cách để có thể đánh giá chất lượng code của một chương trình. Nhưng yêu cầu quan trọng là code của chương trình(ứng dụng) phải có khả năng bảo trì được (maintainable).

1.1 Nguyên tắc trách nhiệm đơn lẻ là gì?

Một class chỉ nên thực hiện một công việc. Nói cách khác, một class chỉ nên thực hiện một công việc, thay vì thực hiện nhiều việc trong một class thì chúng ta có thể cho mỗi class thực hiện một công việc.

1.2 Tại sao cần phải đơn lẻ?

Nếu một class có quá nhiều chức năng cũng sẽ trở nên cồng kềnh và phức tạp hơn. Cùng với sự phát triển của ứng dụng, các requirement liên tục thay đổi dẫn tới sự thay đổi của code. Nếu một class quá nhiều chức năng sẽ rất khó thay đổi, tốn nhiều thời gian cho việc sửa chữa hơn và gây thể ảnh hưởng tới các module đang chạy.

1.3 Làm sao để Đơn lẻ ?

Về cơ bản, chúng ta cần phải thiết kế các module, các class sao cho đơn giản nhất có thể, mỗi module chỉ thực hiện một chức năng cụ thể duy nhất.

1.4 Ví dụ

solid viết code
solid viết code

Việc chia nhỏ ra như vậy ta thấy rằng có thể dễ dàng gọi đến lớp tương ứng với từng công việc

nguyên tắc solid
nguyên tắc solid

1.5 Kết luận

Nguyên tắc này nghe có vẻ đơn giản, nhưng thực ra cần nhiều thời gian để có thể sử dụng thành thục nguyên lý này. Tuy nhiên cũng không nên vội vàng, hãy để thời gian để bạn hiểu rõ được ý nghĩa thực sự của nguyên lý này.

2. OCP Nguyên tắc đóng mở (The Open-Closed Principle)

2.1 Nguyên tắc đóng mở là gì?

“Cần hạn chế việc chỉnh sửa bên trong một Class hoặc Module có sẵn, thay vào đó hãy xem xét mở rộng chúng.”

Có hai vấn đề cần quan tâm tới trong nguyên tắc này:

  • Hạn chế sửa đổi: Có nghĩa là không nên chỉnh sửa mã code của một module hoặc class có sẵn, vì làm như vậy sẽ ảnh hưởng tới tính đúng đắn của chương trình.
  • Ưu tiên mở rộng: Khi ứng dụng của bạn cần phát triển thêm tính năng mới, ta nên kế thừa và mở rộng các module/class có sẵn thành các module con lớn hơn. Các module/class con vừa có các đặc tính của lớp cha (đã được kiểm chứng đúng đắn) và bổ sung tính năng mới phù hợp với yêu cầu.

2.2 Ứng dụng

Nguyên tắc OCP xuất hiện khắp mọi ngóc ngách trong việc lập trình. Ứng dụng cơ bản của nguyên lý này là hệ thống plug-in(cho Eclipse, Visual Studio, add-on Chrome…). Để thêm các tính năng mới cho phần mềm, chỉ việc cài đặt các plug-in này, không cần can thiệp gì đến source code sẵn có.

3. Nguyên tắc phân vùng Liskov (The Liskov Substition Principle)

3.1 Nguyên tắc đóng mở là gì?

“Trong một chương trình, các object của class con có thể thay thế class cha mà không làm thay đổi tính đúng đắn của chương trình”.

3.2 Chúng ta hãy xem ví dụ sau đây:

nguyên tắc phân vùng
nguyên tắc phân vùng

Chúng ta có class Square kế thừa Rectangle

nguyên tắc phân vùng 2- solid
nguyên tắc phân vùng 2

4. Nguyên tắc phân tách giao diện (Interface Segregation Principle)

“Nếu Interface quá lớn thì nên tách thành những interface nhỏ hơn, với nhiều mục đích cụ thể.”

Nhờ thiết kế này, chúng ta không còn phải lo lắng tới việc phải implement những hàm không cần thiết. Sẽ dễ dàng kiểm soát được việc mở rộng hơn.

5. Dependency inversion principle

Nguyên tắc này được phát biểu như sau:

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

-Interface (abstraction) không nên phụ thuộc vào chi tiết, mà ngược lại. ( Các class giao tiếp với nhau thông qua interface, không phải thông qua implementation.)

Có thể hiểu như sau:

Những thành phần trong 1 chương trình chỉ nên phụ thuộc vào những cái trừu tượng (abstraction).
Những thành phần trừu tượng không nên phụ thuộc vào các thành phần mang tính cụ thể mà nên ngược lại.

Xem thêm: Design pattern là gì?

Trên đây là những nguyên tắc thiết yếu cần có trong lập trình. Cảm ơn các bạn đã xem bài viết của mình.

Tham khảo thêm tại: https://en.wikipedia.org/wiki/SOLID

Có thể bạn quan tâm:

  1. Lập Trình Hướng Đối Tượng căn bản, 4 tính chất của oop?

Đừng comment trên Code xấu – Hãy viết lại nó.”

Không gì hữu ích hơn bằng một comment được đặt đúng chỗ. Nhưng cũng sẽ trở nên tồi tệ, gây nhiễu loạn thông tin khi đặt sai hay là cung cấp thông tin không đúng.

Việc dùng đúng những comment là một cách để bù đắp cho sự thất bại của chúng ta trong việc thể hiện ý nghĩa của những dòng code. Chính xác là vậy – comment luôn luôn là sự thất bại.

Vậy nên, khi bạn cần viết comment, bạn hãy suy nghĩ kỹ xem liệu có cách nào đó để biến các dòng code thành chính xác những gì bạn muốn thể hiện hay không.

Điều tin tưởng tuyệt đối nhất là: Code. Chỉ có code mới nói cho bạn biết thật sự là nó làm gì. Nó là nguồn thông tin chính xác duy nhất. Do đó, mặc dù comment đôi khi là cần thiết, nhưng chúng tôi sẽ cố gắng để giảm thiểu nó.

Đừng dùng comments để trang trí cho mã xấu (Comments Do Not Make Up for Bad Code)

Một trong những động lực to lớn để viết comment là do code tồi, code tối nghĩa.

Khi chúng ta viết một module và chúng ta biết rằng nó lộn xộn và vô tổ chức. Chúng ta biết nó bữa bộn Vậy nên chúng ta tự nhủ rằng: “Ồ, tốt hơn nên viết comment ở đây!”. Không! Tốt hơn bạn nên viết lại code!

Code rõ ý nghĩa và rõ ràng với ít comment sẽ tuyệt vời hơn so với code tối nghĩa, phức tạp với nhiều comment. Thay vì dành thời gian để viết bình luận cho mớ lộn xộn, bạn nên dọn dẹp nó.

Giải thích ý nghĩa ngay trong code(Explain Yourself in Code)

Trong nhiều trường hợp, việc thay thế code nghèo nàn bằng cách tạo ra những hàm mới đã nói lên đủ những lời comment mà bạn muốn viết dành riêng cho đoạn code đó.

// Check to see if the employee is eligible for full benefits

if ((employee.flags & HOURLY_FLAG) &&

(employee.age > 65))

Hay cái này?

if (employee.isEligibleForFullBenefits())

comment code
comment code

Good Comments

Một số comment là cần thiết hoặc có ích. Chúng ta sẽ xem một số trường hợp được cho là xứng đáng để bạn bỏ công ra viết.

  • Bình luận pháp lý (Legal Comments)

Điều này tùy thuộc vào tiêu chuẩn viết code của dự án bạn đang làm. Ví dụ: Bản quyền và quyền tác giả là điều cần thiết và hợp lý để đưa vào comment lúc bắt đầu của mỗi tập tin mã nguồn.

/* Copyright (C) 2003,2004,2005 by Object Mentor, Inc.
* All rights reserved.

* Released under the terms of the GNU General Public License version
* 2 or later.
*/

  • Bình luận cung cấp thông tin (Informative Comments)

Cung cấp thông tin với vài dòng comment đôi khi khá là hữu dụng.

// Returns an instance of the Responder being tested.

protected abstract Responder responderInstance();

Một comment như vậy đôi khi khá là hữu ích, nhưng tốt hơn hết là nên truyền tải thông tin thông qua tên của function nếu có thể. (responderBeingTested.)

// format matched kk:mm:ss EEE, MMM dd, yyyy

Pattern timeMatcher = Pattern.compile(

“\\d*:\\d*:\\d* \\w*, \\w* \\d*, \\d*”);

Trong trường hợp này comment cho chúng ta biết định dạng về thời gian trong hàm SimpleDateFormat.format với một chuỗi xác định. Tuy nhiên, nó vẫn có thể rõ ràng hơn nếu mã này được chuyển sang một lớp đặc biệt với mục đích chuyển đổi định dạng thời gian. Sau đó, những comment có thể sẽ không cần thiết nữa.

  • Giải thích về mục đích (Explanation of Intent)

Đôi khi comment không chỉ cung cấp thông tin về những dòng code mà còn cung cấp ý định đằng sau nó.

public int compareTo(Object o)

{

if(o instanceof WikiPagePath)

{

WikiPagePath p = (WikiPagePath) o;

String compressedName = StringUtil.join(names, “”);

String compressedArgumentName = StringUtil.join(p.names, “”);

return compressedName.compareTo(compressedArgumentName);

}

return 1; // we are greater because we are the right type.

}

  • Làm dễ hiểu (Clarification)

Đôi khi bạn cần dùng comment để diễn giải ý nghĩa của các đối số khó hiểu hoặc giá trị trả về, để biến chúng thành thứ gì đó có thể hiểu được.

Nhưng chỉ khi một phần thư viện chuẩn hoặc trong code mà bạn không thể thay đổi được, một bình luận có thể sẽ hữu ích.

{

   WikiPagePath a = PathParser.parse(“PageA”);

   WikiPagePath ab = PathParser.parse(“PageA.PageB”);

   WikiPagePath b = PathParser.parse(“PageB”);

   WikiPagePath aa = PathParser.parse(“PageA.PageA”);

   WikiPagePath bb = PathParser.parse(“PageB.PageB”);

   WikiPagePath ba = PathParser.parse(“PageB.PageA”);

   assertTrue(a.compareTo(a) == 0); // a == a

   assertTrue(a.compareTo(b) != 0); // a != b

   assertTrue(ab.compareTo(ab) == 0); // ab == ab

   assertTrue(a.compareTo(b) == -1); // a < b

   assertTrue(aa.compareTo(ab) == -1); // aa < ab

   assertTrue(ba.compareTo(bb) == -1); // ba < bb

   assertTrue(b.compareTo(a) == 1); // b > a

   assertTrue(ab.compareTo(aa) == 1); // ab > aa

   assertTrue(bb.compareTo(ba) == 1); // bb > ba

}

Dĩ nhiên, khả năng các comment dạng này cung cấp thông tin không chính xác là khá cao. Làm dễ hiểu (Clarification) là cần thiết và nó cũng đầy rủi ro. Vì vậy, trước khi viết bình luận như vậy hãy quan tâm xem có cách nào tốt hơn không, để có một lựa chọn đúng đắn nhất.

  • Cảnh báo về các hậu quả (Warning of Consequences)

Đôi khi nó rất hữu ích để cảnh báo các lập trình viên khác về hậu quả xảy ra. Ví dụ, giải thích lý do tại sao một test case cụ thể bị tắt đi:

// Don’t run unless you

// have some time to kill.

public void _testWithReallyBigFile()

{

   writeLinesToFile(10000000);

   response.setBody(testFile);

   response.readyToSend(this);

   String responseString = output.toString();

   assertSubString(“Content-Length: 1000000000”, responseString);

   assertTrue(bytesSent > 1000000000);

}

Có thể còn nhiều cách tốt hơn để giải quyết vấn đề này. Tôi đồng ý. Nhưng bình luận được đưa ra ở đây là hoàn toàn hợp lý. Nó ngăn chặn một số lập trình viên khác.

  • TODO Comments

TODO là những công việc mà lập trình viên nghĩ là cần nên làm, nhưng vì lý do nào đó mà họ không thể thực hiện nó ngay lúc này. Nó có thể là một lời nhắc nhở để xóa một tính năng không cần thiết, một lời bào chữa cho ai đó khi nhìn vào vấn đề. Nó có thể là một yêu cầu dành cho ai đó để nghĩ ra một cái tên hay hơn, hay nhắc nhở về một sự thay đổi nào đó cho sự kiện đã được lên kế hoạch từ trước. Nhưng dù TODO có là gì đi nữa, nó chắc chắn không phải là lý do để bạn quăng đống code ẩu, code bừa vào dự án.

  • Amplification (Phóng đại)

Comment có thể được dùng để khuếch đại tầm quan trọng của một cái gì đó có vẻ không quan trọng:

String listItemContent = match.group(3).trim();

// the trim is real important. It removes the starting

// spaces that could cause the item to be recognized

// as another list.

new ListItemWidget(this, listItemContent, this.level + 1);

return buildList(text.substring(match.end()));

  • Javadocs in Public APIs

Không có gì hữu ích và tuyệt vời bằng public API được mô tả tốt. Các javadoc của thư viện chuẩn của Java là một trường hợp điển hình. Sẽ rất khó để viết các chương trình Java mà thiếu chúng.

Bad Comments

Đa số các comment rơi vào thể loại này. Chúng thường được sử dụng như cái cớ cho việc viết code rởm hoặc biện minh cho các cách giải quyết đầy thiếu sót.

  • Độc thoại

Quăng vào một comment chỉ vì bạn thấy thích.

  • Các comment thừa thải

Những comment này, có lẽ, còn làm người đọc mất thời gian hơn so với việc đọc code của hàm.

  • Các comment sai sự thật

….Update …

Bài viết cùng chủ đề:

  1. Clean code tiếng việt Chap 1
  2. Clean code tiếng việt chap 2
  3. Clean code tiếng việt chap 6 – ĐỐI TƯỢNG VÀ CẤU TRÚC DỮ LIỆU