Doing something you intrinsically are enthusiastic with.

2017年1月17日 星期二

SOLID Principles of objected programming

晚上11:07 Posted by Unknown No comments

What is SOLID principle ?

SOLID stands for five basic principles of object-oriented programming and design. The intention is that these principles, when applied together, will make it more likely that a programmer will create a system that is easy to maintain and extend over time.The principles of SOLID are guidelines that can be applied while working on software to remove code smells by providing a framework through which the programmer may refactor the software's source code until it is both legible and extensible.

SOLID stands for:

SINGLE-RESPONSIBILITY PRINCIPLE

Definition: A class should have only one reason to change.

擷取.PNG

This principle echoes with the encapsulation concept. The encapsulation concept suggests that the details not necessary for external references should be hidden, those unnecessary methods also shouldn't exist within the class.

In the code above, we can see that the save_page method deals with the file saving of the page.
This violates the single responsibility principle since the Book itself should not operate on this task.
We should let each object focus on what they should do.

In the code below, the save method has been moved to another class called BookData_maniputlation.

擷取2.PNG



OPEN-CLOSED PRINCIPLE

Definition: Objects or entities should be open for extension, but closed for modification. If the OCP is applied well, then further changes are achieved by adding new code, not by changing old code that already works. This can avoid that a single change to a program will result in a cascade of changes to dependent modules.

擷取3.PNG

In the code above we can notice that in method drawShape, the m_type is used to check the objects and determine which part of code should be run. However, it violates the Open-Closed principle, for example, if we want to add one more shape to the code, we inevitably change the code.



So instead, we move the detail to Rectangle class itself. And the drawCircle and drawRectangle
method become useless in the GraphicEditor class. This can help us not to change the logic inside GraphicEditor while adding new shape.


LISKOV SUBSITITUTION PRINCIPLE

Definition: Subtypes must be replaceable for their base types. Let q(x) be a property provable about objects of x of type T. Then q(y) should be provable for objects y of type S where S is a subtype of T.


For example, when implementing the draw method in the subclass of Shape, Circle and Square, the methods both abide by the behavior definition of draw (draw themselves on the canvas). If the subclasses hasn't follow the definition of their base class, the demand of  Liskov substitution principle is not met.

If the program does not meet the demand of LSP, then the behavior of the program is going to be unpredictable. In other words, it's possible that the bugs are unpredictable and unnoticeable.
For instance, if the draw method in Circle does not draw itself on the canvas but save it as a file or output to the printer. There's nothing wrong when programmers simply read the code, but when in the runtime, the behavior of the program won't work as expected.

Let us take a look at the example below to clarify the concept.



In the code above, we have two classes, Square and Rectangle, they seem to work fine. Whatever you do on the Square, it will conform to the definition of Square,so does the Rectangle. Even when you pass a Square to a function that accepts the Rectangle object or pointer, Square still keeps its property and consistency.

Noted that the _width and _height parameter in class Square are nonsense while a Square won't keep different width or height. This inherently violates the Open-closed principle which also means there is a design issue in the base class.

But what's more than that is in the func function, there will be error in the assert function when we pass a square to it. The functions designed in the Rectangle class has violated the Liskov substitution principle.

From the point of view of the behavior, a Square is not a Rectangle. And what we actually care about in software development is the behavior. The Liskov Principle let us know the IS-A in object-oriented design is relevant with the behavior. It's not about the interior, private behaviors but those become public that the users rely on. To meet the demand of both LSP and OCP, all the derived class should conform to the behavior of the base class that user expect.


INTERFACE-SEGREGATION PRINCIPLE

Definition: A client should never be forced to implement an interface that it doesn't use or clients shouldn't be forced to depend on methods they do not use.



In the code above, SomeController which handles clicks from two SomeButton objects and window events from a SomeWindow object. The problem with this design is that SomeButton and SomeWindow both have a SomeController pointer. SomeButton does need to call the onButton[X]methods of the controller object, but it also has access to the onWindow[X] methods which are useless to the button. The presence of useless onWindow[X] methods is a violation of the Interface Segregation Principle. There is also a cyclic dependency.



The improved design above uses abstract base classes and multiple inheritance. SomeButton now only has access to button related controller methods, and SomeWindow only has access to window related controller methods, yet SomeController objects can be plugged into both.


DEPENDENCY INVERSION PRINCIPLE

Definition: The dependency inversion principle refers to a specific form of decoupling software modules. When following this principle, the conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are reversed, thus rendering high-level modules independent of the low-level module implementation details.The principle states:

A. High-level modules should not depend on low-level modules. Both should depend on abstractions. B. Abstractions should not depend on details. Details should depend on abstractions.


Here we have one PC with one HDD. In this example, the HDD depends on the details rather than on the abstractions. If one day I want to add functions to the HDD or replace it with another one, then we must modify the HDD class to achieve it.


In this modified version, if there is a new HDD for update in the future. We can just add a new class called MyHDD or something else rather than modify the original HDD class .

This is the end of this post.
If there is anything wrong, please kindly let me know. I appreciate your help.