Software design - Preliminary Thinking on Modular Design II

Posted by sdjensen on Sun, 05 Dec 2021 22:45:14 +0100

Software design - Preliminary Thinking on Modular Design II

Book connection

We have
The Sender interface is designed to realize the sending of information. Therefore, we have made two implementation classes, mailbox sending and SMS sending. (implemented class, not implemented function)
The Verify interface is designed to implement different verification methods. Therefore, we have made two implementation classes, mailbox verification and account verification. (implemented class, not implemented function)

We aggregate the interface into the AccountModularity class. The code is as follows

package com.ss.account.main;

import com.ss.account.sender.MailSender;
import com.ss.account.sender.Sender;
import com.ss.account.verify.MailVerify;
import com.ss.account.verify.Verify;

public class AccountModularity {

    //Sending interface implementation
    private Sender sender;

    //Verify interface implementation
    private Verify verify;

    public AccountModularity() {
        init();
    }

    private void init() {
        sender = new MailSender();
        verify = new MailVerify();
    }

    private String sendVerifyCode(String account) {
        //TODO processing logic
        String code = "123asd";
        return sender.sendVerifyCode(account,code) ? code : null;
    }

    public boolean verifyAccount(String account) {
        //TODO processing logic
        return verify.verifyAccount(account);
    }
}

Upgrade of AccountModularity class

Today we are going to upgrade this class
Before upgrading, we are looking at the required functions:

  • Sign in
  • register
  • Modify personal information
  • Verify whether the account exists (hidden function)

First, let's analyze the specific functions

Sign in

The parameters required for login can be email account and mobile phone number...
The returned parameters can be user entity classes,

Question:

1. User entity classes cannot be fixed, and the return values should be different according to different entity classes
2. When logging in, the parameters are different.

solve

1. User entity classes can be abstracted as generics.
2. You can abstract different specific validation. Yes, we have abstracted it, that is, the Verify interface. Remember its role? Implement different verification methods.

Similarly, it can be seen that registering and changing passwords requires the use of generics. We will think about whether different interfaces are required.

Here, we have found that AccountModularity is not an implementation class, but defines a set of specifications. Therefore, we make it an abstract class with genericity.
The code is as follows:

package com.ss.account.main;

import com.ss.account.sender.Sender;
import com.ss.account.verify.Verify;
import com.ss.main.BaseModularity;
import com.ss.main.Lifecycle;

/**
 * @author Jangs
 * @param <T> User class
 */
public abstract class AccountModularity<T> {

    //Sending interface implementation
    protected Sender sender;

    //Verify interface implementation
    protected Verify verify;

    /**
     * Protection constructor
     */
    protected AccountModularity() {
    }

    /**
     * Send verification code
     * @param account user
     * @return String Verification Code
     */
    protected abstract String sendVerifyCode(String account) ;

    /**
     * Verify that the user exists
     * @param account user
     * @return boolean Does it exist
     */
    protected abstract boolean verifyAccount(String account);

    /**
     * Sign in
     * @return T User entity class
     */
    protected abstract T login();

    /**
     * register
     * @param t User entity class
     */
    protected abstract void register(T t);

    /**
     * Modify personal information
     * @param t User entity class
     */
    protected abstract void updateAccountInfo(T t);

}

Borderlization

Here, I propose an understanding of modular development: boundary

explain

1. All methods in the module must not be directly accessed externally
2. Provide a public entry method to call other methods.

Take an example

We designed a house and designed many functions inside the house, such as cooking, bathing and sleeping, but these contents are completed inside the house.

If we want to cook, what should we do?
We need to enter from the door, enter the kitchen, cook, and bring out the food when we leave the kitchen.
This is a complete process,
All methods in our module must not be directly accessed externally. If they can be directly accessed externally, it's like we don't need to enter the room to cook. What's the meaning of the house? (a family has no walls). Others can casually enter your kitchen, take a bath and break sleep.

Another example

We are a person, we may do a lot of things, but when we need someone else's help, for example, we need someone to lend me some money.
What do we need to do now?
Just take his pocket and get the money? Not to mention whether you can succeed, when you reach in, you are also beaten. And now it's all electronic money.
What do we need to do under normal circumstances?
First, we need to inform him that I want to borrow some money.
The second step is to let him decide whether to lend us money or not.

We can't dominate other people's behavior. We need to inform Ta and let Ta do it by himself.

Therefore, our AccountModularity class needs the following optimizations
1. Modify the class to an abstract class, AbstractAccountModularity class
2. We need to provide an entry method. Since future modules need this method, we directly abstract it into an abstract class BaseModularity (only entry methods are provided)

Entry abstract class

The code is as follows:

package com.ss.main;

/**
 * @author Jangs
 * This class only provides access methods
 */
public abstract class BaseModularity {
    /**
     * Entrance method
     */
    public abstract void doService();
    
}

Again

We design a house (module), but we only provide various possibilities, specific construction, we can't define, finally, destroy, we can't define
Therefore, I design a life cycle interface to unify the initialization and destruction of all modules.

Lifecycle interface

package com.ss.main;

/**
 * @author Jangs
 */
public interface Lifecycle {
    /**
     * initialization
     */
    void init();

    /**
     * Destroy
     */
    void destroy();
}

The benefits of designing this module are as follows“
1. Unified initialization and destruction
2. Equip for later multi module combination and tree initialization.
Finally, we optimize the

Final optimization results of this period

AbstractAccountModularity

package com.ss.account.main;

import com.ss.account.sender.Sender;
import com.ss.account.verify.Verify;
import com.ss.main.BaseModularity;
import com.ss.main.Lifecycle;

/**
 * @author Jangs
 * @param <T> User class
 */
public abstract class AbstractAccountModularity<T> extends BaseModularity implements Lifecycle {

    //Sending interface implementation
    protected Sender sender;

    //Verify interface implementation
    protected Verify verify;

    /**
     * Send verification code
     * @param account user
     * @return String Verification Code
     */
    protected abstract String sendVerifyCode(String account) ;

    /**
     * Verify that the user exists
     * @param account user
     * @return boolean Does it exist
     */
    protected abstract boolean verifyAccount(String account);

    /**
     * Sign in
     * @return T User entity class
     */
    protected abstract T login(String account, String password);

    /**
     * register
     * @param t User entity class
     */
    protected abstract void register(T t);

    /**
     * Modify personal information
     * @param t User entity class
     */
    protected abstract void updateAccountInfo(T t);

}

Since then

The abstraction of user module has been implemented and optimized, but how to call it? We will decompose it in the next issue
Here I raise a few questions
1. How to implement different validation?
2. How can we extend and optimize the two verification methods if they are implemented at the same time in the future?
3. If this module needs to be extended in the future, how to extend it?
4. If you need to cooperate with other modules in the future, how to link?
5. I use integration a lot, and java can't implement multi inheritance. How about future expansion?

Topics: Java Back-end architecture