Five principles of c++ Object oriented Design

  • 2020-11-03 22:33:02
  • OfStack

Object oriented design (OOD) is an essential part of object oriented programming (OOP). Only good design can guarantee the quality of the program. The main task of object oriented design is class design. Many pioneers and predecessors of Object oriented design (OO) have put forward many design principles about classes to guide OOP, including five basic principles of class design.

1. Single 1 Responsibility Principle (Single Resposibility Principle, SRP)

Focus is a good quality for a person, just as single responsibility is a good design for a class. The core idea of single responsibility: A class does only one thing.

The single-1 responsibility principle can be seen as an extension of the object-oriented principle of high cohesion and low coupling. Too many responsibilities of classes tend to lead to responsibility dependence between classes, improve coupling degree and reduce cohesion. In general, the single-1 responsibility refers to that the class has only one single-1 function. Do not design too many functions for the class. The mixed functions will make the code messy, improve the difficulty of program development and the probability of system error, and reduce the maintainability of the system.

One of the most common examples of the single-1 responsibility principle is undoubtedly the design of iterators in STL. Some people feel that the separation of the container from the iterator is a bad design, adding complexity and making it cleaner than simply putting the iterator in the container. A lot of people don't think so, though, because more classes don't necessarily mean more complexity, and an iterator in a container exposes some of the inner structure of the container, which doesn't quite fit the idea of encapsulation. There is also the issue of scalability, because there are multiple requirements for accessing the container, if you isolate the iterator you can just define some special iterator without modifying the container class, so that whatever the strange requirement is you just write the corresponding iterator.

2. Open and closed principle (Open Closed Principle, OCP)

The principle of open and closed refers to the principle of open and closed, that is, open to expansion and closed to modification.

The so-called modification closure, is the previously designed classes, do not change. Such as deleting a member function, changing the parameter list of a member function, or changing the data member type. The key to achieving closure is abstraction. The abstraction of an object is essentially the generalization, induction and summary of an object, and its essential characteristics are abstractly represented by a class, so that the class is relatively stable and does not need to be changed.

Extended openness means that you can add a lot of functionality without changing existing classes. This is usually done through inheritance and polymorphism, so that the superclass is left as it is by adding some new functionality to the subclass.

"Requirements are always changing," and if open and closed principles are followed, proper design can seal changes, allowing the class to flexibly extend the required functionality.

3. The Richter Principle of Substitution (Liskov Substituion Principle, LSP)

The Liskov substitution principle means that a subclass can replace a parent and appear wherever the parent can. This principle was proposed by Liskov in 1987 and it can also be derived from the concept of DBC (Design by Contract, designed by contract) of Bertrand Meyer.

C++ language mechanism builds the class abstraction and polymorphism on the basis of inheritance, and its implementation method is interface oriented programming: by extracting pure virtual class (Abstract Class), the common part is abstracted as the base class interface or the subclass overrides the base class method to achieve the purpose of polymorphism. The purpose of the Liskov substitution principle is to ensure the reliability of inheritance reuse.

Here's a special example of a violation of the substitution principle:
Squares and rectangles also belong to the category of "circles are not ellipses". We know that a square is a special rectangle, so we can design two classes. The square class inherits from the rectangle class. The rectangle class has two member variables, representing length and width respectively, and a member function that computes the area. If the method for calculating the area is virtual, polymorphic is achieved. The method that calculates the area is called after setting the length and width first. We know that a square has the same length and width, so if we set the length and width to be different than one, and then we call the square area formula, then we must be wrong. You may wonder why you made the length different from the width. A lot of the design ideas and methods are 1 for convenience and 2 for users to make fewer mistakes, which is that no matter how you use it, it doesn't make any mistakes, it makes mistakes at compile time, it makes mistakes at run time. The compiler won't let you know if something is wrong.

So the design of a square class inheriting from a rectangle class is not good (one thing to note is that you violate the Liskov substitution principle not that you just write code that goes wrong, just that the design doesn't make sense. In fact, if you design the code this way, it may run well. If there is no special case, it may be 1:es61EN, but the design is not reasonable, which will lead to some security risks.

4. Dependency inversion principle (Dependecy Inversion Principle, DIP)

The core idea is: dependence on abstraction. Specifically, high-level modules do not depend on low-level modules, and both of them depend on abstractions. The abstract does not depend on the concrete; the concrete depends on the abstract. The principle of dependency inversion is an inversion of the traditional procedural design method and an effective specification for the reuse of high-level modules and their maintainability.

Dependency 1 exists between classes, modules and modules. When there are dependencies between classes, the understanding of dependency inversion principle can be described as follows: dependency means that the specific details are dependent on each other at the beginning, and we change the implementation details into abstract classes to reduce the coupling degree between classes. Then you have the abstract class, and the implementation classes that inherit from it depend on it. How to understand the inverted word? 1 as we are first pay attention to detail, and then according to the details of abstracting some generalization method 1, so generally as a rule is abstract should depend on the details, and now is the other way round, determine an abstract class, the details of implementation of abstracting method as a benchmark, turned into details depend on abstract, or else you'll inherited an abstract class, you don't fully realize it doesn't allow you to instantiate the object.

When there is a close coupling relationship between two modules, the best way is to separate the interface and the implementation: define an abstract interface between the dependencies for the high-level module to call, and the low-level module to implement the interface definition, so as to effectively control the coupling relationship and achieve the design purpose of relying on the abstraction.

To rely on abstraction is not to program the implementation, but to program the interface. Depending on the abstract is a general principle, and sometimes the details are inevitable. We need to choose between the abstract and the concrete according to the specific situation.

5. Principle of interface separation (Interface Segregation Principle, ISP)

The core idea of this principle is to use several small specialized interfaces instead of one large general interface. In particular, interfaces should be cohesive and "fat" interfaces should be avoided. The dependency of one class on another should be based on the smallest interface, rather than being forced to rely on different methods, which is one type of interface contamination.

In a nutshell, this is similar to the single-1 responsibility, where the interface is not a function interface, but a class. C# has a special interface, interface, which distinguishes it from classes, and C# does not support multiple inheritance of classes, as C++ does, only multiple inheritance of interfaces, so here you can understand the interface to be smaller and more specialized classes, one interface may only need so few methods for OK.

Interface separation methods mainly include the following two ways:
(1) Use the delegate to separate the interface;
(2) Use multiple inheritance to separate the interface.

6. Summary

Generally speaking, object-oriented design principles are still the embodiment of object-oriented thinking. For example,
(1) The Single responsibility principle requires the class to be responsible for only one thing. The interface separation principle allows customers to only care about the interfaces they need. The principle of single responsibility and the separation of interface embody the idea of cohesion;
(2) The open and closed principle, which requires the class to extend its functions without modification, reflects the encapsulation and inheritance of the class;
(3) Liskov substitution principle, which requires derived classes to be able to replace base classes, is the specification of class inheritance;
(4) The dependency inversion principle, which requires classes to rely on abstraction rather than implementation, is the embodiment of abstract ideas.

The above five principles of object-oriented design can help us design programs that are easy to reuse, easy to extend, and easy to maintain, and we need to follow them in practice.

The above is c++ five principles of object-oriented design detailed content, more about c++ object-oriented design information please pay attention to other related articles on this site!


Related articles: