1) SOLID Principles
https://scotch.io/bar-talk/s-o-l-i-d-the-first-five-principles-of-object-oriented-design
https://www.codeproject.com/Articles/703634/SOLID-architecture-principles-using-simple-Csharp
S - SRP – Single Responsibility Principle
+ Một class chỉ nên giữ một trách nhiệm duy nhất
+ Chỉ có thể thay đổi class với một lý do duy nhất
O - OCP – Open/Closed Principle
+ Dễ đóng mở: Dễ dàng nâng cấp, mở rộng, thêm tính năng mới cho một module khi có yêu cầu.
+ Khó sửa chữa: Hạn chế sửa đổi source code của module sẵn có.
Tóm lại: Có thể thoải mái mở rộng module nhưng hạn chế sửa đổi bên trong module đó.
L - LSP – Liskov Substitution Principle
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.
I - ISP – Interface Segregation Principle
Thay vì dùng 1 interface lớn, ta nên tách thành nhiều interface nhỏ, với nhiều mục đích cụ thể.
Để thiết kế một hệ thống linh hoạt, dễ thay đổi, các module của hệ thống nên giao tiếp với nhau thông qua interface. Mỗi module sẽ gọi chức năng của module khác thông qua interface mà không cần quan tâm tới implementation bên dưới.
D - DIP – Dependency Inversion Principle
1. 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.
2. 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.
Với cách code thông thường, các module cấp cao sẽ gọi các module cấp thấp. Module cấp cao sẽ phụ thuộc và module cấp thấp, điều đó tạo ra các dependency. Khi module cấp thấp thay đổi, module cấp cao phải thay đổi theo.
Nếu tuân theo DIP, các module cấp thấp lẫn cấp cao đều phụ thuộc vào một interface không đổi. Ta có thể dễ dàng thay thế, sửa đổi module cấp thấp mà không ảnh hưởng gì tới module cấp cao.
2) Design Patterns
Design patterns là những khuôn mẫu để giải quyết một vấn đề nào đó.
Design patterns là các mẫu thiết kế hay giải pháp để giải quyết các vấn đề chung, thường gặp trong lập trình.
+ Kết nối với database mà không dùng Singleton thì hỏng.
+ Cache mà không dùng Singleton thì cũng hỏng.
+ Render view thì chắc chắn phải gặp anh Factory rồi.
+ Mô hình MVC không có Adapter làm kĩ thuật trung gian thì càng dựng càng rối.
+ Khi nào bạn cảm thấy không hài lòng về tính mở mộng, thay thế, lặp lại hoặc tái sử dụng code của mình, lúc đó bạn sẽ nghĩ đến design pattern.
3) WYSIWYG
What You See Is What You Get
https://www.izwebz.com/design-usability/khong-nen-dung-wysiwyg-editor/
4) YAGNI
You Ain’t Gonna Need It: Các chức năng ấy rồi sẽ không cần thiết
5) DRY
Don’t repeat yourself: Đừng lặp lại code của bạn
6) KISS
Keep it simple, stupid: Cứ đơn giản thôi, đồ ngu!
Occam's razor - "Không đưa ra nhiều giả thiết nếu không cần thiết. Cái gì cần ít giả thiết để chứng minh sẽ không thể chứng minh được bằng nhiều giả thiết"
Albert Einstein – "Làm cái gì cũng nên đơn giản nhất có thể, nhưng đơn giản quá thì không được".
Leonardo da Vinci – "Đơn giản nhất chính là tinh xảo nhất".
Antoine de Saint-Exupéry – "Hoàn hảo, không phải là không thêm vào được nữa, mà là không thể bớt đi được nữa".
7) Nguyên tắc bất ngờ nhỏ nhất (Least Astonishment)
Đây là 1 nguyên tắc về giao diện người dùng.
Một ví dụ đơn giản :
Trên 1 interface có 2 chức năng :
-
Ấn ctrl+Q để thoát chương trình.
- Nhập macro (lưu 1 tổ hợp phím mang 1 chức năng nào đó để tiện cho việc sử dụng về sau).
Sẽ có trường hợp user muốn dùng Ctrl+Q cho macro của mình, nên hành xử đúng với nguyên tắc bất ngờ nhỏ nhất chính là : trong khi nhập macro thì Ctrl+Q được coi như là tổ hợp phím bình thường, không phải là lệnh tắt chương trình. Đây chính là điều gây bất ngờ ít nhất cho người dùng.
8) OOP (Object Oriented Programming)
+ Object-Oriented Languages: OOLs
+ Object-Oriented Programming: OOP
Sự trừu tượng hóa dữ liệu:
Sự trừu tượng hóa dữ liệu (Data abstraction) tác động trên các dữ liệu cũng tương tự như sự trừu tượng hóa theo chức năng. Khi có trừu tượng hóa dữ liệu, các cấu trúc dữ liệu và các phần tử có thể được sử dụng mà không cần bận tâm đến các chi tiết cụ thể. Chẳng hạn như các số dấu chấm động đã được trừu tượng hóa trong tất cả các ngôn ngữ lập trình, Chúng ta không cần quan tâm cách biểu diễn nhị phân chính xác nào cho số dấu chấm động khi gán một giá trị, cũng không cần biết tính bất thường của phép nhân nhị phân khi nhân các giá trị dấu chấm động. Điều quan trọng là các số dấu chấm động hoạt động đúng đắn và hiểu được.
Sự trừu tượng hóa dữ liệu giúp chúng ta không phải bận tâm về các chi tiết không cần thiết. Nếu lập trình viên phải hiểu biết về tất cả các khía cạnh của vấn đề, ở mọi lúc và về tất cả các hàm của chương trình thì chỉ ít hàm mới được viết ra, may mắn thay trừu tượng hóa theo dữ liệu đã tồn tại sẵn trong mọi ngôn ngữ lập trình đối với các dữ liệu phức tạp như số dấu chấm động. Tuy nhiên chỉ mới gần đây, người ta mới phát triển các ngôn ngữ cho phép chúng ta định nghĩa các kiểu dữ liệu trừu tượng riêng.
Một số khái niệm OOP cơ bản như sau:
Đối tượng (object)
Các dữ liệu và chỉ thị được kết hợp vào một đơn vị đầy đủ tạo nên một đối tượng. Đơn vị này tương đương với một chương trình con và vì thế các đối tượng sẽ được chia thành hai bộ phận chính: phần các phương thức (method) và phần các thuộc tính (property). Trong thực tế, các phương thức của đối tượng là các hàm và các thuộc tính của nó là các biến, các tham số hay hằng nội tại của một đối tượng (hay nói cách khác tập hợp các dữ liệu nội tại tạo thành thuộc tính của đối tượng). Các phương thức là phương tiện để sử dụng một đối tượng trong khi các thuộc tính sẽ mô tả đối tượng có những tính chất gì.
Các phương thức và các thuộc tính thường gắn chặt với thực tế các đặc tính và sử dụng của một đối tượng.
Trong thực tế, các đối tượng thường được trừu tượng hóa qua việc định nghĩa của các lớp (class).
Tập hợp các giá trị hiện có của các thuộc tính tạo nên trạng thái của một đối tượng.
Mỗi phương thức hay mỗi dữ liệu nội tại cùng với các tính chất được định nghĩa (bởi người lập trình) được xem là một đặc tính riêng của đối tượng. Nếu không có gì lầm lẫn thì tập hợp các đặc tính này gọi chung là đặc tính của đối tượng.
Lớp (class)
Một lớp được hiểu là một kiểu dữ liệu bao gồm các thuộc tính và các phương thức được định nghĩa từ trước. Đây là sự trừu tượng hóa của đối tượng. Một đối tượng sẽ được xác lập khi nó được thực thể hóa từ một lớp. Khác với kiểu dữ liệu thông thường, một lớp là một đơn vị (trừu tượng) bao gồm sự kết hợp giữa các phương thức và các thuộc tính. Để có một đối tượng (mà có thể được xem như là một biến) hoạt động được thì việc thực thể hóa sẽ có thể bao gồm việc cài đặt các giá trị ban đầu của các thuộc tính cũng như việc đăng kí bộ nhớ, mà công việc này thường được giao cho các phương thức gọi là “máy kết cấu” (constructor) hay hàm dựng. Ngược lại khi một đối tượng thuộc về một lớp không còn sử dụng nữa thì cũng có thể có một phương thức để xử lý gọi là “máy hủy diệt” (destructor) hay hàm hủy.
Như vậy, để có được các đối tượng thì người lập trình OOP cần phải thiết kế lớp của các đối tượng đó bằng cách xây dựng các thuộc tính và các phương thức có các đặc tính riêng.
Mỗi một phương thức hay một thuộc tính đầy đủ của một lớp còn được gọi tên là một thành viên (member) của lớp đó.
Lớp con (subclass)
Lớp con là một lớp thông thường nhưng có thêm tính chất kế thừa một phần hay toàn bộ các đặc tính của một lớp khác. Lớp mà chia sẽ sự kế thừa gọi là lớp phụ mẫu (parent class).
Lớp trừu tượng hay lớp cơ sở trừu tượng (abstract class)
Lớp trừu tượng là một lớp mà nó không thể thực thể hóa thành một đối tượng thực dụng được. Lớp này được thiết kế nhằm tạo ra một lớp có các đặc tính tổng quát nhưng bản thân lớp đó chưa có ý nghĩa (hay không đủ ý nghĩa) để có thể tiến hành viết mã cho việc thực thể hóa.
Ví dụ: Lớp “hinh_phang” được định nghĩa không có dữ liệu nội tại và chỉ có các phương thức (hàm nội tại) “tinh_chu_vi”, “tinh_dien_tich”. Nhưng vì lớp hình_phẳng này chưa xác định được đầy đủ các đặc tính của nó (cụ thể các biến nội tại là tọa độ các đỉnh nếu là đa giác, là đường bán kính và toạ độ tâm nếu là hình tròn, …) nên nó chỉ có thể được viết thành một lớp trừu tượng. Sau đó, người lập trình có thể tạo ra các lớp con chẳng hạn như là lớp “tam_giac”, lớp “hinh_tron”, lớp “tu_giac”,…. Và trong các lớp con này người viết mã sẽ cung cấp các dữ liệu nội tại (như là biến nội tại r làm bán kính và hằng số nội tại Pi cho lớp “hinh_tron” và sau đó viết mã cụ thể cho các phương thức “tinh_chu_vi” và “tinh_dien_tich”).
Phương Thức (method)
Là hàm nội tại của một lớp (hay một đối tượng). Tùy theo đặc tính mà người lập trình gán cho, một phương pháp có thể chỉ được gọi bên trong các hàm khác của lớp đó, có thể cho phép các câu lệnh bên ngoài lớp gọi tới nó, hay chỉ cho phép các lớp có quan hệ đặc biệt như là quan hệ lớp con, và quan hệ bạn bè (friend) được phép gọi tới nó. Mỗi phương pháp đều có thể có kiểu trả về, chúng có thể trả các kiểu dữ liệu cổ điển hay trả về một kiểu là một lớp đã được định nghĩa từ trước. Một tên gọi khác của phương pháp của một lớp là hàm thành viên.
Người ta còn định nghĩa thêm vài loại phương pháp đặc biệt:
Hàm dựng (constructor) là hàm được dùng để cài đặt các giá tri ban đầu cho các biến nội tại và đôi khi còn dùng để khai báo về việc xử dụng bộ nhớ.
Hàm hủy (destructor) là hàm dùng vào việc làm sạch bộ nhớ và hủy bỏ tên của một đối tượng sau khi đã dùng xong, trong đó có thể bao gồm cả việc xóa các con trỏ nội tại và trả về các phần bộ nhớ mà đối tượng đã dùng.
Trong một số trường hợp thì hàm hủy hay hàm dựng có thể được tự động hóa bởi ngôn ngữ OOP như là trường hợp của Visual C++, C#.
Tiện ích (utility) là các hàm chỉ họat động bên trong của một lớp mà không cho phép môi trường bên ngoài gọi tới. Các hàm này có thể là những tính toán trung gian nội bộ của một đối tượng mà xét thấy không cần thiết phải cho thế giới bên ngoài của đối tượng biết là gì.
Thuộc tính (attribute)
Thuộc tính của một lớp bao gồm các biến, các hằng, hay tham số nội tại của lớp đó. Ở đây, vai trò quan trọng nhất của các thuộc tính là các biến vì chúng sẽ có thể bị thay đổi trong suốt quá trình hoạt động của một đối tượng. Các thuộc tính có thể được xác định kiểu và kiểu của chúng có thể là các kiểu dữ liệu cổ điển hay đó là một lớp đã định nghĩa từ trước. Như đã ghi, khi một lớp đã được thực thể hoá thành đối tượng cụ thể thì tập họp các giá trị của các biến nội tại làm thành trạng thái của đối tượng. Giống như trường hợp của phương pháp, tùy theo người viết mã, biến nội tại có thể chỉ được dùng bên trong các phương pháp của chính lớp đó, có thể cho phép các câu lệnh bên ngoài lớp, hay chỉ cho phép các lớp có quan hệ đặc biệt như là quan hệ lớp con, (và quan hệ bạn bè (friend) trong C++) được phép dùng tới nó (hay thay đổi giá trị của nó). Mỗi thuộc tính của một lớp còn được gọi là thành viên dữ liệu của lớp đó.
Thực thể (instance)
Thực thể hóa (instantiate) là quá trình khai báo để có một tên (có thể được xem như là một biến) trở thành một đối tượng từ một lớp nào đó.
Một lớp sau khi được tiến hành thực thể hóa để có một đối tượng cụ thể gọi là một thực thể. Hay nói ngược lại một thực thể là một đối tượng riêng lẽ của một lớp đã định trước. Như các biến thông thường, hai thực thể của cùng một lớp có thể có trạng thái nội tại khác nhau (xác định bởi các giá trị hiện có của các biến nội tại) và do đó hoàn toàn độc lập nhau nếu không có yêu cầu gì đặc biệt từ người lập trình.
Một số tính chất của lập trình hướng đối tượng
- Abstraction (Tính trừu tượng)
- Encapsulation (Tính bao đóng)
- Inheritance (Tính kế thừa)
- Polymophirsm (Tính đa hình)
Abstraction
Đây là khả năng của chương trình bỏ qua hay không chú ý đến một số khía cạnh của thông tin mà nó đang trực tiếp làm việc lên, nghĩa là nó có khả năng tập trung vào những cốt lõi cần thiết. Mỗi đối tượng phục vụ như là một “động tử” có thể hoàn tất các công việc một cách nội bộ, báo cáo, thay đổi trạng thái của nó và liên lạc với các đối tượng khác mà không cần cho biết làm cách nào đối tượng tiến hành được các thao tác. Tính chất này thường được gọi là sự trừu tượng của dữ liệu.
Tính trừu tượng còn thể hiện qua việc một đối tượng ban đầu có thể có một số đặc điểm chung cho nhiều đối tượng khác như là sự mở rộng của nó nhưng bản thân đối tượng ban đầu này có thể không có các biện pháp thi hành. Tính trừu tượng này thường được xác định trong khái niệm gọi là lớp trừu tượng hay hay lớp cơ sở trừu tượng.
Encapsulation
There are two important aspects of encapsulation:
- Access restriction - preventing one object from accessing another's internal state, for example.
- Namespaces/scopes - allowing the same name to have different meanings in different contexts.
Encapsulation mechanisms are essential for reducing couplings between software components. Many encapsulation mechanisms originated with non-object-oriented languages. Object-oriented languages add additional encapsulation mechanisms.
Inheritance
There are two types of inheritance in OOLs
- Interface inheritance
- Implementation inheritance.
Interface inheritance is only necessary in typed OOLs. This is best understood when considering delegation-based design patterns.
Polymorphism
Polymorphism refers to the ability of different objects to respond to the same message in different ways. Polymorphism is essential for modeling our world including our social environment. We frequently use the same phrases and sentences to mean different things in different contexts. Often there is an abstract sameness, but concrete differences.
For example, say the following sentence to two different architects and you will likely get two houses: "Build me a house". Abstractly, both architects will do the same thing but many of the details will differ.
In early structured design methodology there were three principle types of control or data structure elements: sequence, alteration, and repetition. Polymorphism is often used as an alternative to alternation.
Polymorphism is implemented with a dispatch mechanism. This mechanism may only be dependent on the object that receives a message or it may also be dependent on message parameters.
9) Principles of Software Engineering
Separation of Concerns
Modularity
Abstraction
Anticipation of Change
Generality
Incremental Development
Consistency
10) POCOs
POCOs: Plain Old CLR Objects (POCOs)
https://msdn.microsoft.com/en-us/library/dd456853(v=vs.110).aspx
POCOs là gì?
- Nguyên tắc 1: Chỉ dùng CLR
- Nguyên tắc 2: Persistence Ignorance
https://yinyangit.wordpress.com/2013/03/08/net-poco-la-gi/
+ Một POCO có thể sử dụng các POCO khác miễn là nó cùng phạm vi.
+ Các POCO sẽ không biết, không liên hệ, không có bất kì phương thức nào cho phép chúng truy xuất đến nguồn dữ liệu. POCO chỉ có nhiệm vụ chứa và xử lý dữ liệu.
+ POCO ko nên có bất kì nhiệm vụ nào khác ngoài việc lưu trữ và xử lý dữ liệu (trên bộ nhớ).
+ Khi sử dụng POCO, nên tạo một tầng Repository (pattern) để thao tác với nguồn dữ liệu.
+ Việc giữ cho các POCO đơn giản và độc lập khiến chúng trở nên linh hoạt và dễ dàng thay đổi.
+ Do đó, bạn có thể coi các POCO là một phần độc lập và hoàn toàn không phụ thuộc vào bất kì phần nào khác của dự án.
POCOs (Plain old CLR objects) are simply entities of your Domain. Normally when we use entity framework the entities are generated automatically for you. This is great but unfortunately these entities are interspersed with database access functionality which is clearly against the SOC (Separation of concern). POCOs are simple entities without any data access functionality but still gives the capabilities all EntityObject functionalities like
- Lazy loading
- Change tracking
Here is a good start for this
You can also generate POCOs so easily from your existing Entity framework project using Code generators.
POCO is simply a regular object that has no references to any specific framework and does not follow their interfaces or restrictions. POCO classes are persistence ignorant objects that can be used with any ORM.
Entity is an object which has an identity and can be uniquely determined.
Entities represent domain model and domain logic. Usually they are designed as persistence ignorant POCO objects. But not every POCO object is an Entity. Value Objects are also designed as POCO objects and they are not Entities.