Object Oriented Design Principles 2

A good object-oriented design needs to follow some basic principles, such as Single Responsibility Principle (SRP), Open-Closed Principle (OCP), Liskov Substitution Principle (LSP), Dependency Inversion Principle (DIP), Interface Separation Principle (ISP), etc.

1. Single Responsibility Principle (SRP) 

Description: For a class, there should be only one reason for it to change. 
Application: When constructing an object, separate the different responsibilities of the object into two or more classes to ensure that there is only one reason for the class to change. 
Benefits: Improve cohesion and reduce coupling. 
Personal opinion: This principle can effectively reduce coupling and reduce references to unnecessary resources. However, the consequence is the increase of source files, which brings inconvenience to management. Therefore, in practical applications, this principle can be applied to modules that are frequently used or frequently need to be changed.

2. Open-Closed Principle (OCP) 

Description: "Open for extension". This means that the behavior of modules is extensible. When the needs of the application change, modules can be extended to have new behaviors that meet the changes. That is, we can change the functionality of the module. "Close for modification". When extending the behavior of a module, it is not necessary to change the source code or binary code of the module. 
Application: Interfaces and virtual classes in high-level languages. 
Benefits: Increased flexibility, reusability, maintainability. 
Personal opinion: The key to OCP is abstraction. The purpose of abstraction is to create a fixed base class that can describe an arbitrary set of possible behaviors. This set of possible behaviors is represented by a derived class. Changes to the base class are closed, so the methods in it cannot be changed once they are determined (changes to methods in the interface will have disastrous consequences). Modules are referenced through abstract base classes, and extensions to derived classes do not affect the entire module, so it is open. Following the OCP is expensive, creating the correct abstraction takes development time and effort, and abstraction adds complexity to software design. So effectively predicting change is the point of OCP design, which requires us to do proper investigation, ask the right questions, and use our experience and common sense to make judgments. The right thing to do is to abstract only the frequently changing parts of the program and reject premature abstractions as important as the abstractions themselves.

3. Liskov Substitution Principle (LSP) 

description: If for every object O1 of type S, there is an object O2 of type T, so that in all programs P written for T, after O1 is used to replace O2, the behavior of program P is functional unchanged, then S is a subtype of T. 
Application: When implementing inheritance, subtypes must be able to replace their base types. If a software entity uses a base class, it must also apply to subclasses. But the reverse substitution does not hold. 
Personal opinion: LSP is one of the main principles that make OCP possible. Violation of LSP will lead to violation of OCP. At the same time, the two are the theoretical basis of abstraction and polymorphism in OOD, which is manifested as inheritance in OOPL. In high-level languages ​​(JAVA, C#), this principle can be well followed as long as we strictly follow the syntax specifications of interfaces and virtual classes, and we should also avoid some more subtle violations. For example, square and rectangle, rectangle can be used as the base class of square, because square is also a kind of rectangle, but for square, setWidth() and setHeight() are redundant and easy to cause errors, such a design It violates the LSP principle. If there is a relationship between two concrete classes A and B that violates LSP, you can choose one of the following two refactoring schemes: 1. Create a new abstract class C, as the superclass of the two concrete classes, which will The common behavior of A and B is moved into C, thus solving the problem that A and B behaviors are not completely consistent. 2. The inheritance relationship from B to A is rewritten as a delegation relationship.

4. Dependency Inversion Principle (DIP) 

Description: A. High-level modules should not depend on low-level modules. Both should rely on abstraction. B. Abstraction should not depend on details. Details should depend on abstraction. 
Application: rely on abstraction, not on concrete. That is, programming for the interface, not for the implementation. Programming for interfaces means that interfaces and abstract classes should be used for variable type declaration, parameter type declaration, method return type declaration, and data type conversion. The meaning of not programming for implementation means that concrete classes should not be used for variable type declaration, parameter type declaration, method return type declaration, and data type conversion. 
Conclusion: Although DIP is powerful, it is not easy to implement. Because of dependency inversion, object creation is likely to use object factories to avoid direct references to specific classes. The use of this principle will lead to a large number of class files. Bring unnecessary trouble to maintenance. So, the right thing to do is to do dependency inversion only for the frequently changing parts of the program.

5. Interface Segregation Principle (ISP) 

Description: Don't force clients to rely on methods they don't use. 
Application: A class's dependency on another class should be based on the smallest interface. If the client only needs certain methods, then it should provide the required methods to the client, not the ones that are not needed. Providing an interface means making a promise to the client, and too many promises can unnecessarily burden the maintenance of the system. 
Conclusion: Using multiple specialized interfaces is better than using a single interface.

6. Summary

Following the above principles can make our software more flexible and robust. But flexibility comes at a price, and the performance penalty caused by polymorphism is the most obvious problem. So we need to make tradeoffs, we need to make choices, between flexibility and performance.

追本溯源,促使我们使用这些原则的原因是为了满足需求的变更,于是需求分析就显得格外重要。然而不管怎么充分的需求分析都可能遭遇需求变更,于是预测变化就成了一个让人头痛的事。还是让我们来看看敏捷设计(XP)是怎么解决这些问题的:"敏捷开发人员不会对一个庞大的预先设计应用那些原则和模式,相反,这些原则和模式被应用在一次次的迭代中,力图使代码以及代码所表达的设计保持干净。"也就是说敏捷设计通过快速的迭代来刺激变化,让这些变化及早暴露,再根据变化进行相应改动。很明显这要比一次性完整设计轻松容易的多。

软件开发的全部艺术就是权衡:在简单与复杂之间权衡,在一种方案与另一种方案之间权衡。如果把每个问题、每个权衡的利弊都考虑得清清楚楚,恐怕开发一个应用程序的成本会高得惊人。所以,很多时候我们更依赖自己的审美眼光,用平静的心去设计一个赏心悦目的系统。 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324845402&siteId=291194637