What I learned and understand so far...

1. Divide and Conquer

This stay on the top as it's the most important principle, for instances when we are being provided with a business case with huge list of requirements, we started to confuse ourselves and often the solution came up straight away potentially not the best solution. The idea of divide is to decompose a given problem into two or more similar, sub-problems, which then we conquer by composing solutions to the give problem.

2. Increase cohesion

A good object oriented design must be loosely coupled and highly cohesive. This design principle have been created based on the idea of Loose coupling and high cohesion. Don't mixed up with Cohesion and Coupling, as cohesion refers to what a class, module or function can do. Low cohesion would means that it does great variety of action being unfocused on what it should do. Another way to think of this is to have something that could not be broken down further, and should be doing only one thing.

Example of Low Cohesion:

Staff
check_email()
send_email()
email_validate()
get_salary()

Example of High Cohesion:

Staff
-salary
-email_add
set_salary()
get_salary()
set_email_add
get_email_add

3. Reduce coupling

This refers to how related or dependent of two classes / modules. Low coupled would mean that changing something in major should not affect the other. High coupling would increase the difficulties of code maintenance, a change of module usually forces a ripple effect of changes in other modules. The lesser dependencies the better

4. Increase abstract

Abstraction is one of the key principles behind many of the OO design principles such as below and is the hardest in my opinion. The idea is to have a simplified version of something technical and the goal is to reduce complexity.

  • Inheritance
  • Polymorphism
  • Composition
  • Benefits of abstraction
  • Code is easy to understand
  • Manages change and the effect of change
  • Creates cohesive code – it finds common fields
  • Create loose coupling

https://en.wikipedia.org/wiki/Code_Complete

abstraction is the ability to view a complex operation in a simplified form. A class interface provides an abstraction of the implementation that’s hidden behind the interface

Some examples below:

Concrete

place_triangle(triangle)
place_square(square)
place_circle(circle)

Abstract

place_shape(shape)

You will just lock down the attributes like x, y, areas

5. Increase re-usability

Think of writing codes as re-usable as possible, the challenge part could be keeping the balance on spending more time to design and write a general function. I see this as a investment of time to reduce the future time needed to go back and understand the piece of code.

6. Design for flexibility

Think ahead and anticipate that the fact of there will be a change in the future, as and when the business grows, requirement changes and more. A-lot of time when comes to an end of project, I realised that I have to add some features and based on the way I have written my codes, that's not possible. Ending up spending more time to re-write and re-design the whole program.

7. Anticipate Obsolescence

  • Avoid early release and version of software
  • Use software from reputable companies
  • Use as few external dependencies as possible
  • Avoid poorly documented or maintained projects

8. Design for portability / scalability

Think ahead about possibility to have your program in other platform. Things can get harder when your client based grows and you do not have the ability to scale your program or port to another platform.

9. Design for testability

This is important especially for larger code based project. Design the code in a way that you are able to test the code. Think of test case, functional test, unit test, and find out a way to test the code.

10. Design defensively

Idiot proof your code. Good error messages, handling all the invalid input, handling wrong output.