How can factory mode be used to achieve program decoupling in Spring?

Keywords: Java JDBC MySQL Database

Catalog

@

1. What is coupling and decoupling?

Since it is program decoupling, we must first know what coupling is. Coupling is simply program dependency, which mainly includes

1. Dependencies between classes
2. Dependency between methods

For example, here is the code:

  public class A{
        public int i;
    }

    public class B{
        public void put(A a){
            System.out.println(a.i);
        }
    }

In the example above, there is a strong coupling relationship between class A and class B. Class B depends directly on class A. The put method of class B is not required for class A. We call this a strong coupling relationship.

In actual development, you should do this: compile-time dependency, run-time dependency.How do you understand that?It's easy to think of polymorphic upward transition, yes, compile-time uncertainty, run-time certainty, of course a wider contact with children's shoes will think of interface callbacks, is the interface callback method can also effectively decouple!The following code:

//An interface called Inter defines a happy () method that is implemented by two classes A and B

interface Inter{
    void happy();
}

class A implements Inter{

    @Override
    public void happy() {
        System.out.println("happy...A");
    }
}

class B implements Inter{

    @Override
    public void happy() {
        System.out.println("happy...B");
    }
}

public class Test{
    public void happys(Inter inter){
        inter.happy();
    }
}

Yes, if the code above is a typical interface callback, the happys method parameters in the Test class become relatively flexible. There is a weak coupling relationship between the Test class and class A and class B in the code. The happys method parameters of the Test class can make the class A type or class B type, unlike the non-class A type in the strong coupling relationship.

In a sense, decoupling using the upward transition of classes or interface callbacks is using the idea of polymorphism!

Of course, there are many ways to decouple. To achieve low coupling is to decouple the two classes, to decouple the direct relationship between classes, to convert the direct relationship into indirect relationship, so there are also many design modes to decouple the program, such as adapter mode, observer mode, factory mode.... In a word, it must be clear that the highly coupled programVery poor independence!

2. jdbc program decoupling

Let's start with a code:

//1. Registration Driver
DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //If removing jdbc's MySQLjar package dependency from direct compilation failure prompt no mysql    
//2. Get Connections
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost:3306/ufida","root","root");
//3. Get the preprocessing object of the operation database
PreparedStatement pstm=conn.prepareStatement("select * from client");
//4. Execute SQL to get a result set
ResultSet rs=pstm.executeQuery();
//5\Traverse result set
while(rs.next()){
    System.out.println(rs.getString("name"));
}
//6. Release Resources
rs.close();
pstm.close();
conn.close();

Wait a minute to get familiar with the code of nostalgia...

Yes, it's jdbc's code, not for nostalgia, but if it's designed like this, how would you feel about such program coupling?How to decouple?Think carefully first.

A minute has passed...
Two minutes have passed...

Okay, we all know that jdbc connection to MySQL requires a jar package for mysql-connector. If we depend on this jar package or remove this jar package, obviously this program above will compile errors, as shown in the following figure

Obviously, such program coupling is too high!So we can design this to change the way we register driver code new for the first step to reflect as follows:

 //1. new way to register drivers
DriverManager.registerDriver(new com.mysql.jdbc.Driver()); //If removing jdbc's MySQLjar package dependency from direct compilation failure prompts no mysql-related jar package

//Change to the following

 //2. Register drivers Reflectively
Class.forName("com.mysql.jdbc.Driver"); //Registering the driver this way will not fail to compile, which is relatively decoupled compared to the above method, but still has a drawback: if the connection is changed to an Oracle database, the string here has to be changed!

As the comment explains, another flaw emerges: if the connection changes to an Oracle database, the string here will change again!

So there is such a decoupling idea for this jdbc program:

Step 1: Create objects through reflection and try to avoid using the new keyword
Step 2: Get the fully qualified class name of the created object by reading the configuration file

3. Program coupling of traditional dao, service, controller

Following the decoupling idea of jdbc program, let's also look at the program coupling analysis of traditional dao, service, controller

Since it's just a demo, leave out the dao layer operation...

Define a Service Interface

public interface IAccountOldService{
    public void save();
}

Service Interface Implementation Class

public class AccountServiceOldImpl implements IAccountOldService{

    @Override
    public void save() {
        System.out.println("save Success in an account....");
    }
}

controller code:

public class AccountCencollertOld {
    public static void main(String[] args) {
        IAccountOldService iaccount=new AccountServiceOldImpl (); 
        iaccount.save();  //Run result: save succeeded in an account...
    }
}

What do you think about getting here?There seems to be no problem, So Beautiful, but look closely.The strong interdependence of the presentation and business, business and persistence layers contradicts the principle of high cohesion and low coupling in our development programs, Oh My God,So Bad!Following the decoupling idea of the jdbc program, we should try to avoid using the new keyword, we find that the service layer within these layers, the new persistence layer, the new business layer service of the controller representation layer.... Too bad

So what are your decoupling ideas for this?

4. Use factory mode to decouple

Don't think, factory mode implementation program decoupling is worth it!Following the decoupling thought of jdbc program:

1. Get the fully qualified class name of the created object by reading the configuration file
2. Create objects through reflection and try to avoid using the new keyword

Start by writing a bean.properties configuration class in the resources directory, as follows

accountServiceOld=com.factory.service.impl.AccountServiceOldImpl

Then use the factory method code:

/**
 * A factory for creating Bean objects
 *
 *    1,A configuration file is required to configure the contents of our service and dao configuration files: Unique ID = Fully Qualified Class Name (key-value)
 *    2,Reflect Create Object by Reading Configuration in Configuration File
 *
 *    Scenario: Mainly the service calls dao, and the controller calls the service's program.In this case, the coupling is very high, depending on each other new ly
 *
 *    To decouple, use factory mode
 */
 public class BeanFactoryOld {
    private static Properties props;

    static{
        try {
            //Instantiate Object
            props = new Properties();

            //Get Stream Object for Properrties File
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);//Load the configuration file under its corresponding path

        }catch (Exception e){
            throw new ExceptionInInitializerError("Initialization properties Failed!");
        }
    }

    //Get a bean object by its name
    public static Object getBean(String beanName){
        Object bean=null;
        try {
        String beanPath= props.getProperty(beanName);
        bean = Class.forName(beanPath).newInstance();   //Here, newInstance creates an instance (default parameterless constructor) which needs to be created once per execution
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }
}

At this point, the controller's code can be written as

/**
 * Here we simulate a controller calling service
 *
 */
public class AccountCencollertOld {
    public static void main(String[] args) {
   //    IAccountOldService iaccount=new AccountServiceOldImpl (); //Use factory method no longer via new method

        IAccountOldService iaccount= (IAccountOldService) BeanFactoryOld.getBean("accountServiceOld");
        iaccount.save(); //Run result: save succeeded in an account.... Indicates that the service was successfully invoked
    }
}

By running the result, nothing is wrong and the program coupling is successfully reduced!So Beautiful!Happy for a while, because it's coming up soon.... But then there's another problem, so let's rewrite this controller

for(int i=0;i<5;i++){
        IAccountOldService iaccount= (IAccountOldService) BeanFactoryOld.getBean("accountServiceOld");
        iaccount.save(); 
     }

Print results:

com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
com.factory.service.impl.AccountServiceImpl@677327b6
save Success in an account....
com.factory.service.impl.AccountServiceImpl@14ae5a5
save Success in an account....
com.factory.service.impl.AccountServiceImpl@7f31245a
save Success in an account....
com.factory.service.impl.AccountServiceImpl@6d6f6e28
save Success in an account....

There are five different objects printed, and the description is multiple. Each time getBean is called, a new object is printed from newInstance, as follows

Many cases create objects every time, wasted resources, inefficient

For single and multiple cases, we will modify the service business layer code:

public class AccountServiceImpl implements IAccountService {
    //Define class members
    private int i=1; 

    @Override
    public void save() {
        System.out.println("save Success in an account....");
        System.out.println(i);
        i++;
    }
}

Run controller code, run results

save succeeded in an account...
1
 save succeeded in an account...
1
 save succeeded in an account...
1
 save succeeded in an account...
1
 save succeeded in an account...
1

why?Many examples are 1 because they are new objects each time, which is also validated above, so each time a new object is created, it is initialized and reassigned, so if we change the class member to a local member variable as follows

public class AccountServiceOldImpl implements IAccountOldService {

//    private int i=1; 
    @Override
    public void save() {
        int i=1;   //Change to local variable
        System.out.println("save Success in an account....");
        System.out.println(i);
        i++;
    }
}

Don't guess, the result is also 1.Let's figure it out or run it.

save succeeded in an account...
1
 save succeeded in an account...
1
 save succeeded in an account...
1
 save succeeded in an account...
1
 save succeeded in an account...
1

With all this said, it doesn't seem like a matter to look at just one instance between service and dao, because there are no class members that have changed in the business method between them, so you don't need multiple instances to keep threads safe.So what does that mean?Don't forget, since it's not good (wasting resources) to create instances every time you execute using factory improvements. newInstance Create Instance (default parameterless constructor) as shown below, it's perfect to design an instance created only once by newInstance, which is why I add an Old keyword to both service and controller, so follow upLet's see how the factory has been improved!

5. Improvement of factory mode

In order not to get confused, we rewrite the code, that is, start over writing the code ~actually remove Old~

Since it's just a demo, leave out the dao layer operation...

Define a Service Interface

public interface IAccountService {
    public void save();
}

Service Interface Implementation Class

public class AccountServiceImpl implements IAccountService{

    @Override
    public void save() {
        System.out.println("save Success in an account....");
    }
}

controller code:

/**
 * Here we simulate a controller calling service
 *
 */
public class AccountCencollert {
    public static void main(String[] args) {
//        IAccountService iaccount=new AccountServiceImpl(); 

        IAccountService iaccount= (IAccountService) BeanFactory.getBean("accountService");
        iaccount.save(); //Result of operation: save succeeded in an account.... Indicates that the service was successfully invoked
      }
 }

Improved factory method code:

public class BeanFactory {
    private static Properties props;

    //Define a map container to hold the created objects
    private static Map<String,Object> beans; //Improved code=============

    static{
        try {
            //Instantiate Object
            props = new Properties();

            //Get Stream Object for Properrties File
            InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
            props.load(in);//Load the configuration file under its corresponding path

            /////////////////////Following is the improved code=====================
            //Instantiation Container
            beans=new HashMap<String,Object>();
            //Remove all key values from the configuration file
            Enumeration<Object> keys = props.keys();
            //Traversal Enumeration
            while(keys.hasMoreElements()){
                //Take out each key
                String key = keys.nextElement().toString();
                //Remove the corresponding value from the key (because each value corresponds to a class path)
                String beanPath = props.getProperty(key);
                //Reflection Create Object
                Object value = Class.forName(beanPath).newInstance();
                //Store key s and value s in containers
                beans.put(key,value);
            }
        }catch (Exception e){
            throw new ExceptionInInitializerError("Initialization properties Failed!");
        }
    }


    //As the code improves, we can simplify the following method of getting bean objects, as shown in the following code
    /**
     * Get object by bean name (singleton)
     */
    public static Object getBean(String beanName){
        //Get the corresponding object from the corresponding key in the Map container
        return beans.get(beanName);    //Get it here in the Map container so you don't create instances every time!
    }

//No longer use the following method
  /*
    //Get a bean object by its name
    public static Object getBean(String beanName){
        Object bean=null;
        try {
        String beanPath= props.getProperty(beanName);
        bean = Class.forName(beanPath).newInstance();   //Here, the newInstance creation instance (default parameterless constructor) needs to be created once per execution, which is not good
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bean;
    }*/
}

From the improved factory code above, we can see that a Map container is defined at the beginning to hold the created objects. Why define a Map container first?Install this instance in a container, because if you don't put this object in, it's easy to get rid of by GC without using it, let's just use this object now!

Define a Map container to store each object in the configured file, and then we directly provide a getBean method to get the value based on the key of the Map. This not only extends the flexibility of the program's configuration file, but also guarantees that only one object is generated and that resources are not wasted, So Beautiful!

So how can you prove that this is a singleton pattern?It's easy to design the service business layer and controller presentation layer code as follows:

service business layer: add a class member property and i++ inside the method;

public class AccountServiceImpl implements IAccountService {

    private int i=1; //Class Member Properties

    @Override
    public void save() {
        System.out.println("save Success in an account....");
        System.out.println(i);
        i++;//Second Reform Code
    }
}

controller Representation Layer: Create a method that calls the factory five times to create objects

/**
 * Here we simulate a controller calling service
 *
 */
public class AccountCencollert {
    public static void main(String[] args) {
        for(int i=0;i<5;i++){
            IAccountService iaccount= (IAccountService) BeanFactory.getBean("accountService");
            System.out.println(iaccount);  //Five different objects are printed, and the description is multiple
            iaccount.save();  //You will find that the i-values printed are all 1 and do not self-increase successfully
        }
    }

Run Code Result:

com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
1
com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
2
com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
3
com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
4
com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
5

It was found that all five objects were identical, and there was a change in the attributes of class members.

What if we change the class member property to a local member property?

public class AccountServiceImpl implements IAccountService {

    @Override
    public void save() {
        int i=1; //Local Member Properties
        System.out.println("save Success in an account....");
        System.out.println(i);
        i++;
    }
}

Run Results

com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
1
com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
1
com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
1
com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
1
com.factory.service.impl.AccountServiceImpl@1540e19d
save Success in an account....
1

When we see this result, we can think of the reason why we avoided defining class members in servlet s before.In many cases, this will not happen!!!

6. Conclusion

Here we have a basic understanding of the coupling and decoupling between programs, as well as a further understanding of single cases and multiple cases.spring uses factory mode to decouple programs. spring is a big factory. Perhaps you are not aware of it...

If this has helped you a little, please give me a little approval. Your approval is my greatest motivation. Thank you ~

Finally, if there are any deficiencies or inaccuracies, you are welcome to correct the criticism and appreciate it!If in doubt, please leave a message and reply immediately!

Welcome to my Public Number, which contains some java learning materials and a large wave of Java e-books, such as Professor Zhou Zhiming's in-depth Java virtual machine, Java programming ideas, core technology volume, big story design mode, Java Concurrent Programming practice.....are all the Bibles of java, let's not say get on the Tomcat car, let's go!The most important thing is to explore technology together, yearn for technology, and pursue technology. Well, if you come, you'll be friends.

Posted by olko on Wed, 25 Dec 2019 16:43:53 -0800