S.O.L.I.D là gì? Nguyên tắc thiết kế trong lập trình hướng đối tượng

Chắc hẳn ai đã học lập trình hướng đối tượng đều đã biết đến các khái niệm:

Các khái niệm này khá là căn bản mà các bạn mới đi làm thường được hỏi trong các buổi phỏng vấn. Thế nhưng để áp dụng tốt các kiến thức trên thì có phần khó khăn và nếu không khéo project của bạn sẽ đi đến ngõ cụt. Khi bạn sữa lỗi hay thêm một tính năng mà rất khó khăn và tốn thời gian thì có lẽ bạn đã đi sai hướng rồi đấy.

Trong bài viết này chúng ta sẽ cùng nhau bàn luận về nguyên lý SOLID để xem nó có giúp gì cho các dev chúng ta không nhé!

S.O.L.I.D là gì? 

SOLID là nguyên lý thiết kế trong hướng đối tượng được đút kết từ hàng ngàn devloper “chất” trên toàn thế giới và những bài học rút ra từ rất nhiều dự án thất bại. Nếu các bạn nói ok, để tui code rồi từ từ rút ra bài học cho mình thì cũng được, nhưng mà đến bao giờ mới rút đủ dây kinh nghiệm để build một sản phẩm đây =)).

Lúc vừa vào làm công ty, trong lần commit đầu tiên của mình bị comment là “Em code gì gì bậy vậy”, “vậy làm sao sau này người khác vào maintain code của em”, “Chỗ này em thấy em code bị duplicate không”? Sau buổi đó thì mình đã được join vào buổi training về S.O.L.I.D luôn các bạn ạ. Vậy SOLID là gì

S.O.L.I.D là viết tắt của 5 nguyên lý sau:

Single responsibility principle

single_responsibility_principle

Nguyên lý đầu tiên Single Responsibility Principle tương ứng với chữ “S” trong SOLID. 

Phát biểu: Một class nên có một và chỉ một lý do để thay đổi.

Ví dụ

public class UserService {
    public void registerUser() {
        createUser();
        sendEmail();
    }

    private void createUser(){
        // Write code at here
    }

    private void sendEmail() {
        // Write code at here
    }
}

Các bạn thấy class UserServise đảm nhận đến hai nhiệm vụ là tạo user và gửi mail. Như vậy khi các bạn cần thay đổi logic hay mở rộng việc gửi email hoặc thay đổi cách tạo user thì các bạn đều phải vào class UserServise sửa hoặc thêm code. Như vậy thì chúng ta đã vi phạm single responsibility principle rồi đấy. 

Open closed principle

openclosed_principle

Nguyên lý thứ 2 Open closed principle tương ứng với chữ O trong SOLID.

Phát biểu: Một software entities như class, modules, functions, etc.. không nên chỉnh sửa thay vào đó hãy mở rộng chúng.

Theo nguyên lý này thì mỗi khi chúng ta muốn thêm một chức năng mới thì nên viết một class mới mở rộng từ class cũ (bằng cách sử dụng tính kế thừa hoặc sở hữu class cũ). Không nên chỉnh sửa class cũ.

 Liskov substitution principle

liskov_substitution_principle

Nguyên lý Liskov Subtitution Principle tương ứng với chữ L trong SOLID. 

Phát biểu: Các instance của lớp con có thể thay thế các instance của lớp cha mà không làm thay đổi tính đúng đắn của chương trình

public abstract class Bird {
    public abstract void fly();
}
public class Sparrows extends Bird {
    @Override
    public void fly() {
        // your code
    }
}
public class Penguins extends Bird {
    @Override
    public void fly() throws  UnsupportedOperationException{
        throw  new UnsupportedOperationException("UnsupportedOperationException");
    }
}

Trong khi Sparrows có thể bay được nên chúng kế thừa từ Bird. Còn Penguins kế thừa từ Bird nhưng chúng không bay được nên sẽ gây ra lỗi trong chương trình. 

// more code
List<Bird> birds = new ArrayList<>();
for(Bird bird : birds) {
   bird.fly();
}

Đều gì sẽ xảy ra với đoạn code trên, nếu trong List<Bird> có một con con chim nào đó là chim cánh cụt thì chương trình chúng ta sẽ throws exception và crash nếu chúng ta không handle kỹ đấy.

Với Liskov substitution principle ta sẽ tách method Fly() ra một interface riêng, và chỉ những con chim nào biết bay thì sẽ implement interface này vào, còn con chim nào không biết bay thì không phải implement.

Interface Segregation Principle

interface_segregation_principle_thumb

Nguyên lý thứ 4 ứng với chữ I trong SOLID.

Phát biểu: Interface nên được thiết kế thành từng interface nhỏ với nhiều mục đích cụ thể thay vì dùng một Interface lớn.

public interface FileUtils {
    // Read file
    public void readFile(String fileName);
    // Write
    public void WriteFile(String fileName);
    // Strim
    public void strimFile(File file);
}

Chúng ta thấy interface FileUtils chứa rất nhiều các phương thức như readFile, WriteFile, strimFile, thay vì vậy chúng ta nên làm như sau

public interface Reader {
    public void readFile(String fileName);
}
public interface Writer {
    public void writeFile(String fileName);
}
public interface Strimmer {
    public void readFile(String fileName);
}

Dependency Inversion principle

dependency_inversion_principle_thumb

Phát biểu: 

Các module cấp cao không nên phụ thuộc vào module cấp thấp, cả hai nên phụ thuộc vào interface.
Các class giao tiếp với nhau phải thông qua interface.

Đây là nguyên lý cuối cùng cũng là nguyên lý khó hiểu nhất.

public class Manager {
    List developers;
    List testers;
    public Manager() {
        developers = new ArrayList<>();
        testers = new ArrayList<>();
    }
    public void addDeveloper (Developer developer) {
        this.developers.add(developer);
    }
    public void addTester(Tester tester) {
        this.testers.add(tester);
    }
}

Chúng ta thấy Manager quản lý các tester và developer, nhìn vào hàm khởi tạo trong thật lằng nhằng phải không? nếu sau này mở rộng thêm BA, SA, thì chúng ta lại phải mở rộng code của class Manger. 

Đế giải quyết chúng ta hãy xem hình sau

Chúng ta sẽ định nghĩa class Employee cho Developer, Tester thừa kế, và Manger chỉ quan tâm đến Employee thôi.

Tham khảo: http://blogs.msdn.com/b/cdndevs/archive/2009/07/15/the-solid-principles-explained-with-motivational-posters.aspx

5 1 vote
Article Rating
Subscribe
Notify of
guest
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x