Summary: Three effective ways to improve code quality: domain modeling, design principles, and design patterns.

The root cause of bad code

The embodiment of bad code

We can list the symptoms of poor quality code: names that don’t know what they mean, oversized classes, oversized methods, duplicate code, difficult code, difficult code to change… Two of the most influential manifestations of code quality are incorrect naming and poor logic expansibility. When a new person reads the code, he sometimes finds that the method naming is not consistent with the actual logic, which makes people feel very confused. This phenomenon is not uncommon in daily work. Another is poor logic expansibility. After a new business requirement is put forward, it is found that there are many business logic to be changed in multiple places, resulting in low r&d efficiency.

Problems of induction

Summarize the phenomena mentioned above, roughly sort out 6 types of problems, and expand to explain them respectively.

  • Naming problems: Naming problems can be a headache, and it is not easy to come up with a name that is worthy of the name and easy to understand. When it comes to variable naming, method naming and class naming, there are two common naming problems: one is unintelligible; The other is a misnomer. When one first looks at it, one does not know what it means. The root cause is that one has not thought of a suitable word to abstract the problem. Misnomenclature is the meaning of naming and actual logic to express is not the same, such naming will be misleading.
  • Code structure problem: when a person look at the projects at the beginning of the code, when not in-depth look at code logic, the module division, class, the method divided the whole can be divided into feelings come to code quality, if a class has several thousand lines of code, a method for hundreds of lines, such logic believe that not many people like to go to the, complexity is high. Good code hierarchies are clear and enjoyable, like reading a good book.
  • Programming paradigm Issues: There are three programming paradigms: Table mode, transaction script mode and domain design mode, the most commonly used is transaction script mode, which is the most consistent with the way people do things, step by step. The biggest problem of this mode is that it takes on responsibilities that should not be undertaken by itself. It seems logical, but in fact there are many problems. People like to call it “spaghetti code”.
  • Readability problem: in addition to the implementation of business functions, the code should also have good readability, some code without any comments; Some code format is not uniform; Some are show-offs, with long sections of Lambda expressions (not that Lambda expressions are bad, but the point is to control the level of depth). This code looks concise and not very readable.
  • Extensibility problem: extensibility problem is a platial problem, to achieve good extensibility is not so easy, generally there is no abstract problem, such as the shop in the shop to display Tab, noodle type code is directly define a List, and then add Tab objects, if you need to add a Tab? Typically it does not satisfy the open close principle.
  • No design issues: The whole code looks flat and no one can learn from it. Generally, this kind of problem is solved without in-depth analysis of the problem, without considering how to solve the problem better. For example, can repetitive processing work be abstracted into a common template class, can different processing classes obtain specific policies through factory classes, can asynchronous processing be processed using event patterns, can new capabilities be discovered through automatic registration…

Returning for analysis

Let’s look at the reasons why the code is bad. There are both external and internal reasons for this problem.

  • The main external reasons are: the project schedule is urgent, there is not much time to design; Shortage of resources, lack of manpower, can only how fast how to; Emergency problem repair, temporary solution quick handling…… .
  • The internal reasons are mainly as follows: my own skills are low and I have not mastered them, such as Lamda expression, common tool classes, advanced use of framework, etc. No extreme pursuit of the spirit, just to meet the needs of the line, stability, scalability, performance, data consistency, no consideration……

The author thinks that the most critical is the internal problems, the root causes are two: self-requirements are not high; There is no feedback channel. It’s hard to write high-quality code if you don’t push yourself too hard, and it’s hard to improve your skills without external feedback. The previous supervisor of the author was very strict and carefully reviewed the code written by everyone. The name of a variable and the writing method of a logical paragraph were repeatedly revised, which was actually the fastest way to improve skills.

Ways to improve code quality

The author likes to use three methods to improve code quality: domain modeling, design principles, and design patterns.

  • Analysis phase: When you get a requirement, don’t worry about implementing it. It’s easy to get stuck in transactional scripting mode. Analyze what? You need to analyze what the purpose of the requirement is and what entities are required to perform the function. The core of this step is to find the entity. As an example of the in-store Tab shown above, it has two key entities: the navigation bar and the Tab, where the navigation bar contains several tabs.
  • Design stage: after analyzing which entities there are, then analyze how to assign responsibilities to specific entities, which requires the use of some design principles to guide. There are some principles of responsibility allocation mentioned in GRASP for interested students to see in detail. Back to the above example, the Tab has two main responsibilities: one is whether the Tab can display, which is its own responsibility, as the logic of the new Tab display is that the store will have a new product in 30 days; The other responsibility, which it is also responsible for, is building Tab specs. The navigation bar has two responsibilities: one is to accept Tab registration; The other is presentation. Responsibility allocation is not reasonable, it does not meet the characteristics of high cohesion, low coupling.
  • Polish phase: This phase chooses the appropriate pattern to implement, everyone will understand what it does when they see the pattern, for example, when they see the template class, they will know that the general business process is handled, and the specific changes are handled in the subclass. In the example above, two design patterns are used: one is the subscriber pattern, the Tab automatic registration process; The other is the template pattern, first determine whether the Tab can display, and then build Tab specifications, although the process is simple, can also be abstracted out of the general process, subclass only to simply rewrite 2 methods.

The role of domain models

Domain modeling has a high threshold of entry and contains some difficult concepts. This article will not cover how to model (in private), but I have found that it is much more important to get people to accept domain modeling than to know how to do it, and once you know how domain modeling works, you will find ways to learn it. The following through the author’s experience of some practical cases to elaborate, so that we do not feel so empty.

Simplify the understanding

One year later, I joined a financial company. At that time, I knew nothing about finance. When I first came into contact with terms such as subject matter, creditor’s rights, creditor’s rights transfer, financing guarantee and non-financing guarantee, I felt at a loss what to do and had to learn a lot of new content every day.

Two months later, my supervisor gave us a presentation with a PPT, which included the entities in the field and the relationship between the entities. I immediately knew how the whole business worked. The role of models is to simplify people’s understanding of things. If we get bogged down in the details of the code at the beginning, it’s hard to see the whole business, and the code is there to implement the business capabilities, and it’s much faster to look at the code once you know the business.

Unified understanding

In the company, there is r&d, product, operations, testing… When we communicate with each other, the default language is not unified. Developers often talk about how to operate the database table, and products often talk about the business model… This leads to the disunity of our understanding.

That night, after confirming the interaction process with the interaction student, she suddenly asked a question: Is it easy to move sellers to the same folder from similar pages? After listening, I was told that I could not do it. Once I heard that it was very reasonable, how could it not be realized? Began to tell her the process under the existing system, found out that she heard a face of meng, I immediately realized that I am in describing the problem in development language, immediately, in a way, looking for a pen and a piece of paper, give interaction students painting, what is our domain model, the interaction between business entities is how, after finished, Interaction students immediately understand why this is not possible.

To guide the design

Some classmates think domain modeling slant hole, is virtual, in fact, in addition to being able to simplify the understanding and the unified understanding, domain modeling may also guide the code design, such as shops navigation Tab of the example above, the author is through the domain modeling design, although it is a small demand, does not prevent the use of the domain modeling.

In the figure below, you can clearly see that the navigation bar contains several tabs, one of which contains specification information and click action information. After this business model is drawn, the corresponding code will also have the above concept, there is a mapping between reality and code, model is code, code is model. If your model can’t reflect the reality, the module can only be regarded as a vanity. Professor Fan Gang summed up three sentences: What is the reality, what should be the object; What is the behavior of the real thing, what is the method of the corresponding object; What is the relation of the real thing, what is the relation of the corresponding object.

The underlying logic of design principles

SOLID

For design principles, we usually talk about SOLID, which contains five design principles:

  • Single responsibility principle: A class should have one, and only one, reason to change, A class can only be modified for one reason.
  • Entities should be open for extension, but closed for modification.
  • Richter’s substitution principle: Functions that use pointers of references to base classes must be able to use objects of derived classes without knowing It, subclasses can replace parent classes.
  • Interface isolation principle: A client should not be forced to implement an interface that it doesn’t use, and should detach the interface as small as possible.
  • Abstract actions should not depend on details. Abstract actions should depend on details. Abstract actions should depend on details.

Why design principles

We’ve basically heard and understood the SOLID principles, but why these design principles? To answer this question, let’s go down the target. The goal of software development is high cohesion, low coupling, which is often said to be difficult to measure, for example: what is high cohesion? What is low coupling? How high is high cohesion? How low does low coupling go? These four questions are not easy to answer.

Conversely, what if our code were not highly cohesive and poorly coupled? That is, low cohesion and high coupling scenarios. If the code is low cohesion and high coupling, changes will appear a logic, can lead to much code to be modified, and this is not we want to see, modify the original logic, in particular, are prone to bugs, such as the author before modifying a problem, changed the rules of another, seems to be no problem, the result affects a business party, This is why the open closed principle suggests that it is risky to modify logic.

Ideally, the changes are limited to a local scope, so the scope of impact is limited, so we want the logic to be simple and not to contain multiple responsibilities. Think about it further: Why should we change it? In addition to bugs in the original logic that need to be fixed and code refactoring, an important reason is that requirements have changed, and it is the change that causes us to modify the original logic. Without modified scenarios, there is no such thing as high cohesion and low coupling. Therefore, the underlying logic of design principles is to make software better able to cope with change, reduce cost and increase efficiency.

How to practice

The design principle is just a guideline, and there is still a long way to go before it is put into practice. Just like some students said that I understand the design principle, but I still can’t use it. In fact, the essence of this problem is not to understand the underlying logic of design principles, no insight into changing concerns, how to solve this problem? Design patterns provide the answer: find change, encapsulate change.

The nature of design patterns

Case study

When the call interface has different implementations (input parameter, output parameter, interface are not the same), need to abstract out a layer of corrosion, how to implement? Next, we will look at two cases respectively. The emphasis of these two cases is different, one is the abstraction of partial behavior, the other is the abstraction of partial structure.

Store brand inquiry

Stores need to query store brand information, however, Lazada and AE interface is not the same, how to abstract the anti-corrosion layer?

The simplest solution is to define one interface and then have two implementations. Its advantage is that the hierarchy is simple, we basically read to understand. Its disadvantages are also obvious, in the two implementation classes, the responsibility is not a single, undertake two responsibilities: one is to achieve store brand query, the other is data conversion.

Given the shortcomings mentioned in scenario 1, it is easy to think of using the adapter pattern to split the previous class into two classes: one that invokes the corresponding branded service; The other class does data adaptation conversions. However, the downside of this approach is that in an international scenario where isolation between multi-tenants is considered, for example Lazada has multiple sites, how can finer grained differences be achieved? Scheme three is based on these considerations.

Solution 3 introduces the multi-tenant framework to support multi-tenant scenarios.

Store coupon Query

There is a “one-size-fits-all” development pattern: assemble parameters, call interfaces, parse response results, and you will find it too versatile for all scenarios. This development pattern is called “transaction scripting” or “spaghetti code”.

The coupon query case, using the domain modeling model, first think about what entities there are. The essence of coupon query: by xx conditional query to return a set of coupons that meet the conditions. There are two types of information that are critical when it comes to coupons. One is the specification information of the coupon, such as the name of the coupon, discount amount, validity period, etc. Another is the restrictions on coupons. When querying, check store coupons, or check fan coupons, or check commodity coupons… . Therefore, the abstract coupon is divided into two parts: one is the coupon query request; The other is the coupon specification entity.

One disadvantage of this design is that the business side understands that complexity increases, that it is a low-level implementation and not easy to use. Coupons skewed towards product delivery rather than just feature delivery. Therefore, on top of the underlying implementation, the product components are abstracted so that the business side can use them more easily.

The original link

This article is the original content of Aliyun and shall not be reproduced without permission.