Regain object-oriented software design


Introduction: it has been only 70 years since von Neumann created the first computer in the 1950s. From the first computer language FORTRAN to the commonly used C + +, Java and python, the evolution speed of computer language is much faster than any natural language we use. From the earliest machine oriented, then process oriented, to the evolution of object-oriented we use now. What remains unchanged is the purpose of programming, and what changes is the idea of programming.

Regain object-oriented software design

Author Nie Xiaolong
Source: Ali technical official account

Are you still writing process oriented code in object-oriented language?

I. Preface

During the Renaissance, a great mathematician astronomer Copernicus proposed the heliocentric theory at the time, refuting the celestial body thought of the earth as the center of the universe. Because of the extremely advanced thought, only after half a century later, Galileo Kepler and others had gradually recognized and established the advanced nature of Copernicus thought.

Coincidentally, the same story is happening in the field of software engineering. Half a century ago, Kristen Nygaard invented the simula language, which is now recognized as the first language in the world to explicitly realize object-oriented programming. He proposed the class based programming style and determined the “ultimate idea” of the object-oriented theory of “everything is an object”, but it was also not recognized at that time. Peter Norvig refuted this in design patterns in dynamic programming and stated that we do not need any object-oriented. Half a century later, Robert C. Martin, Bertrand Meyer, Martin Fowler and others once again confirmed and sublimated the object-oriented design concept. The evolution of programming thought is not achieved overnight, but it has developed rapidly in this century.

II. Evolution of programming ideas

It is only 70 years since von Neumann created the first computer in the 1950s. From the first computer language FORTRAN to the commonly used C + +, Java and python, the evolution speed of computer language is much faster than any natural language we use. From the earliest machine oriented, then process oriented, to the evolution of object-oriented we use now. What remains unchanged is the purpose of programming, and what changes is the idea of programming.

1 face the machine

Regain object-oriented software design

The computer is the world of 01. The earliest program controlled the computer through this 01 machine code, such as 0000 for reading, 0001 for saving, etc. In theory, this is the fastest language in the world. It runs directly without translation. But the drawback is also obvious, that is, it is almost impossible to maintain. Run for 5 milliseconds and program for 3 hours. Because the machine code cannot be maintained, people invented the assembly language on this basis. Read stands for 0000 and save stands for 0001, which is easier to understand and maintain. Although assembly is more visual and intuitive in machine code, it is essentially a machine oriented language, and there is still a high programming cost.

2 process oriented

Regain object-oriented software design

Process oriented programming is an event centered programming idea, which is a great progress compared with machine oriented programming. Instead of focusing on machine instructions, we focus on specific problems. It divides a thing into several execution steps, then realizes each link through functions, and finally connects them to complete the software design.

The process design makes the coding clearer. Compared with machine code or assembly, the development efficiency has been greatly improved, including there are still many scenarios that are more suitable for process oriented completion. However, the biggest cost of software engineering lies in maintenance. Because process oriented focuses more on problem solving than domain design, the disadvantages of code reusability and scalability are gradually revealed. With the increasing complexity of business logic, the complexity of software becomes more and more uncontrollable.

3 object oriented

Regain object-oriented software design

Object-oriented thinking and solving problems in the way of classification. The core of object-oriented is abstract thinking. It abstracts commonness, encapsulates convergence logic, and realizes extension through polymorphism. The essence of object-oriented thinking is to combine data and behavior. The carrier of data and behavior is called object, and object is responsible for defining the boundary of responsibility. Process oriented is simple and fast. When dealing with simple business systems, the effect of object-oriented is not as good as process oriented. However, in the design of complex systems, universal business processes, personalized differences, atomized functional components and so on are more suitable for object-oriented programming mode.

But object-oriented is not a silver bullet, and even some scenes are worse than not. The root of everything is abstraction. If else is the most rigorous classification of software engineering. When we design abstractions for classification, we may not be able to grasp the most appropriate entry point. The complexity of wrong abstractions is higher than that of no abstractions. Barbara Liskov, founder of Richter’s substitution principle, talks about the power of abstraction.

III. domain oriented design

Are you really “Object-Oriented”

//Pick up customers to the sales private sea
public String pick(String salesId, String customerId){
    //Verify sales role
    Operator operator = dao.find("db_operator", salesId);
        return "operator not sales";
    //Verify whether the sales storage capacity is full
    int hold = dao.find("sales_hold", salesId);
    List<CustomerVo> customers = dao.find("db_sales_customer", salesId);
    if(customers.size() >= hold){
        return "hold is full";
    //Check whether the customer can pick it up
    Opportunity opp = dao.find("db_opportunity", customerId);
    if(opp.getOwnerId() != null){
        return "can not pick other's customer";
    //Pick up customer
    return "success";

This is the business code of a customer picked up by sales in the CRM field. This is the familiar java object-oriented language, but is this a piece of object-oriented code? Fully event oriented, no encapsulation, no abstraction, difficult to reuse and difficult to expand. I believe there are many such codes in our code base. Why? Because it puts the cost into the future. We call this “doing process oriented activities under the cloak of object-oriented.”

In the early stage of system design, business rules are not complex, logic reuse and expansion are not strongly reflected, and process oriented code is very easy to support these relatively simple business scenarios. But the biggest cost of software engineering lies in maintenance. When the system is complex enough, the most easy code written at the beginning will be the hardest debt to maintain in the future.

2 Domain Driven Design

Regain object-oriented software design

Another way is to add a “business opportunity” model to associate the relationship between customers and sales through business opportunities. The attribution of business opportunities is also divided into specific attribution scenarios such as the high seas and private seas. In addition to the necessary data, business opportunities should also collect some business behaviors, such as picking up, opening, distribution, etc. Through domain modeling, using the characteristics of object-oriented, determine the boundary, abstract encapsulation, behavior collapse, and divide and rule the business.

When we say “business opportunities are distributed to private sea” in business, our code is “opportunity. Pickto (privatesea)”. This is the change brought by domain driven. Domain oriented design, object-oriented programming, and the abstraction of domain model is the description of the real world. But this is not an overnight process. When you only touch the elephant’s body, you think it is a door. When you touch the elephant’s ear, you think it is a banana. Only by constantly abstracting and reconstructing can we get closer to the real model of the business.

Use the model as the backbone of a language, Recognize that a change in the language is a change to the model.Then refactor the code, renaming classes, methods, and modules to conform to the new model
— Eric Evans 《Domain-Driven Design Reference》

Use the model as the pillar of the language, realize that the change of language is the change of the model, and then refactor the code, rename classes, methods and modules to conform to the new model.

3 software complexity

Regain object-oriented software design

This is Martin Fowler’s view on complexity in the book patterns of enterprise application architecture. He divides software development into data-driven and domain driven. Most of the time, people tend to look at how to design the table after getting the requirements, and then look at how to write the code. In fact, this is also a manifestation of process oriented. In the early stage of software, the complexity of this method is very low. There is no reuse and expansion. One person is full and the whole family is not hungry. However, with the development of business and the evolution of the system, the complexity will increase sharply.

At the beginning, through domain modeling and object-oriented thinking, the rise of complexity can be well controlled. First consider the design of our domain model, which is the core of our business system, and then gradually extend it to the interface, cache and database. However, the cost of domain boundary and model abstraction is higher than that of data-driven from the beginning.

The goal of software architecture is to minimize the human resources required to build and maintain the required system.
— Robert C. Martin 《Clean Architecture》
The ultimate goal of software architecture is to meet the needs of building and maintaining the system with the minimum labor cost

If we directly use data-driven process oriented process code at the beginning, we can easily solve the problem, and will not face more complex scenarios and businesses later, this model is the most suitable architecture design for this system. If our system will become more and more complex with the development of business, and each release will increase the cost of the next release, we should consider investing the necessary cost in domain driven design.

Four abstract qualities

Abstract is always the most difficult proposition in the field of software engineering, because it has no rules, no standards, or even right or wrong, only good or bad, only suitable or not. Similarly, a domain abstraction of Taobao commodity model can be regarded as an industry benchmark, but it is not suitable for your system. How do we manage abstraction? In the book object oriented analysis and design with applications, Grady Booch, the founder of UML, mentioned that the quality of an abstraction can be measured by the following five indicators: coupling, cohesion, sufficiency, integrity and foundation.

1 coupling

The measurement of the correlation strength established between one module and another is called coupling. If a module is highly related to other modules, it is difficult to understand, change or modify independently. Professor John ousterhout, the inventor of Tcl language, has the same view. We should reduce the coupling dependency between modules as much as possible, so as to reduce the complexity.

Complexity is caused by two things: dependencies and obscurity.
— John Ousterhout 《A Philosophy of Software Design》
Complexity is caused by two things: dependency and fuzziness.

But that doesn’t mean we don’t need coupling. Software design is developing towards expansibility and reusability. Inheritance is naturally strong coupling, but it provides us with the reusability of software system. Like friction, at first we thought it hindered our progress. In fact, without friction, we couldn’t move a step.

2 cohesion

Cohesion and coupling are both concepts in structured design. Cohesion measures the connection degree of each element in a single module. High cohesion and low coupling is a viewpoint written in textbooks, but we should not blindly pursue high cohesion anytime and anywhere.

Cohesion is divided into accidental cohesion and functional cohesion. Goldfish and fire hydrants can be abstracted together because they can’t whistle, but obviously we shouldn’t do so. This is accidental cohesion. The most desirable cohesion is functional cohesion, that is, the elements of a class or pattern work together to provide some clearly defined behavior. For example, I gather fire hydrants, fire extinguishers and detectors together. They all belong to fire-fighting facilities, which is functional cohesion.

3 adequacy

Sufficiency means that a class or module needs to record enough features of an abstraction, otherwise the component will become unused. For example, set collection class, if we only have remove and get but no add, this class must be useless because it does not form a closed loop. However, this situation is relatively rare. As long as we really use it and complete a series of process operations, some missing contents are easier to find and solve.

4 integrity

Integrity means that a class or module needs to record all meaningful features of an abstraction. Integrity is relative to sufficiency. Sufficiency is the minimum connotation of the module, and integrity is the maximum extension of the module. After we go through a process, we can clearly know what we lack, and let us immediately supplement the sufficiency of abstraction, but these features may not be enough in another scenario. We need to consider what features the module needs to have or what capabilities it should supplement.

5 basic

Sufficiency, integrity and foundation can be said to be three mutually complementary and restrictive principles. Basic refers to the most effective basic operation of the abstract underlying expression (it seems to explain itself by itself). For example, the add operation in set is a basic operation. When add already exists, do we need to add an add2 operation of two elements at one time? Obviously, we don’t need it, because we can call add twice, so add2 doesn’t meet the basic requirements.

But let’s imagine another scenario. If we want to judge whether an element is in the set set, do we need to add a contains method. Set already has foreach, get and other operations. According to the basic theory, we can also traverse all elements and see whether the element is included. However, there is a keyword called “effective” in basic operations. Although we can combine some basic operations, it will consume a lot of resources or complexity, so it can also be used as a candidate for basic operations.

V. software design principles

The quality of abstraction can guide us to abstract and model, but it is still not concrete enough. On this basis, some more landing and easier to implement design principles have emerged. The most famous is the five object-oriented design principles s.o.l.i.d.

1 opening and closing Principle OCP

Software entities should be open for extension,but closed for modification
— Bertrand Meyer 《Object Oriented Software Construction》
Software entities should be open to extensions and closed to modifications.

The opening and closing principle is a viewpoint mentioned by Bertrand Meyer in his book object oriented software construction in 1988. Software entities should be open to expansion and closed to modification.
Let’s take a look at an example of the opening and closing principle. We need to send in the user list and sort it twice. Our code can be written like this.

public List<User> sort(List<User> users, Enum type){
    if(type == AGE){
        //Sort by age
        users = resortListByAge(users);
    }else if(type == NAME){
        //Sort alphabetically by name
        users = resortListByName(users);
    }else if(type == NAME){
        //Sort by customer health score
        users = resortListByHealth(users);
    return users;

The above code is an example that obviously violates the opening and closing principle. When we need to add a similar one, we need to modify the main process. Since these methods are defined in private functions, we need to modify this code file even if we adjust the existing logic.

There is another way to open extensions and close modifications. JDK sorting has actually defined such a standard for us. We abstract different sorting methods. Each logic is implemented separately. A single adjustment logic does not affect other contents, and the new sorting method does not need to adjust the existing modules.

Regain object-oriented software design

2 dependent inverted dip

High level modules shouldnot depend upon low level modules.Both should depend upon abstractions. Abstractions should not depend upon details. Details should depend upon abstractions
— Robert C.Martin C++ Report 1996
High level modules should not rely on low-level modules, both should rely on abstraction; Abstract should not rely on details, details should rely on abstraction.

Robert C. Martin is the author of two classic books, clean code and code architecture. In 1996, he published an article called the dependency inversion principle in C + + report. He believes that the dependencies between modules should be orderly, the high level should not rely on the low level, the low level should rely on the high level, the abstraction should not rely on details, and the details should rely on abstraction.

Regain object-oriented software design

How to understand Robert C. Martin’s view. Let’s look at this picture. Our hands can hold the cup. Are we dependent on the cup? Some people say that we need to adjust the hold service provided by the cup before we can hold it, so we rely on the cup. But let’s think about it again. Can we hold sticks and kettles, but cats and dogs can’t. why? Because our cup is designed according to our hand shape, we define a holdable interface, and the cup depends on our needs. So the cup depends on us, not us.

Regain object-oriented software design

The principle of reliance inversion is not a newly created theory. It is used in many places of our life. For example, a company needs to set up a “legal person”. If there is something wrong with the company, the regulatory authority will find the company legal person. It is not that the regulatory authority relies on the legal person position provided by the company, it can find people, but that the company relies on the requirements of the regulatory authority to establish the legal person position. This is also a manifestation of dependency inversion.

3. Other design principles

S.o.l.i.d is not listed here one by one. You can refer to what you want to know by yourself. In addition to solid, there are other design principles, which are also very excellent.

Ploa minimum surprise principle

If a necessary feature has a high astonishment factor, it may be necessary to redesign the feature
— Michael F. Cowlishaw

If the necessary feature has a high surprise factor, it may need to be redesigned.

Ploa minimum surprise principle was proposed by Michael f. Cowlishaw, a computer professor at Stanford. No matter how good your code is, if most people are surprised by it, maybe we should redesign it. There is a violation of ploa principle in JDK. Let’s look at the following code.

Regain object-oriented software design

At the sharing meeting, I deliberately covered up this line of comments, and everyone couldn’t guess the newformatter The function of getClass () is written here. If you want to check the null pointer, you can use the methods provided by the objects tool class. The implementation is exactly the same, but the meaning of the code is very different.

Regain object-oriented software design

Kiss simple principle

Keep it Simple and Stupid
— Robert S. Kaplan
Keep stupid, keep simple

KISS principle is a theory put forward by Robert S. Kaplan. Kaplan is not a software scientist. He is the founder of Balanced Scorecard, and his theory is still applicable to the software industry. It’s very simple to make things complicated, and it’s very complicated to make things simple. We need to make complex problems as concise and simple as possible.

Six at the end

The biggest goal of software design is to reduce complexity. Everything is not for me, but everything is for me. End with a quote from Josh Bloch, founder of the JDK collection framework. To learn the art of programming, you must first learn the basic rules, and then you can know when to break them.

You should not slavishly follow these rules, but violate them only occasionally and with good reason. Learning the art of programming, like most other disciplines, consists of first learning the rules and then learning when to break them.
— Josh Bloch 《Effective Java》
You should not blindly follow these rules. You should break them only occasionally and for good reasons

To learn the art of programming, you must first learn the basic rules, and then you can know when to break them

Original link
This article is the original content of Alibaba cloud and cannot be reproduced without permission.