Java Code Refactoring _ refactoring

Source: Internet
Author: User
Tags reflection

A few days ago, a line, brain residual hand-shaking accidentally wrote a bug, although the group's eldest brother did not say anything, but the heart is very sad. My colleague said I wrote Bugs because I hated if/else, which was a bad habit. Indeed, If/else can help us write down the Process Control code very conveniently, concise and clear, this condition does what, that condition does what, speak very clearly. To be honest, I never objected to If/else, from the experience, the more complex the business scenario, the more simple the code written, the more often the more error-prone. The result-oriented modern project management method, which is a very effective practical experience.

My colleagues are right, I really hate if/else. This habit is greatly affected by ThoughtWorks a consultant friend, he often nagging in my ear, write code to clean, to be concise, to be flexible, do not cling to the city rules, do not always if/else,switch/case. I've been a classic in the IT field for the first time. In the future learning work also always remind yourself to make their own code as far as possible to look concise, without losing flexibility. Not liking if/else does not mean rejecting it, the use of the time necessary, such as the function interface into the parameter check, processing abnormal branch logic flow. Usually without branching statements, I try not to use, because I think If/else is very ugly, often see If/else code, always look at it with a critical eye, think can reconstruct the better. Most of the time, on what good code, everyone's opinion is often very different, everyone has their own ideas, people who review your code may choose another way of implementation, which does not mean who is right or wrong.

OO design follows the principle of solid (single function, open and close principle, Richter substitution, interface isolation and dependency inversion), using this principle to examine if/else, may find many problems, such as not conforming to a single principle, it itself is like a paste, blending a variety of condiments, sticky very dirty , for example, do not meet the open and closed principle, each new scene, you need to modify the source file to add a branch statement, business logic Complex if there are 1000 scenarios have to have 1000 branch tributaries, this case code not only disgusting problems, efficiency is also a big problem. This shows that, although if/else simple and convenient, but improper use of coding code will bring a very painful experience. For this disgusting If/else branch, of course we think first of all to refactor it-without changing the external features of the code to adjust and optimize the internal logic of the code, but how to do it. Some time ago in the project just met a disgusting if/else example, want to share with you in this blog to remove If/else refactoring process.

If/else's malignant tumor.

There's a good saying--Good articles are changed, and again, good code is definitely refactored, because no software engineer can pat the chest to ensure that the code is designed at the beginning of the project, taking into account the expansion of the possibilities of all requirements changes. As the project grows, the business logic becomes more complex, the code becomes more and more, and the original design may no longer meet the requirements, then it must be refactored. In terms of the overall architecture of the system, refactoring may require significant changes that may require review on the architecture process, and in the context of functional code, this refactoring can be done at any time in our coding process, similar to if/else,swicth/case this type of code refactoring. Today we want to refactor the If/else source code as shown below, for different status Code,countrecoder objects will perform different set methods to assign values to different internal properties.

Public Countrecoder Getcountrecoder (List countentries) {Countrecoder Countrecoder = new Countrecoder (); for (Countentry countentry:countentries) {if (1 = Countentry.getcode ()) {Countrecoder.setcountoff
        Irststage (Countentry.getcount ());
        else if (2 = Countentry.getcode ()) {Countrecoder.setcountofsecondstage (Countentry.getcount ());
        else if (3 = Countentry.getcode ()) {Countrecoder.setcountofthirdtage (Countentry.getcount ());
        else if (4 = Countentry.getcode ()) {Countrecoder.setcountofforthtage (Countentry.getcount ());
        else if (5 = Countentry.getcode ()) {Countrecoder.setcountoffirthstage (Countentry.getcount ());
        else if (6 = Countentry.getcode ()) {Countrecoder.setcountofsixthstage (Countentry.getcount ());
} return Countrecoder; }

The Countrecoder object is a simple Java Bean that holds data entries for each of the six states of the day, and provides get and set methods. Countentry is a data entry record for each state in the database, with state code and count two fields, and we can use MyBatis to implement transformations between database records and Java objects. The above Getcountrecoder method implements the function of converting list to Countrecoder.

See this code, presumably already have a lot of people want to hehe, like a tuo what what, grow so ugly, really don't know it "parents" how to think, how dare "born" out. Don't say anything, just melt down and reconstruct it. Refactoring is an art, Martin flow has written a book "Refactoring Change Code", which records the refactoring of the methodology, interested friends can read. When it comes to refactoring, we usually have a problem with refactoring, which is how to ensure that the refactoring code doesn't change the original external feature. TDD trained friends should know the answer, that is unit testing, refactoring to write unit test, accurate should be complementary unit test, after all, the core concept of TDD is test-driven development. For today's blog sharing examples, because the code logic is relatively simple, so steal lazy, save the unit testing process.

Reconstructing the initial experience--reflection

To refactor the code above, people who are proficient in design patterns can immediately see that this is the perfect scenario for using the policy mode/state pattern, slightly changing the policy pattern, and the factory pattern should be OK, and of course some people will choose to use reflection. For these methods, here are not listed here, mainly want to talk about how to use reflection and Factory mode to solve the problem of eliminating if/else, let's say the Reflection bar, the code looks like this:

private static Map Methodsmap = new hashmap<> ();
    static {Methodsmap.put (1, "setcountoffirststage");
    Methodsmap.put (2, "setcountofsecondstage");
    Methodsmap.put (3, "setcountofthirdtage");
    Methodsmap.put (4, "setcountofforthtage");
    Methodsmap.put (5, "setcountoffirthstage");
Methodsmap.put (6, "setcountofsixthstage");
    Public Countrecoder Getcountrecoderbyreflect (List countentries) {Countrecoder Countrecoder = new Countrecoder ();
    Countentries.stream (). ForEach (Countentry-> fillcount (Countrecoder, countentry));
return countrecoder; } private void Fillcount (Countrecoder shippingordercountdto, Countentry countentry) {String name = Methodsmap.get (CO
    Untentry.getcode ());
        try {Method Declaredmethod = CountRecoder.class.getMethod (name, Integer.class);
    Declaredmethod.invoke (Shippingordercountdto, Countentry.getcount ());
    catch (Exception e) {System.out.println (e); }
}

Reconstructing the initial experience--the so-called pattern

The principle of using reflection to remove If/else is very simple, using HASHMAP to establish a mapping relationship between the status code and the method name of the method that needs to be invoked, for each countentry, first remove the status code, and then obtain the corresponding method name based on the status code to invoke the method. Then use the Java reflection mechanism to implement the corresponding method call. The use of reflection in this example can indeed help us to perfectly remove if/else, but, as we all know, reflection is very inefficient, and in high concurrency, reflection is definitely not a good choice. To remove the reflection method, the only thing I can think of is the use of a policy pattern or a similar state pattern, and a factory model, for example, the classic architecture UML frame composition is usually composed of three elements: abstract product role: usually an abstract class or interface, which defines an abstract method Specific product roles: implementation classes for specific products, inheriting or implementing abstract policy classes, usually consisting of one or more constituent classes. Factory role: Holding the reference of abstract product class, responsible for the selection and construction of the product in the dynamic runtime

The schema diagram of the policy pattern is very similar to the factory pattern, but objects executed in the policy pattern are not called products, called policies. In this case, the product here is a virtual product, which is an interface or implementation of the nature of the service class. Ok, refactoring our code according to the Factory mode, we first define an abstract product interface Fillcountservice, which defines the product's behavior method Fillcount, the code looks like this:

Public interface Fillcountservice {
    void Fillcount (countrecoder countrecoder, int count);
}

Then we need to implement each of these six types of service products, in each product to encapsulate a different service algorithm, the specific code is as follows:

Class Firststageservice implements Fillcountservice {@Override public void Fillcount (Countrecoder countrecoder, I
    NT count) {Countrecoder.setcountoffirststage (count); Class Secondstageservice implements Fillcountservice {@Override public void Fillcount (Countrecoder Countreco
    der, int count) {countrecoder.setcountofsecondstage (count); Class Thirdstageservice implements Fillcountservice {@Override public void Fillcount (Countrecoder countrecod
    er, int count) {countrecoder.setcountofthirdtage (count); Class Forthstageservice implements Fillcountservice {@Override public void Fillcount (Countrecoder countrecod
    er, int count) {countrecoder.setcountofforthtage (count); Class Firthstageservice implements Fillcountservice {@Override public void Fillcount (Countrecoder countrecod
    er, int count) {countrecoder.setcountoffirthstage (count); } class Sixthstageservice implementsFillcountservice {@Override public void Fillcount (Countrecoder countrecoder, int count) {countrecoder.se
    Tcountofsixthstage (count); }
}

Next, we need to implement the factory role, in the factory domestic demand to achieve the product dynamic selection algorithm, using HashMap to maintain the state code and the specific product of the mapping relationship between the objects,
It can be very easy to achieve this, the specific code is as follows:

public class Fillcountserviefactory {

    private static Map Fillcountservicemap = new hashmap<> ();

    static {
        Fillcountservicemap.put (1, New Firststageservice ());
        Fillcountservicemap.put (2, New Secondstageservice ());
        Fillcountservicemap.put (3, New Thirdstageservice ());
        Fillcountservicemap.put (4, New Forthstageservice ());
        Fillcountservicemap.put (5, New Firthstageservice ());
        Fillcountservicemap.put (6, New Sixthstageservice ());
    }

    public static Fillcountservice getfillcountstrategy (int statusCode) {return
        fillcountservicemap.get (statusCode );
    }
}

The client becomes very simple when used specifically, and the Getcountrecoder method can be implemented using the following code:

Public Countrecoder Getcountrecoder (List countentries) {
    Countrecoder countrecoder = new Countrecoder ();
    Countentries.stream (). ForEach (Countentry-> 
            fillcountserviefactory.getfillcountstrategy ( Countentry.getcode ())
                    . Fillcount (Countrecoder, Countentry.getcount ()));
    return countrecoder;
}

Reconstruction of initial experience--java8 the simplification of pattern design

and reflection using design patterns are also perfect to remove the if/else, but have to introduce a large number of specific service implementation classes, while the program appears a large number of template code, so that our program looks very dirty, fortunately, after Java 8 introduced functional Interface, We can use lambda expressions to get rid of these template codes. Turning an interface into functional interface can be implemented by adding Functionalinterface annotations on the interface, as shown in the following code:

@FunctionalInterface Public
interface Fillcountservice {
    void Fillcount (countrecoder countrecoder, int count) ;
}

Then the specific service implementation class can use a simple lambda expression instead, and the original Firststageservice class object can use the following expression instead:

(Countrecoder, Count)-> Countrecoder.setcountoffirststage (count)

Then the code in the factory class can be changed to:

public class Fillcountserviefactory {private static Map<integer, fillcountservice> Fillcountservicemap = new H

    Ashmap<> ();
        static {Fillcountservicemap.put (1, (Countrecoder, Count)-> Countrecoder.setcountoffirststage (count));
        Fillcountservicemap.put (2, (Countrecoder, Count)-> Countrecoder.setcountofsecondstage (count));
        Fillcountservicemap.put (3, (Countrecoder, Count)-> Countrecoder.setcountofthirdtage (count));
        Fillcountservicemap.put (4, (Countrecoder, Count)-> Countrecoder.setcountofforthtage (count));
        Fillcountservicemap.put (5, (Countrecoder, Count)-> Countrecoder.setcountoffirthstage (count));
    Fillcountservicemap.put (6, (Countrecoder, Count)-> Countrecoder.setcountofsixthstage (count)); public static Fillcountservice getfillcountstrategy (int statusCode) {return Fillcountservicemap.get (status
    Code);
 }
}

So our code has been reconstructed, of course, or some imperfections, the magic numbers in the program is not conducive to reading comprehension, you can use the easy to read the constants to identify them, here is not too much to explain.

Summarize

Craig Larman once said that the most important design tool for software development is not technology, but a well-trained mind in design principles. The end result of refactoring does not necessarily make the code less, but it may increase the complexity and abstraction of the program, which is true for if/else in this example. I very much agree with a friend of mine said, do the pursuit of technology, yes If/else can work in the code is very good, can easily be understood by the successor, but we can have a better choice, because the simple code can also be very exciting. More often than not, perhaps one day you can really achieve what Craig says is a well-trained mind in design principles, who says that's not the case. Come on, let's go.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.