Many programmers don’t know how to organize code, how to improve efficiency, how to improve code maintainability, reusability, scalability, and flexibility. The code they write is a mess, but such a mess of code can actually run normally. .
Does this kind of coding experience seem familiar to you?
Many programmers around me will have such an experience. After a year and a half, when I look at the code I once wrote, it is very strange. I was surprised and thought, was such a bad code really written by me before? I could actually write such a bad code!
And for the code that is still being maintained, at this time, a kind of Refactor the idea, or there may be a better way to implement it. At this point, your love-hate relationship with code has already begun...
This film mainly starts from the six basic principles. As an introduction to the design pattern, it describes the relationship between the six basic principles and the design pattern. The follow-up It will introduce design patterns one by one, detailing design patterns and daily development.
Basic specifications and constraints
For basic specifications and constraints, I believe that every qualified team will have its own set of things. On the one hand, it unifies standards and increases readability and maintainability. On the other hand, it also facilitates the occurrence of bugs after leaving the company, and newcomers can locate and solve the problems faster.
The messy code implements a big function. For latecomers to maintain it, they will undoubtedly send cordial greetings to their ancestors. A good coding habit belongs to the self-cultivation of a qualified programmer. It is beneficial to oneself and others without any harm.
Regarding the specifications and constraints in development, the first thing to say is naming.
In the past five years of work, I have cooperated with all kinds of people. The most I remember is that I developed and maintained five projects at the same time. In my spare time, I also cooperated and interacted with heroes from all walks of life. Learning and making progress together, in this process, the most troublesome thing for me is some irregular naming.
There are many non-standard styles, with all kinds of strange names. I have seen such a list of names. There are two functions, one is called column details and the other is called column messages. The names are "ZhuanLaiDetalActivity" and "ZhuanLaiLiuYanActivity", which makes me confused.
I wonder if you are afraid of such a naming! If you are afraid of such a naming, you really have never seen the world. At least you can get a rough idea of this. Before I have seen some abbreviations of Chinese Pinyin, such as the animal inspection certificate is named djz, and this one seems even more confusing.
Regarding Pinyin naming, I don’t know if I will be criticized if I say something here. I have met some friends who always like Pinyin naming. Hanyu Pinyin is a great initiative of the Chinese nation to promote Chinese culture, but when programming, use Pinyin, I feel really low.
Even with the support of the most powerful technology, the code written is like the work of a primary school student. There is no meaning here to look down on Chinese Pinyin, it is just to express some inner thoughts.
Recommendation: Big hump, small hump or underscore naming is acceptable. If there is no unified standard, you can refer to the "Alibaba Java Development Manual". For friends who have just entered the industry, they should start with naming. It will be of great help for future growth.
Development manual download address: https://pan.baidu.com/s/1mjZxvSW.
Another thing to emphasize is the comments. Many people may think that the more comments, the better. I have seen books advocating adding more comments before, but I think otherwise. Sometimes comments add a lot of burden and misunderstanding to us.
During my last review, I found that when some colleagues copied some of my code, they actually wanted to make another function. They just wanted to copy some code and then make major modifications (I don’t like to reinvent the wheel, so for For some similar functions, it is best to make them more flexible to improve the reusability and flexibility of the code).
In fact, there are very few places that can be reused. I don’t understand why I didn’t write a few lines of code myself. It doesn’t matter. What makes me confused is that they also copied my notes and author. , when I entered that class, I found that the author was me. I went to git to check the historical submissions. It had nothing to do with me, and the functional description was completely irrelevant to this class...
There is one more thing to say about comments. The only thing is some redundant comments. This is called need to be combined with naming. With good naming standards, many unnecessary comments can be omitted, such as login and register, plus login and registration comments, which are completely unnecessary.
Good habits can bring a lot of convenience to our development, but some people like to name them textview1 and textview2. Even if they are commented, when they are used below, they will still be a group of alpacas running. The kind that rushes up and down.
1.for (int i = 0; i
2. // TODO
3. }
for Many people may think that this kind of code is normal, and some people may blame Teacher Tan Haoqiang. Yes, there are indeed many problems in Tan Haoqiang's book, but this is not the reason for writing this kind of code.
In daily development, while maintaining other people's code, I always debug the for statement. Don't you think that such code is terrible and a bit confusing? Even if you add comments, it is still a mess.
Because each team has its own norms and constraints. A large company will have its own set of norms that are unified for each team. Different languages also have different constraints. If I have time in the future, I will write a special one. A blog post with detailed constraints and specifications is presented.
There are so many things I want to write about this area, such as the indiscriminate use of cases, 1, 2, and 3 are always confusing, such as necessary constant substitution variables, such as thread pools replacing threads, such as Use singletons where necessary. There are so many things that I won’t go into details. Today I will explain two key points, naming and annotation.
Recommendation: Use comments rationally. For novices, when learning unfamiliar code and unclear logic, use as many comments as possible to facilitate understanding. For veterans, use as standardized naming as possible.
The effect of comments is achieved through naming, but when the logic is complex or there are too many operating states, necessary comments are still very important to reduce maintenance costs.
Some programming ideas that you should be familiar with
The time a programmer spends writing programs accounts for about 10-20% of his working time. Most programmers can write about 10-12 lines of code that will make it into the final product - no matter how technical the person is.
Good programmers spend 90% of their time thinking, researching and experimenting to find the optimal solution. Bad programmers spend 90% of their time debugging problem programs and blindly modifying programs in the hope that a certain way of writing will work.
For an excellent programmer, logic is the most important. They are willing to spend more time thinking. In doing so, fewer bugs will appear, and the bug rate can even be reduced. to very low.
I am not a very good developer, but I still have this habit over the years. For complex or diverse functions, I always clarify my ideas first and list out what operations there will be and where there are bugs. You should pay more attention to the minefields. I often mention it to my teammates that a picture is worth a thousand words. If you clarify your thoughts before starting, you will get twice the result with half the effort.
No matter whether the business logic is complex or not, just start working on it. If you find anything wrong, you will modify it. If you find something missing, you will add it. This will result in the code always being piled up there. After many times, The modification has changed beyond recognition, and it is even more miserable for the maintenance people.
Here, the author also suggests that readers, you might as well try drawing first, continue to disassemble a module into interfaces, and implement the module by implementing the interface, so as to achieve interface-oriented programming. In this way Maintainability will be improved a lot...
For communication between modules, it should not be the association between classes, but to achieve interaction through abstraction. Abstraction should not rely on details. Details It should rely on abstraction. This is a bit convoluted. To put it bluntly, it is interface-oriented programming, not implementation-oriented programming.
The advantage of this is that when you want to replace the called class with another implementation class in the future, you don’t have to change the classes that called it one by one, because they adjust It is an interface. The interface has not changed. If you replace the implementation class of the interface with a new class in the configuration, everything will be replaced.
The weaker the coupling between classes, the more conducive to reuse. If a weakly coupled class is modified, it will not affect related classes.
Suggestion: Clear your mind before you start, you will get twice the result with half the effort. During the development process, you might as well define the interface first and complete the module development by implementing the interface to reduce the bug rate as much as possible and write better code.
The improvement of technical capabilities is mainly reflected in the code of "high cohesion and low coupling", because these ideas have derived many development models, such as the now popular MVC, MVP, MVVM, etc.
Version iteration and reconstruction
When we build any system, we should not expect that the system requirements will be determined at the beginning and will never change again. This is unrealistic and unscientific. The idea is that since the requirements are bound to change, how can the design software be relatively easy to modify when faced with changes in requirements, so that when new requirements come, the entire program must be overthrown and restarted.
I believe many friends have encountered it. What was originally a very common requirement has now formed a huge function after N iterations and modifications. With the continuous iteration of versions, the cost of maintenance has also increased. As it gets bigger and bigger, this creates a vicious cycle, and refactoring code is about to enter the stage of history.
It is undeniable that from the perspective of maintenance costs, reconstruction is indeed a very good solution. The cost of reconstruction is smaller than the cost of original basic maintenance, and it is also more convenient for future maintenance. Some companies even directly push the entire project to reconstruction after multiple version iterations. This kind of thing not only happens in small companies, but also happens many times in some large companies.
Technically speaking, there are three things to do when refactoring complex code: understand the old code, decompose the old code, and build new code.
The old code that needs to be refactored is often difficult to understand, especially in modules that have multiple iterations and are handled by many people; excessive coupling between modules causes a single move to affect the whole body, making it difficult to control the scope of influence; old code is not easy to test. There is no guarantee that new code will be correct, especially if the product documentation is incomplete.
This is a piece of code I found during the last review. Let’s not talk about the irregular use of constants. This is the result of product iterations, but this is not an excuse. , after building the wheel so many times, it really shouldn’t be. As a negative teaching material for code reusability, it is vividly reflected here. If there are more states, it will inevitably be repeated many times...
Recommendation: Refactoring is not a panacea. Refactoring is not a panacea. After the code was modified in several subsequent versions, the code became messy and disorganized, and the mess of code was always repeated.
Since we cannot be sure whether the requirements will be modified in the future, we can only deal with it by improving the code quality, adapting to changes in the same way, designing the interface reasonably, and thinking more every time when the requirements are changed. The code is encapsulated and extracted, and the existing logic is changed as little as possible.
The Importance of Design Pattern
Those who know how to design buildings are construction engineers, and those who don’t know how to design buildings are bricklayers.
I have said a lot before. Let me just talk about the importance of design patterns. When it comes to design patterns, we must mention the six basic principles and architectural design. When it comes to architectural design, the importance of design patterns. You can imagine.
First of all, the six basic principles are still a bit controversial. In the books I have read before, they are generally the single responsibility principle, Demeter's law, Richter substitution principle, opening and closing principle, dependency inversion principle and Interface isolation principle.
But I saw on some posts recently that there is a saying that there is no interface isolation principle, but the synthesis/aggregation reuse principle. In order not to affect the previous preparations, the synthesis/aggregation reuse principle will be taken separately. Come out and say it.
The six basic principles are the soul of the entire architecture design and a guiding ideology of architecture design. The design pattern is a specific design technique of architecture design. It is the specific practice of architectural design.
Let’s start with architectural design. For architectural design, it is mainly reflected in abstraction ability. Abstraction ability depends on the architect’s coding experience, functional disassembly and understanding, and logical rigor.
When doing architectural design, we should consider the problem as much and more comprehensively as possible, and make the code as inclusive as possible. The sea is open to all rivers, and tolerance is great. This is the basic condition that architecture designers should have.
The more comprehensive and inclusive the issues considered, the more difficult the work will be and the more obstacles will be created for oneself. Reasonably abstract these detailed issues and propose solutions. The higher the level of abstraction, the more reasonable the solution. This is the value of the architect.
From specific requirements, to code implementation, to specific products. The purpose of architectural design is nothing more than the reusability, scalability and stability of the system. Specific things cannot reflect these characteristics well. Only abstract things can best reflect them.
In the process of architecture design, the single responsibility principle tells us that we should better reflect high cohesion and low coupling. This class is used for data requests, so don’t put some methods for parsing json. If this The class is used for image loading. Please isolate the annotations of the view so that one class is only responsible for one responsibility and has only one reason for changes.
If a class takes on more responsibilities, it means coupling these responsibilities together, which will bring some unnecessary maintenance costs. From a large perspective, the MVP and MVC patterns are both embodiments of the single responsibility principle. Model, view, and control are isolated and each performs its own duties.
Demeter's Law guides us that if two classes do not need to communicate directly with each other, then the two classes should not interact directly. If one class needs to call a method of another class, the call can be forwarded through a third party.
The weaker the coupling between classes, the more conducive to reuse. If a weakly coupled class is modified, it will not affect related classes. The main emphasis is on loose coupling between classes.
Regarding the Liskov substitution principle, many people may not have heard of this term, but it is used all the time in the actual development process. In fact, it is very simple. Subtypes must be able to replace their parent types.
Take a simple example, "List list = new ArrayList();". The benefit of doing this is actually very simple. For example, one day ArrayList cannot meet the demand and you need to use LinkedList instead. You only need to replace ArrayList with LinkedList instead of changing the global list object, which improves maintainability.
The opening and closing principle is the core of the object-oriented principle. It consists of two parts, open to extension and closed to modification. Software requirements will always change. For software designers, it is necessary to achieve flexible system expansion without modifying the original system.
Being open to extensions means abstract programming, not concrete programming, because abstraction is relatively stable. Extensions are restricted through interfaces or abstract classes, and boundaries are defined for extensions. Public methods that do not exist in interfaces or abstract classes are not allowed to appear. .
Let the class depend on a fixed abstraction, so it is closed to modification. This is based on inheritance and polymorphism, which can implement inheritance of abstract classes and extend methods by overriding their methods.
The principle of dependency inversion refers to relying on abstraction rather than relying on specific implementation. This has been mentioned above. In fact, it is interface-oriented programming rather than implementation-oriented programming. The advantage of this is to solve coupling.
Under normal circumstances, the probability of abstraction change is very small, making the user program dependent on abstraction, and the implementation details also depend on abstraction. Even if the implementation details continue to change, as long as the abstraction remains unchanged, the client program does not need to change. This greatly reduces the coupling of client programs to implementation details.
The interface isolation principle holds that "it is better to use multiple specialized interfaces than to use a single interface."
A module should rely on the interfaces it needs, provide whatever interfaces are needed, and eliminate unnecessary interfaces. At the same time, it should also follow the single responsibility principle, so as to avoid the pollution caused by bloated interfaces. Irrelevant interfaces are merged together to form a bloated large interface, which is a kind of pollution to the interface.
The granularity of the interface cannot be too small. Too small will lead to a sharp increase in the number of interfaces, which is unfriendly to developers; if the granularity of the interface is too large, the flexibility will be reduced, and customized services cannot be provided, which will bring problems to the overall project. Unpredictable risks and reasonable interface design are also an art.
There must be a lot of controversy about this picture, because many design patterns use multiple basic principles. The above picture is just a rough summary of the design patterns. .
Emphasizes the specific embodiment of the six basic principles (including the principle of synthesis/aggregation and reuse) in the design pattern. It also explains that the six basic principles and the 23 design patterns are complementary to each other. The six basic principles serve as The cornerstone and template of design patterns, design patterns are a flexible manifestation of the application of six basic principles.
The synthesis/aggregation reuse principle is somewhat controversial. At present, some books still retain the composition/aggregation reuse principle and remove the interface isolation principle. The composition/aggregation reuse principle refers to the implementation of less use of inheritance and more use of composition relationships.
Composition and aggregation are both types of object modeling relationships. Aggregation represents a weak ownership relationship. The whole is composed of parts, and the part can exist as an independent individual without being separated from the whole. Composition is a kind of association relationship. A strong ownership relationship reflects the strict relationship between the part and the whole. The life cycle of the part and the whole is consistent, and the part cannot be separated from the whole.
Summary: The six basic principles are the embodiment of object-oriented thinking. The single responsibility principle and the interface isolation principle embody the idea of encapsulation. The opening and closing principle embodies the encapsulation and encapsulation of objects. Polymorphism, and the Liskov Substitution Principle is the norm for object inheritance.
As for the dependency inversion principle, it is the embodiment of polymorphism and abstract ideas. On the basis of fully understanding object-oriented, mastering basic design principles, and being able to use them flexibly in project design, we can improve our code quality and structural design.
In particular, it can ensure reusability, maintainability, scalability and flexibility, which is also necessary knowledge to understand and master design patterns.
Supplement
Regarding the six basic principles, we should keep them in mind and use them flexibly in development. Regarding the application of design patterns in daily development, one thing still needs to be emphasized. ,What suits you is the best.
If a module is very lightweight at this time and uses design patterns just for the sake of using design patterns, this will undoubtedly appear nondescript, make the project bloated, and also bring some unnecessary maintenance costs. (Although maintenance costs are low).
I have recently started to organize information and prepare to write a special topic on design patterns, mainly focusing on MVP pitfalls and Dimit's law, framework and opening and closing principles, singletons and pitfalls, skinning and observer mode, loading lists and The template method pattern, the comparison between constructors and builders, multiple third-party logins and command patterns will continue to be improved in the future, so stay tuned.