If you think this is a title party, then I sincerely implore you to patiently read the first part of the article and then jump to conclusions. If you think you can poke your G-spot, please feel good.
Refactor 3,000 lines of code into 15 rows
I had just graduated that year and entered the company now. The company is engaged in data center environment monitoring, which is filled with embedded, precision air conditioning, bus, RFID concept, I do not understand. Fortunately, the company before using Delphi to write the old client because too slow, and then made a webform substitution, just I know the ASP, I do not understand the business does not hinder me to become a programmer of this company. Small companies also have small companies of good, less people, go in quickly responsible for code development. Of course I'm doing this. Data Center intelligent Management System.
This system is very large, especially the good is to support the client configuration, and then dynamically generate Web pages, data can also be monitored through the socket real-time (I really do not understand network programming). This for me at that time, really is high, big, up !! Then understand the whole system for half a month to be able to debug, write some simple page.
In the process of maintaining the system, occasionally to extend some functions, but also to contact the following class:
See no, that was the most popular three-storey structure of the product, for just out of the hermitage of the hair of the boy, this is how professional file header comments, as well as reflection also forget, this constructor can also be static, but also private? At that time just contact with such a tall code of me, an instant to kneel!
However, the class write more, I feel more and more awkward, is the following code:
Each add a table, in addition to change the interface, to change the DAL, to change the BLL, but also in this factory class add a method, really tired hand cramp, even if there was the company's G work to me recommended artifact--Dynamic soft code generator, this paste copied several times, but also let me feel unusually cumbersome, Sometimes playing the keyboard a little bit tired, but also copy out the code to change the wrong, your sister's, this is the programmer to do things, no, absolutely not! I remember a word of wisdom: When you think that the code repeats itself in the program, it should be refactored. Yes, under the guidance of this sentence, I began to toss, decided to challenge this tall code, it turns out that the power of thought is infinite.
So, how to modify it, after careful observation, it is found that className
the generation is very similar to the type of return, just a class name, a string, the two should be able to associate. So Google a bit (at that time GFW not rampant up ha), vaguely found the " reflection " of the two words, in-depth understanding, OK can be completed.
Next, is the type returned, the type of return is not fixed, but it seems to be very regular ... This seems to have been seen somewhere, right, template , C + + lessons, and then Google again, learned that C # used generics instead of the template in C + +. After studying generics and reflection, and referring to some articles on the web, I made the following code:
Yes, it is, the most popular factory class in the three-story architecture era ...
Look at the original roll more than 10 screen code, became more than 10 lines of code, really cool to the bone, too clean! The only thing that worries me is, when I entered the company, help to organize the company application software copyright is required code, according to the code how many lines to evaluate the size of the software, in case the boss know that I did not help the company to increase the amount of code, but also reduced, will I open it immediately? I did not dare to show our boss my excellent results, fortunately, this code has not been any problem, but also to avoid the previous colleague always after adding a class, the code is copied, but there is no correct modification of the problem, greatly improve the efficiency. Although, I did not dare to announce the results of my work, but this successful revision, then completely let me embark on the code reconstruction of the non-return.
See here, you should know whether this case is true or not. I believe that the yards that started in the 08, see this similar code absolutely no less than me. So, what do I want to tell you?
- To think more about the programming process
- The idea of programming is important, please read more classic books
- From small to small, slowly refactoring, especially when dealing with a large system
- When repeated, you should consider refactoring the
- The less code you paste copied, the more stable your system is
Use less code generators
Let's analyze why my predecessors wrote the code above. I have summed up the following points:
- Because of the use of the dynamic Soft code generator, the code is easy to generate, there is no more thinking.
- The concept of a three-tier architecture is understood, but it is applied without further thought.
- Encountering repetitive code, without the concept of refactoring, is the question of thought-thought is more important than your ability
So far, many people have used code generators, so how do we deal with this problem? I think that the code generator really can reduce your work, but less, those repetitive work, in addition to some really no way, and most of the other can be solved through the framework, for example, like a three-tier architecture, really need to use the code generator, which is the model class, The rest can be completely done in the framework. So you have to do your best to think about how to reduce your repetitive work in the framework, rather than relying on the code generator .
In addition, if you are still using the relevant code generation tool, please redefine the code template of "Dynamic Soft code generator" , write a template yourself, or use Codesmith to fully develop your own code generation , because the code template is really messy, For example, the following code:
for (int n = 0; n < rowsCount; n++){model = new DBAccess.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());}}
First of all, can't you var row=dt.Rows[n]
replace it? Second, int.Parse
How low is the performance of the exception if it is thrown directly? Again, if this code changes a bit, I don't want each dt. Rows[n] have to change it again?
Don't invent the wheel again.
Let's take a look at some other code:
public List<string> GetDevices(string dev){List<string> devs=new List<string>();int start=0;for(int i=0;i<dev.Length;i++){if(dev[i]==‘^‘){devs.Add(dev.SubString(start,i));start=i+1;}}return devs;}
There is no familiar, yes, this is the String.Split()
simple implementation of the function. My predecessors should have been transferred from C + + programmers, accustomed to the various functions of their own implementation, but he ignored a lot of C # things. We don't judge the merits and demerits of this code, but in fact he works very well for a long time. Let's take a look at what's bad about using this piece of code:
- Repeat the invention of the wheel. It took extra time, the robustness of the function and the poor
- poor readability . is actually a very simple function, but with such a function, at first I thought there was something special.
So, how do we avoid reinventing the wheel? I would like to make the following points from my personal experience to help you:
- Learn about the features of the programming language you are learning . You can read a basic introductory book, browse through all the features, or go to MSDN to get the relevant content over again.
- before you decide to invent a wheel, search for a ready-made solution first . You can also search for sites like CodeProject and GitHub. In the knowledge that many people are criticizing such a phenomenon, always ask some repetitive questions, and then the responsibility of the decline, and no one to answer his questions, in fact, the relevant questions have been a very detailed answer, the question before the first to search whether there is a ready answer, instead of accusing him of not answering his question?
- after you have a certain foundation, you should also read the relevant classic books, in-depth understanding of the principles . For example, you think you have a certain foundation, I suggest you read the "CLR Via C #" A few more times, you know the more principles, the more you can use the characteristics of the programming language, so as to achieve the original you think you want to write code on the function.
I'll give you an example of myself here. In my existing program, I found that I need more and more threads to perform some simple tasks, such as checking the hard disk every day to achieve 90%, 9 o ' Day to control the opening of the air conditioning and on the Internet 6 points when the air conditioning off. The more threads I use, the more I feel wasted, because these sites only need to be done once or for a limited number of times, most of the time is meaningless, then how to do? I decided to write a task class to do the related things. I'll write this class out soon.
PublicAbstractClass missionbase:imission{Private DateTime _nextexecutetime;ProtectedVirtual datetime[] Executetimepoints {GetPrivateSet }ProtectedVirtualint Intervalseconds {GetPrivateSet }Protected Iengine Engine {GetPrivateSet }PublicBOOL iscanceled{Get{...}PublicBOOL isexecuting{Get{...}PublicBOOL istimetoexecute{Get{...}PublicAbstractBOOL Enable {Get }PublicAbstractString Name {Get }ProtectedMissionbase (Iengine engine) {executetimepoints =NullThe default is to use the interval Intervalseconds =60 *60;The default interval is 1 hours engine = engine; }///Task execution method public void done () {if (Interlocked.compareexchange ( Span class= "Hljs-keyword" >ref _isexecuting, 1, 0) = = 1) return; try {...} finally {interlocked.compareexchange (ref _isExecuting, Span class= "Hljs-number" >0, 1); }}///actual method execution protected abstract void DoneReal ();}
But, in fact, this task method, not good, to write a lot of code, and reliability is not guaranteed. Of course, I can continue to refine this class, but I decided to search for other ways. Until one day, I read the CLR Via C # again, and when I saw the thread chapter, and the System.Threading.Timer
ThreadPool
class, I knew that using a Timer
class would completely solve my problem of doing timed tasks with as few threads as possible .
Because, in principle, the Timer
class no matter how many you declare, there is actually only one thread executing. When you get to the execution time, this management thread will be used ThreadPool
to execute Timer
the function in, because the use of ThreadPool
, after the completion of the execution, the thread is immediately recycled, this actually completely implemented the function I need.
Consider rewriting when you can't refactor.
I've brought a lot of good programmers and worked with a lot of good programmers. When a large number of programmers are not so satisfied with a system, or have some obvious problems, they can't help but rewrite the whole system in the direction they think they can optimize, and the result is that the rewrite structure is often unsatisfactory. There are a lot of unreasonable places in the system, but there are a lot of this code, just to solve the problem of some specific scenarios. In other words, all specifications and programming principles are actually conditional, and he may be right in most cases to guide you through your tasks, but not everywhere. such as the database paradigm, but in practice our design often consider redundancy, which is contrary to the paradigm, but why there are so many people flock to it? Because we may need to use space to change time.
If we think about rewriting from the start, then you may be stuck in the following dilemma:
- It takes a lot more effort to complete some seemingly simple bugs.
You know, some of the seemingly wrong or very not beautiful code, in fact, is precisely to solve some very tricky problems.
- No longer compatible with the old system.
You are eager to rewrite the original system, but often ignore the original system compatibility, then your new system will be very slow to advance. And the maintenance of the old system, will be caught in and embarrassing situation.
- Over-design, resulting in a rewrite plan that was slow to complete
Programmers with rewriting impulses tend to have some reading on architecture design, they are good at using the various design patterns and architectural skills they have learned to build systems, but the more they try to use design patterns as much as possible, the more they fall into the trap of over-design, which delays the completion of rewriting plans.
- Unable to effectively utilize code already completed and tested by the existing system
If you really need to rewrite it, I suggest you refactor the code as much as possible. Because the reconstructed system allows you to rewrite it more easily and keep the previously available business code to a maximum.
Let me give you an example of how to make better use of existing code through refactoring.
I have a very large system in which a function is used for data collection, storage, alarm management, telephone, SMS and other alarm notifications. The approximate structure is as follows:
class MainEngine:IEngine{public MainEngine(ConfigSettings config){}public void Start();public void Stop();}
When you need to add new business functions, the code that programmers write is often the same: first modify the Configuration class
class ConfigSettings{public bool NewFuncEnable{get;private set;}public ConfigSettings(){NewFuncEnable=xx;//从配置文件读取}}
Then modify the main program:
class MainEngine:IEngine{private NewFuncClass newCls=new NewFuncClass();public MainEngine(ConfigSettings config){}public void Start(){if(config.NewFuncEnable)newCls.Start();}public void Stop(){if(config.NewFuncEnable)newCls.Stop();}}
In the process of modification, it is often based on the configuration file to determine whether the new feature is enabled. What is wrong with the above code:
- The main program code and the extension function is too strong coupling, each add a function to modify the main program code, here is very very error-prone. In particular, the new people Progress Development Group, it is easy to forget the main program added some lethal code. For example, the extension function mentioned above, may be in a specific project will have this extension function, but, the person who wrote the code forgot to add the configuration option is enabled , so that all projects have applied this feature, which requires a specific table, it is tragic. Even if you add a configuration, it's pretty ugly, because using this configuration in a generic version tends to confuse people outside of a custom project .
- The person who added the extension has to be familiar with the whole mainengine code, otherwise, he doesn't know the call of the corresponding method of Newclas in the Start method and the Stop method
- If you're going to rewrite this code, you're going to be very difficult because you don't know what the new instance of Newcls is doing, or you're spending a lot of time figuring out all the code, or just removing the new business code.
So how do we refactor this piece of code? First, we extract the Code of the new feature registration and implement the new function registration through reflection.
PrivatevoidRegistertaskhandlerbundles () {var bundles = xxx. Bll. Caches.ServiceBundleCache.Instance.GetBundles ("Taskhandlerbundle");if (bundles! =Null && bundles. Count >0) {var Asmcache =New dictionary<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, new object[" {this, bundle}, null, null); _taskhandlerbundles.add (bundle, handler); } catch (Exception e) {NLogHelper.Instance.Error ( "load bundle[ NAME:{0},ASSEMBLY:{1}:CLASS:{2}] Exception: {3} ", bundle. Name, bundle. AssemblyName, bundle. ClassName, E.message); } } } }
Modify the Mainengine code
class MainEngine:IEngine{private NewFuncClass newCls=new NewFuncClass();public MainEngine(ConfigSettings config){RegisterTaskHandlerBundles();}public void Start(){_taskHandlerBundles.Start();}public void Stop(){_taskHandlerBundles.Stop();}}
OK, now let's see how we can achieve the original new functionality: you simply create a new class by specification, inherit the ITaskHandler
interface, and implement the interface method. Finally XTGL_ServiceBundle
, a new record is added to the table. Let's take a look at what's good about doing this:
- The new class can only be written by specification and has no effect on the Mainengine code at all. You can even write this mainengine code in a new DLL.
- This new business class is decoupled from the original code, making it easy to conduct business testing of new functionality without having to consider the impact of the original framework
- The new business class is completely separate from the schema, and we can reuse the original business function code as long as we ensure the stability of the interface in the rewrite code, no matter how we rewrite the system architecture.
one of the goals of refactoring is to completely separate the framework from the business .
Interested in in-depth understanding of the students, can understand the reflection, IOC and plug-in programming, and so on.
Learn unit tests and develop your refactoring awareness
Maybe there are so many of them, and there are still a lot of people that don't understand refactoring. It doesn't matter, here I teach you a quick-start approach, which is unit testing . What is unit testing, please Google yourself. What are the requirements for unit testing? is to ask you to make every method as far as possible to test. try to make your method testable, and that is to cultivate the weapon of your reconstruction consciousness . When you ask to turn the method into a testable process, you will find that you have to constantly modify your methods to make it as simple as possible, so that it is as context-independent as possible, so that it can be done with the input and output of the method parameters, so that dependent classes are changed to interfaces instead of instances. In the end, you will find that this is refactoring! And unconsciously, your refactoring will greatly improve the level of your programming will be greatly improved!
Seeing here, an experienced programmer will ask, are you encouraging me to use TDD? No, it's not. TDD (Test-driven development) encourages test-driven development, writing unit test case code before it is developed, and testing the code to determine what product code needs to be written. This is a more advanced development method, but in the practice of programming, I think it is too cumbersome, many small and medium-sized enterprises are difficult to implement, not to mention our personal developers. I encourage you to use unit testing to develop your refactoring consciousness, which can be said to be a post-driver, to improve your refactoring ability and to reconstruct your desires, you can call this method "TDR (Test-driven refactoring)-Test-driven refactoring ." Of course, if you consciously let the method be tested before development, the function you write will be the higher-quality code. When your function is a function of high reusability, you will find that writing code is like a pile of wood, you can break down a large demand into countless small functions, and quickly realize the requirements.
Here's a piece of code in a super-big way, if you know how to make this code a testable method, congratulations, you're getting started.
So-called refactoring
If you have the patience to see here, you should know that I am not a headline party, and this article may be called " how to apply refactoring ideas in programming " is more appropriate, but I do not want to use such a serious title.
Many programmers, or those with many years of programming experience, find it very difficult to read other people's code, and refactoring is not a problem, they either feel powerless the code or overturn it. However, if we have a sense of refactoring and are familiar with some of the tricks of code tuning and optimization during programming, you will naturally develop the ability to refactor.
Refactoring, in fact, is simple:
- Make the foundation strong
- A lot of good code
- Avoid copy and paste, and if you see duplicate code, you should be aware of the need to destroy it.
- Reduce dependency on code generators
- Try to use refactoring instead of rewriting when working with existing code, and be sure to refactor before overriding
- Try to make all the methods testable.
If you insist on doing so, it will feel natural to come out after a while.
The purpose of refactoring is to make your code more streamlined, stable, reusable, and to maximize the separation of functionality and business. In the process of refactoring , your ability to read code, write good code, and system architecture will improve steadily . You become a good programmer will be in the corner.
From the 3,000 lines of code to re-form 15 lines of code to start [go]