Avoid unnecessary changes and access to make code robust and easier to maintain level: entry Eric E. Allen (Eallen@cs.rice.edu) Doctoral degree, Java Programming Language Team, Rice University (Rice University) in April 2003 This month, ERIC Allen explained that while avoiding and controlling the change without reason, how to keep the code robustness. He focused on the concepts such as function style code, and methods for marking fields, methods, and classes to process and prevent variability. ERIC also explains the role of unit testing and reconstruction in this task, and provides two tools to assist in achieving reconstruction. Share your views on this article with authors and other readers in the relevant forum. (You can also click on "Discussion" at the top or bottom of this article to access the forum.) Valid adjustment from good programming. Designing easy-to-maintain procedures is one of the most difficult challenges facing programmers, and part of the reason is that the program is usually not maintained by programmers who write code. In order to effectively maintain such procedures, new programmers must quickly understand the working principle of the program. If the programmer can understand the individual sections in the entire program separately, then it can easily understand the working principle of the program. By discussing variability, codec, private methods, end methods, events, local code, unit testing, and reconstruction issues, we will briefly describe some methods of writing programs to help make the program more easily and maintained. Variableability and codec are first discussing variable problems. If the data processed in each of the programs is not changed by the other, remote sections of the program, so it is easy to understand the various parts of the program. Too many information, for example, consider a program that uses a container class instance that can modify the ingredient link. Each time the method from the program from the program is passed to the other portion of the program, and each time the NEW expression is called (where the container is passed as a parameter transmission), the container may be separated from the control of the calling method. Before we first understand how each method calls to modify the container, we can't really ensure that we understand the calling method, whereby our ability to diagnose errors is worse. If each of these calls calls other modifications, the maintenance programmer must be increased rapidly in order to understand that the single method must read, and it is not possible to control. For this reason, it is very advantageous to use different classes for variable containers and non-variable containers. In a non-variable version, the field of the container can be marked into final. Help in the function style relative to the old data to modify the old data, the code is called function style, because the method of the program is similar to the mathematical function, and its behavior is described separately according to the output returned by each input. The advantage that the function style is often ignored is that it is relatively easy to understand the individual components of the program separately. If the data manipulated by the method will never be changed by any operational executed in its body, then the programmer must understand that the method must understand the results returned by the operation. Calling a solution of several other methods of other methods with a procedure, and other methods in that process modify the data structure operated by this method. A considerable feature of the Java language is that it allows us to use the Final keyword (as the pseudo command of the Type Checker) to declare when we have to make a certain data. Use the final keyword to avoid a good way to change the method of "pinning" class. Each time you modify the field, it is possible to change the behavior of the reference to this field. In addition, the field is marked as FINAL to let other programmers of the reading program: No matter how big the entire program is, never modify this field. For example, consider the following type hierarchy representing the non-variable list.
Listing 1. Indicated class hierarchy Abstract Class List {...} class constends list {...} class constends list {...} class constends list {...} class constends list {...} class constends list {...} class constends list {...} class. It is marked into final. To make sure these instances of these classes are not variable, is this enough? Not too enough. Of course, even if the field is marked into final, the component of this field may not be Final, remember this is important. When those components change, any part of the program that references those components may be modified, regardless of whether the field itself changes. In the example above, although the composition elements of the list cannot be modified, we must check those elements themselves not contain non-final fields that may be modified. In this case, although the list may contain variable elements, we can see the element sequence stored in a given list. Due to the following reasons: the instance of the EMPTY list (ie, the length of zero) is not Contains any elements; therefore cannot modify them. CONS (Non-empty list) instance contains two fields, all of which are final. The first field contains the first element of the list, which cannot be modified; the second field contains a list, which contains all remaining elements. If the content of this list is not variable, the list is not variable. However, the list contained in this second field is smaller than the length of the list, so if we know all the lists of length N, we know that the list of length N 1 is not variable. Because we already know that the list of length is constant, we also know that the list of length 1, 2, 3, etc. is also not variable. Tracking with this similar data structure is tedious, but when you can determine the global feature of this structure (such as invariative), this is worth it. The best strategy for controlling changes in undesirable changes is to avoid all changes as much as possible. We should use it if you have a reason to change (for example, when this is greatly simplified). The benefits produced are huge (at lower maintenance costs and enhanced robustness). Even if there is a reason why you want to change the data, it is best to try to control the change, thereby restricting the possible damage as possible. The iterator and streams are excellent examples of the data structure, which is clearly designed to control changes by allowing us to use a series of elements in conventional, definition, rather than clearly modifying a certain handle of these elements. Private ways are like setting fields to Final helps restricting the external effects of field values, which are set to private help to limit their impact on the other parts of the program. If the field is private, we can confirm that other parts of the program are not directly related to it. If we remove this field and replace the internal representation of the class data, then we only care to correct the internal methods to access new data correctly. In the previous example, please pay attention to the field of class CONS is private. In this case, we can control how to access those elements through a reader and a similar approach. If the future maintenance personnel of our list sometimes wants to modify the internal representation of the list (for example, on some platforms, based on the list of arrays may be more effective), then programmers can do so, without having to modify or even see those lists. Any client. As long as you write getter, you can take appropriate operations on new data.
The final method, the ultimate class, and understanding of local code and labeled fields into Final formation, marking the method into Final usually accused to be inconsistent with the OO design objective, as this is prohibited from inheritance polymorphism. But when trying to understand the behavior of large programs, this helps to understand what method has not been rewritten. Now a good OO design involves using a lot of inheritance, this is indeed true. In fact, inheritance is the core of many OO design patterns. But that doesn't mean that we should allow each method we have written to be rewritten. Usually the program will implicitly rely on some key ways without rewriting. By marking such a method into Final, we will allow other programmers to better understand the expression behavior of the method. In addition, the class labeled Final will greatly improve the codently decodable. It will truly help to initially understand which species will not be subclassified. In fact, I think: Only a class that should not be marked into finals is a class that is truly subcatenated in the program, and classes intentionally submitted from external components (the inherent part of the program design). Some people may think that this concept will bind future code maintenance staff to make them unable to expand the code. I don't definitely limit them. If the program future maintenance person needs to extend the code to include the subclass of the previously absence, as long as they have access to source code (if they have no right to access, how do you become the "maintenance person" of this code?), Delete The Final keywords on the corresponding class are not too difficult. At the same time, the added keyword acts as an automatic verification document form about the important invariant of the program ("Auto Verification" because if the document is destroyed, then the program does not even compile). By compulsory developers to consciously choose When to delete such an invariant, we can help reduce the incorrect introduction. Unit testing and variation unit testing can always help understand the code with side effects. If a set of unit tests fully demonstrate the role in the program, then programmers can more quickly understand each method more quickly by reading their unit testing. Of course, whether the unit testing has really covered all the roles is a big problem. An effective range analysis tool similar to Clover can provide some degree of assistance. However, please note that the unit test itself is much more simpler than the strict function method of writing. To test strict function methods, all of them involved these methods with various representative inputs and check their output (and make sure they can throw when they should throw an exception). When testing the method of modifying the data structure status, we must first perform such an operation, which is required to put the input data in the state desired by the method, and then check whether the customer is performed correctly after calling the method. Each of the data desired by the machine. This trick is useful when the reactor tool is packaged in writing a new code, but when you must maintain the old code that is almost coded, what should I do? Reconstruction, reconstruction, or reconstruction. Although the old code is expensive, these times are worthwhile, especially all supported tools now support Java code. There are already many powerful tools that automatically reconstruct Java code that automatically saves key uncapsms. Reconstructing a function of a functionality of Java code is an IDEA development environment. This environment provides automatic support for considerable MARTIN FOWLER reconstruction modes. Another very useful tool I found is CodeGuide, it is an IDE from Germany. Although the list of auto-reconstructed lists is small, it shows an extremely powerful feature - continuous compilation. When you enter a new code, CodeGuide analyzes it and tells you whether it is incomplete in your project (of course, this produces a short delay, it prevents error signals for each ht button). Although continuous compilation has a negative impact on the response, it is very worthwhile in some context.