Vinova tuyển lập trình viên Mobile & Web ở Hà Nội, lương $300-1000

Article: Các nguyên lí cơ bản trong thiết kế hướng đối tượng 3905

Design pattern
phananhvu.myopenid.com 125
Over 4 years ago

Trong một thảo luận về kế thừa không hết ở JavaVietnam, người ta bàn về vấn đề thiết kế một pattern làm sao để lớp con kế thừa lớp cha nhưng tự loại bỏ những method mà nó không mong muốn. Điều đó có nghĩa là lớp con không tuân thủ "giao ước" (xin dùng từ của anh cl, ý chỉ contract) định trước, tức là nó không tuân thủ một trong những nguyên tắc cơ bản của thiết kế hướng đối tượng (OOD - Object oriented design) là Liscov substitution principle. Điều đó cho thấy khi thiết kế, tìm giải pháp cho một vấn đề nào đó, việc nắm rõ các nguyên lí cơ bản của OOD là vô cùng quan trọng.

Bài này xin giới thiệu về 5 nguyên tắc cơ bản của OOD là:

  1. Open closed
  2. Liskov substitution
  3. Dependency inversion
  4. Interface segregation
  5. Single responsibility

Open closed

Ivar Jacobson từng nói: “Để thiết kế các hệ thống lâu dài, cần luôn tâm niệm rằng các hệ thống luôn thay đổi trong quá trình sử dụng". (All systems change during their life cycles. This must be borne in mind when developing systems expected to last longer than the first version.”). Năm 1988, Bertrand Meyer đưa ra mục tiêu để thực hiện điều mà Ivar Jacobson nói trên mà sau này trở thành nguyên lí open-closed nổi tiếng. Đó là: SOFTWARE ENTITIES (CLASSES, MODULES, FUNCTIONS, ETC.) SHOULD BE OPEN FOR EXTENSION, BUT CLOSED FOR MODIFICATION.

Các chương trình áp dụng nguyên lí open-close được thay đổi bằng cách thêm code mới chứ không phải sửa code có sẵn. Bằng cách này, tránh được thay đổi dây chuyền trong toàn bộ chương trình. Tuy nhiên, mỗi entity của chương trình có thể đóng với thay đổi này nhưng lại không đóng với thay đổi nào đó khác. Do đó, tính đóng này chỉ là tương đối và nhiệm vụ của người thiết kế là với mỗi đặc thù của chương trình, ưu tiên đóng các thuộc tính dễ thay đổi nhất.

Để "đóng" các entity của chương trình, có thể sử dụng giải pháp abstraction, data driven, ..

Open-closed là nguyên li trung tâm, rất quan trọng trong thiết kế hướng đối tượng vì chính nguyên lí này làm cho lập trình hướng đối tượng có tính tái sử dụng (reusability) và dễ bảo trì (maintainability).

Tham khảo thêm ở đâyở đây.

Liskov substitution

Nguyên lí này được phát biểu như sau: FUNCTIONS THAT USE POINTERS OR REFERENCES TO BASE CLASSES MUST BE ABLE TO USE OBJECTS OF DERIVED CLASSES WITHOUT KNOWING IT.

Tức là hoạt động của các function có sử dụng reference hay pointer tới object của lớp cha cần được đảm bảo là không bị ảnh hưởng khi thay thế reference hay pointer tới object của lớp cha bởi reference hay pointer tới object của lớp con và function đó không cần biết về sự tồn tại của lớp con. Khi đó, các virtual member functions ở lớp cha cũng phải có ở lớp con, và phải thực hiện một công việc có nghĩa.

Nếu nguyên lí này bị vi phạm, function có sử dụng reference hay pointer tới object của lớp cha phải kiểm tra kiểu của object để đảm bảo chương trình có thể chạy đúng, và việc này vi phạm nguyên lí open-closed nhắc đến ở trên.

Tham khảo thêm ở đâyở đây.

Dependency inversion

Việc áp dụng hai nguyên lí open-closedLiskov substitute một cách chặt chẽ có thể tổng quát hóa thành nguyên lí depndency inversion được phát biểu như sau:

  1. HIGH LEVEL MODULES SHOULD NOT DEPEND UPON LOW LEVEL MODULES. BOTH SHOULD DEPEND UPON ABSTRACTIONS.
  2. ABSTRACTIONS SHOULD NOT DEPEND UPON DETAILS. DETAILS SHOULD DEPEND UPON ABSTRACTION.
Thực hiện một bằng cách dùng abstract layer như hình dưới.

Tham khảo thêm ở chỗ này, chỗ kiachỗ đó.

Interface segregation

Nguyên lí này được phát biểu như sau: CLIENTS SHOULD NOT BE FORCED TO DEPEND UPON INTERFACES THAT THEY DO NOT USE

Khi một client bị ép phải phụ thuộc vào những interface mà nó không sử dụng thì nó sẽ bị lệ thuộc vào những thay đổi của interface đó. Chúng ta cần phải tránh điều này nhiều nhất có thể bằng cách chia nhỏ interface.

Tham khảo thêm ở đây.

Single responsibility

Nguyên lí này được phát biểu như sau: THERE SHOULD NEVER BE MORE THAN ONE REASON FOR A CLASS TO CHANGE.

Tham khảo thêm ở đâyở đây.

Chú ý: để hiểu bài này, cần có kiến thức cơ bản về lập trình hướng đối tượng, nhất là các khái niệm encapsulation, inheritance, polimorphism.

Comments

phananhvu.myopenid.com 125
over 4 years ago

Ở đây có khóa học về OOD đắt kinh khủng: $2050!

Dân mình nghèo, lấy đâu ra xiền học đây. Đành "cọp" cái curriculum của nó về rồi dựa vào đấy ngâm cú dzậy.

By the end of this course, you will know how to:

  • Understand and apply the principles of object-oriented design and dependency management
  • Learn professional practices that make Java projects succeed
  • Learn important design patterns and how and when to apply them
  • Be able to apply Test-Drive Development in the context of Object Oriented Design Principles and Patterns

Course Outline

<!-- InstanceEndEditable --> <!-- InstanceBeginEditable name="DialogContent" -->

Day 1

Coding for Readability and Maintainability
  • The Problems
  • The Objectives
  • Forms of Software Rot
  • Attributes of a Good Design
  • Clean Code Qualities
  • Professional Responsibilities
  • Automated testing, TDD and Refactoring Practices Review
  • Refactoring
  • Exercise
Single Responsibility Principle
  • Problem addressed by SRP
  • Example design violating SRP
  • Example design conforming to SRP
  • Patterns built on SRP
  • Exercise

Day 2

Dependency Inversion Principle
  • Problems with Procedural Programming
  • Object-Oriented Programming
  • Problem addressed by DIP
  • The Dependency Inversion Principle
  • Dependency Inversion Heuristics
  • When is DIP appropriate/inappropriate
  • Example design violating DIP
  • Example designs conforming to DIP
  • Exercise
Open/Closed Principle
  • Problem addressed by OCP
  • The Open/Closed Principle
  • When is OCP appropriate/inappropriate
  • Several Example designs violating OCP
  • Several example designs conforming to OCP
  • Exercise
Liskov Substitution Principle
  • Problem addressed by LSP
  • The Liskov Substitution Principle
  • Example designs violating LSP
  • Example designs conforming to LSP
  • Relationship OCP/LSP
  • When is “instance of is” not an LSP problem
  • Exercise

Day 3

Interface Segregation
  • Problem addressed by ISP
  • The Interface Segregation Principle
  • Example designs violating ISP
  • Example designs conforming to ISP
  • When is ISP appropriate/inappropriate
  • Exercise
Law of Demeter
  • Problem addressed by LoD
  • Example design violating LoD
  • Example designs conforming to LoD
  • When is LoD appropriate/inappropriate
  • Pro/Con discussion
  • Exercise
Packaging Cohesion Principles
  • Problem being solved by package cohesion principles
  • Package Cohesion Principles
  • The Reuse-Release Equivalency Principle
  • The Common Closure Principle
  • The Common Reuse Principle
  • Relationship between cohesion principles
  • Relationship to class design principles
  • Exercise

Day 4

Packaging Coupling Principles
  • Problem being solved by Packaging Coupling Principles
  • Packaging Objectives
  • Package Coupling Principles
  • The Acyclic Dependencies Principle
  • The Stable Dependencies Principle
  • The Stable Abstractions Principle
  • Exercise
Practices That Support the Principles
  • Simple Design
  • Automated Testing
  • Test-Driven Development
  • Refactoring
  • Teamwork
  • Pair Programming
  • Collective Ownership
  • Quick Design Sessions
  • Continuous Integration
  • Code/Design Reviews

Summary

  • Clean code
  • Professional Responsibilities
  • SOLID Principles
  • Packaging Principles
chauhonglinh.myopenid.com 2
over 4 years ago

Có một số câu hỏi thảo luận về bài này:

1) Trong 5 nguyên tắc cơ bản của OOD được trình bày trong bài viết, chỉ trừ có Dependency Inversion,  4 cái còn lại liệu có lý tưởng không?

2) Có trường hợp nào mình không cần phải theo một hoặc một vài nguyên tắc đó không?

3)  Liệu những nguyên tắc cơ bản đó còn đúng cho OOP ngày nay không?

phananhvu.myopenid.com 125
over 4 years ago
Theo em thì cả 5 đều không thể là lí tưởng được. Không thể nào thỏa mãn được cả 5 nếu xét trên mọi phương diện. Tùy từng project, người thiết kế phải thiết kế càng hiệu quả càng tốt. Bởi thế mà không có giải pháp tốt nhất, chỉ có giải pháp tốt hơn.

Trường hợp OCP, người thiết kế phải xét xem sau này những tính năng nào có khả năng phải thay đổi nhất để "close" hợp lí. Sau này khi cần thay đổi, ko phải sửa code cũ.

Về LSP, em nghĩ nó giống duck typing của Ruby, nếu con chó mà quack được thì vẫn đối xử với chó như vịt. Theo em, những chương trình yêu cầu phải có nhiều implementation khác nhau cho một class thì phải tuân thủ theo nguyên lí này.

Về DIP, thực chất là sự tuân thủ chặt chẽ OCP và LSP.

Về 2 nguyên lí còn lại thì em chưa nắm thật rõ :D

Hay anh Lĩnh đưa ra yêu cầu thiết kế một hệ thống nào đó giúp em để em tập áp dụng các nguyên lí và các pattern của OOP. Trong quá trình thiết kế, xin nhờ anh cố vấn :D
chauhonglinh.myopenid.com 2
over 4 years ago
Gợi ý một cách chi tiết hơn về OCP thì nó như thế này:

- Thứ nhất, nó có thể đúng vào hoàn cảnh là trong một nhóm làm việc hoặc nhiều nhóm làm việc cùng nhau, có người viết framework, và những người kia mở rộng framework để tạo thành phần mềm, thì việc mở rộng chức năng hoặc thay đổi chức năng bằng cách mở code của class có sẵn ra để sửa lại là không được khuyến khích. Tuy nhiên, trong những trường hợp re-use code của người khác và sử dụng opensource mà nó không thực sự lý tưởng cho mục đích của mình, và việc mở rộng bằng inheritance hay composition lại tạo ra những phức tạp không cần thiết, thậm chí tạo ra thiết kế dở hơi, thì việc refactoring, hoặc mở code của class ra viết lại là rất cần thiết.

- Thứ hai, đối với những dynamic language, ví dụ như Smalltalk hay Ruby, thì ranh giới giữa việc modification và extension là rất mờ nhạt. Ví dụ các chú có thể thấy là người ta không cần phải mở code của class String hay Fixnum ra viết lại, mà vẫn có thể thêm methods và variables vào class String hay Fixnum.

- Nhiều khi do chỉ cần thêm một chức năng, mà tạo ra một subclass là việc rất dở hơi. Tạo ra khoảng 5 unrelated functionalities thì lại cần 5 subclass, thì số lượng code phải viết thêm rất nhiều, khối lượng công việc bảo trì cũng tăng lên, và nhiều khi tạo ra các class hierarchy không tự nhiên.

Nói về Interface segregation, anh gợi ý một cách chi tiết hơn thì nó như thế này: Trong distributed programming, có một design pattern là Facade design pattern. Nói chung là nó vi phạm Interface segregation, nhưng tại sao người ta vẫn dùng? Tiếp nữa, trong dynamic language, ai cần interface? Ai sợ interface thay đổi?


Còn mấy cái còn lại, các chú cố gắng đào sâu thêm về chi tiết nhé.

You must login to be able to comment

Uploaded files

No file uploaded yet

You must login to be able to upload

Nhà tài trợ:

Mọi người đều tự do viết bài, sửa bài của người khác, và bình luận ở trang web này. Bạn muốn chủ động tạo bài mới để chia sẻ kinh nghiệm với mọi người? Xin click link ở dưới.

Create new content