If you think this is a title party, I sincerely ask you to read the first part of the article and draw conclusions. If you think you can stamp your G point, click like.
Refactor three thousand lines of code into 15 lines
I just graduated from the company that year. The company is engaged in data center environment monitoring, which is filled with embedded, precision air conditioners, bus, RFID concepts, I do not understand either. Fortunately, the old client that the company previously wrote with Delphi was too slow, and then it made a webform alternative, which happened to me for ASP. net is still known, my lack of business does not prevent me from being called a programmer in this company. Small companies are also good at small companies, with fewer people. They will soon be responsible for code development. Of course, I am also engaged in this data center Intelligent Management System.
This system is very huge, especially supporting client configuration, and then dynamically generating web pages, data can also be monitored in real time through socket (at that time I really didn't understand network programming ). This is really high, big, and awesome for me at the time !! At that time, I was able to debug the entire system for more than half a month and write some simple pages.
In the process of maintaining the system, some functions need to be extended from time to time, and the following class is involved:
The image is compressed. Click here to view the clear source image.
I can't see it, it's the product of the most popular three-tier architecture of the year. For the startup boy, how professional is the file head comment and reflection, can this constructor be static or private? At that time, I was just getting in touch with such a tall code, and I was instantly paralyzed!
However, when many classes are written, I feel more and more awkward. The following code is used:
The image is compressed. Click here to view the clear source image.
Every time you add a table, in addition to the interface, Dal, and BLL, you have to add a method to the factory class. This is really exhausting, even if I had a software code generator, an artifact recommended to me by the company's G engineer, pasted and copied several times, it also made me feel very complicated. Sometimes I felt a little tired when I typed the keyboard, you have also corrected the copied code. Do you mean this is what programmers should do? No, absolutely not! I think of a famous saying: When you think code is repeated in a program, you should refactor it. Yes, under the guidance of this sentence, I began to make a fuss and decided to challenge this tall code. Facts have proved that the power of thinking is infinite.
So, how can we modify it? After careful observation, we find thatclassName
The generation of is very similar to the type returned. It is only a class name and a string, and the two should be associated. As a result, Google found out the word "reflection" after a while (at the time GFW was not yet rampant.
The next step is the return type. The return type is not fixed, but it seems quite regular ...... This seems to have been seen somewhere. By the way, templates and C ++ courses have been mentioned, So Google again, I learned that C # uses generics instead of the templates in C ++. After learning about generics and reflection, I made the following code by referring to some articles on the Internet:
The image is compressed. Click here to view the clear source image.
That's right. It's the most popular factory class in the three-tier architecture era ......
Looking at the code that rolled over a dozen screens, it turned into more than a dozen lines of code. It was so nice to go to the bones, it was too clean! The only thing that worries me is that when I enter the company, the amount of code is required to help sort out the company's application for software copyrights. The software size is evaluated based on the number of lines of code, if the boss knows that I haven't helped the company increase the amount of code and reduce it, will the boss immediately remove me? I did not dare to show my boss my outstanding achievements. Fortunately, this code has not only encountered any problems, but also avoided the previous colleagues copying the code after adding a new class, but the failure to correct the changes greatly improves the efficiency. Although I did not dare to announce the success of my work, this successful modification completely put me on the road of code reconstruction.
You should know whether this case is true. I believe that the coders who started in saw that this kind of code is definitely no less than me. So what do I want to tell you?
Think more during programming
The idea of programming is very important. Read more classical books.
Looking at it from a small point of view, we need to reconstruct it slowly, especially in response to a large system.
When the repeat occurs, you should consider refactoring.
The less code you copy and paste, the more stable your system is.
Less Code Generator
Let's analyze why my predecessors wrote the above Code. I can summarize the following points:
Because the dynamic soft code generator is used to generate the code conveniently, I don't think much about it.
I understand the concept of a three-tier architecture, but I will apply it without thinking deeply.
There is no refactoring concept in the case of repeated code. This is an ideological problem-thinking is more important than your ability
So far, many people have used code generators. How should we deal with this problem. In my opinion, code generators can reduce your work a lot, but they are less useful. Some repetitive work, except some of them, is actually useless. Most of the work can be done through the framework. For example, for example, in a three-tier architecture, the code generator, that is, the model class, can be used in the framework. Therefore, you must do your best to think about how to reduce repetitive work in the framework, rather than relying on code generators.
In addition, if you are still using the relevant code generation tools, please re-define the Code Template of "dynamic soft code generator" and write a template by yourself; you can also use codesmith to create your own code, because the soft code template is really messy, such as the following code:
for(intn = 0; n < rowsCount; n++)
{
model = newDBAccess.Model.eventweek();
if(dt.Rows[n]["GroupNo"].ToString()!="")
{
model.GroupNo=int.Parse(dt.Rows[n]["GroupNo"].ToString());
}
if(dt.Rows[n]["Week0"].ToString()!="")
{
model.Week0=int.Parse(dt.Rows[n]["Week0"].ToString());
}
if(dt.Rows[n]["Week1"].ToString()!="")
{
model.Week1=int.Parse(dt.Rows[n]["Week1"].ToString());
}
}
Do not reinvent the wheel
First, you cannot usevar row=dt.Rows[n]
Alternative? Second, directly useint.Parse
How low is efficiency? Again,dt.Rows[n]["Week0"]
IsNULL
What should I do?
Let's take a look at some other code:
publicList<string> GetDevices(string dev){
List<string> devs=newList<string>();
intstart=0;
for(inti=0;i<dev.Length;i++){
if(dev[i]==‘^‘){
devs.Add(dev.SubString(start,i));
start=i+1;
}
}
returndevs;
}
Reinvent the wheel. It takes an extra amount of time to see if the robustness and badness of the function are very familiar.String.Split()
Simple implementation of functions. My predecessors should have switched from C ++ programmers. I used to implement various functions myself, but he ignored many things of C. We don't judge the merits of this code, but it actually runs well for a long time. Let's see what's wrong with using this Code:
So how should we avoid repeating the wheel of invention? I have put forward the following points from my personal experience, hoping to help you:
Understand the features of your programming language. You can read a basic getting started book, browse all the features, or go to msdn to repeat the relevant content.
Before you decide to invent a wheel, search for a ready-made solution. You can also search for codeproject, GitHub, and other websites. In zhihu, many Daniel are actually criticizing him. Before asking a question, you cannot first find out if there is a ready-made answer, instead accusing him of failing to answer his question.
After you have a certain foundation, you should read the relevant classic books to learn more about the principles. For example, if you think you have a certain foundation, I suggest you read "CLR via C #" several times. The more principles you understand, the more you can use the features of this programming language to implement the functions that you think you need to write code on your own.
Here is another example. In my existing programs, I found that I need more and more threads to execute some simple tasks, such as checking whether the hard disk reaches 90% every day, at every day, you need to control the opening of the air conditioner and turn off the air conditioner at on the Internet. The more threads are used, the more I think it is a waste, because these scenes only need to be completed once or a limited number of times, most of the time is meaningless. What should I do? I decided to write a task class to complete related tasks. Just do it. I soon wrote this class.
Publicpolicactclassmissionbase: imission
{
Privatedatetime _ nextexecutetime;
Protectedvirtualdatetime [] executetimepoints {Get; privateset ;}
Protectedvirtualintintervalseconds {Get; privateset ;}
Protectediengine engine {Get; privateset ;}
Publicbooliscanceled {get {......}}
Publicboolisexecuting {get {......}}
Publicboolistimetoexecute {get {......}}
Publicabstractboolenable {Get ;}
Publicabstract string name {Get ;}
Protectedmissionbase (iengine engine)
{
Executetimepoints = NULL; // The default interval is used.
Intervalseconds = 60*60; // The default interval is 1 hour.
Engine = engine;
}
/// Task execution Method
Publicvoiddone ()
{
If (interlocked. compareexchange (ref _ isexecuting, 1, 0) = 1) return;
Try
{
......
}
Finally
{
Interlocked. compareexchange (ref _ isexecuting, 0, 1 );
}
}
/// Actual method execution
Protectedabstractvoiddonereal ();
}
Because in principle,Timer
No matter how many classes you declare, there is actually only one thread to execute. When the execution time is reached, this management thread will useThreadPool
To executeTimer
BecauseThreadPool
After the execution is complete, the thread will be recycled immediately. This actually fully implements the functions I need. However, in fact, this task method is not easy to use. There are a lot of code to be written, and the reliability is not guaranteed yet. Of course, I can continue to improve this class, but I decided to search for other methods. One day, I read "CLR via C #" again and read the thread chapter.System.Threading.Timer
AndThreadPool
Class, I will know, useTimer
Class can completely solve the problem of using a small number of threads to complete the scheduled task.
You can't rewrite it when you cannot refactor it.
I have brought many excellent programmers and worked with many excellent programmers. When most programmers see that a system is not so satisfied or has some obvious problems, they can't help but rewrite the entire system in the direction they think it can be optimized, the rewrite structure is often unsatisfactory. There are a lot of unreasonable points in the system, but there are a lot of such code, just to solve the problems in some specific scenarios. That is to say, all the norms and programming principles are actually conditional. They may be correct most of the time and can guide you through your tasks. However, not all regions are applicable. For example, in the database paradigm, we usually consider redundancy in our design. This is against the paradigm, but why are there so many others? Because we may need space for time.
If we start to consider rewriting, you may fall into the following dilemmas:
It takes more effort to complete some seemingly simple bugs
You need to know that some code that seems to be wrong or very bad is actually designed to solve some very tricky problems.
No longer compatible with old systems
You are eager to rewrite the original system, but often ignore the compatibility with the original system, so your new system will be very slow. The maintenance of the old system will be in another embarrassing situation.
Over-design, resulting in delayed completion of the rewrite plan
Programmers with an impulse to rewrite often have some insights on architectural design. They are good at using various design patterns and architectural skills they have learned to build systems, however, the more you want to use the design model as much as possible, the more you fall into the dilemma of over-design, resulting in a delay in the completion of the rewrite plan.
Unable to effectively use the code that has been completed and tested by the existing system
If you do need to rewrite the code, I suggest you refactor the code as much as possible. Because the restructured system makes it easier for you to rewrite and retain previously available business code to the maximum extent.
I will give an example to illustrate how to better utilize existing code through refactoring.
I have a very large system, one of which is used for data collection, storage, alarm management, telephone, SMS, and other alarm notifications. The general structure is as follows:
classMainEngine:IEngine{
publicMainEngine(ConfigSettings config){
}
publicvoidStart();
publicvoidStop();
}
When new business functions need to be added, the code written by the programmer is usually like this: first, modify the configuration class.
Classconfigsettings {
Publicboolnewfuncenable {Get; privateset ;}
Publicconfigsettings (){
Newfuncenable = xx; // read from the configuration file
}
}
Next, modify the main program:
classMainEngine:IEngine{
privateNewFuncClass newCls=newNewFuncClass();
publicMainEngine(ConfigSettings config){
}
publicvoidStart(){
if(config.NewFuncEnable)
newCls.Start();
}
publicvoidStop(){
if(config.NewFuncEnable)
newCls.Stop();
}
}
The coupling between the main program code and extended functions is too strong. Every time you add a function, you must modify the main program code, which is very error-prone. Especially for new developers, it is easy to forget that some fatal code is added to the main program. For example, the above Extension function may only be available in a specific project. However, the code writer forgets to add the enabled configuration options, as a result, all projects have applied this function, and this function requires specific tables. This is a tragedy. Even if you add a configuration, it is not beautiful, because the use of this configuration in a general version will often confuse people outside of the custom project. During the modification process, the configuration file is often used to determine whether the new function is enabled. What problems does the above Code cause:
The person who adds the extended function also needs to be familiar with the entire mainengine code. Otherwise, he does not know how to call the corresponding newclas method in the Start and Stop methods.
If you want to rewrite this code, you will feel very difficult, because you do not know the role of newcls, either you spend a lot of effort to clarify all the code, or you can simply remove the newly added business code.
So how can we reconstruct this code. First, we extract the code for registering new functions and use reflection to register new functions.
Privatevoidregistertaskhandlerbundles ()
{
VaR bundles = xxx. BLL. Caches. servicebundlecache. instance. getbundles ("taskhandlerbundle ");
If (bundles! = NULL & bundles. Count> 0)
{
VaR asmcache = newdictionary <string, assembly> ();
Foreach (VAR bundle in bundles)
{
Try
{
If (! Asmcache. containskey (bundle. Category) asmcache. Add (bundle. Category, assembly. Load (bundle. assemblyname ));
VaR handler = (itaskhandler) asmcache [bundle. Category]. createinstance (bundle. classname, false, bindingflags. Default, null,
Newobject [] {This, bundle}, null, null );
_ Taskhandlerbundles. Add (bundle, Handler );
}
Catch (exception E)
{
Nloghelper. instance. error ("loading bundle [name: {0}, assembly: {1}: Class: {2}] exception: {3}", bundle. name, bundle. assemblyname, bundle. classname, E. message );
}
}
}
}
Modify mainengine code
classMainEngine:IEngine{
privateNewFuncClass newCls=newNewFuncClass();
publicMainEngine(ConfigSettings config){
RegisterTaskHandlerBundles();
}
publicvoidStart(){
_taskHandlerBundles.Start();
}
publicvoidStop(){
_taskHandlerBundles.Stop();
}
}
OK. Now let's take a look at how to implement the original new feature: you only need to create a class according to the specification, inheritITaskHandler
Interface. FinallyXTGL_ServiceBundle
Add a record to the table. Let's take a look at the benefits of doing so:
The newly added classes only need to be written according to the specifications, which has no impact on the mainengine code. You can even write the mainengine code in a newly created DLL.
This service class of the new function is decoupled from the original code, making it easy to test new functions without considering the impact of the original framework.
The Service Classes and architectures of new features are completely separated. In code rewriting, we can immediately reuse the original service function code no matter how we rewrite the system architecture to ensure interface stability.
One of the goals of restructuring is to completely separate the framework from the business.
If you are interested in in-depth understanding, you can understand reflection, IOC, and plug-in language programming.
Learn unit testing and cultivate your refactoring Consciousness
As mentioned above, many people still do not understand refactoring. It doesn't matter. Here I will teach you a quick start method, namely unit testing. For more information about unit testing, Google. What are the requirements for unit testing? It is required that you try to test every method. Try to make your method testable, and it is a powerful tool to cultivate your refactoring consciousness. When you change a method into a testable process, you will find that you must constantly modify your method, so that its responsibilities are as simple as possible, so that it is irrelevant to the context as much as possible, make it complete related functions through the input and output of method parameters as much as possible, so that all dependent classes can be changed to interfaces rather than instances as much as possible. In the end, you will find that this is refactoring! Without knowing it, your refactoring skills will be greatly improved, and your programming level will be greatly improved!
When I see this, experienced programmers will ask, are you encouraging me to use TDD? No, no. TDD (test-driven development) encourages test-driven development. Before development, write the unit test case code to determine the product code to be written. This is a relatively advanced development method, but in the process of programming practice, I think it is too cumbersome, many small and medium-sized enterprises are difficult to implement, let alone our individual developers. I advocate that you use unit testing to cultivate your refactoring consciousness. It can be said that it is a post-drive to improve your refactoring ability and desire, you can call this method "test-driven refactoring ". Of course, if you consciously make the method testable before development, the function you write will be of high quality code. When all your functions are highly reusable functions, you will find that writing code is actually like a pile of wood, which can break down a large demand into numerous small functions, quickly implement the demand.
Below is a piece of code in a very large method. If you know how to program this code as a testable method, congratulations! You are getting started.
The image is compressed. Click here to view the clear source image.
Reconstruction
If you have the patience to see this, you should know that I am not a title party, and this article may be called "how to apply the reconstruction idea in programming, but I don't want to use such a serious title.
Many Programming beginners, or those who have years of programming experience, feel that it is very difficult to read other people's code, and refactoring is even more difficult to talk about. They either sigh with these codes or overwrite them. However, if we are aware of refactoring and are familiar with some code adjustment and optimization tips during programming, you will naturally develop the refactoring capability.
Refactoring is actually very simple:
Build a solid foundation
Highlights of excellent code
Avoid copying and pasting. If you see duplicate code, eliminate it consciously.
Reduce dependencies on code generators
Try to replace rewrite with refactoring when dealing with existing code. You must refactor it before rewriting.
Try to make all methods testable
If you insist on doing so, you will naturally feel it will come out after a while.
The purpose of refactoring is to make your code more concise, stable, and reusable, and to maximize the separation of functions and services. In the reconstruction process, pay attention to the DEX source code encryption protection, I believe that your ability to read code, write excellent code, and system architecture will be steadily improved. It will be just a few days before you become a good programmer.
Starting from rebuilding 3000 lines of code into 15 lines of code