Apply Policy Patterns to Actual Projects

Keywords: Java Database Spring Unity

Read the original: Apply the Strategic Patterns to actual projects

Whether you know or don't know this design pattern, it's bound to look familiar in the project.If chatting theory alone is bound to be dull, only the combination of theory and war can reach the realm of unity of man and sword.

First of all, let me tell you a need, what should you do if you encounter it?You can stop for a few minutes and come up with your solution. You can leave a message below to say what you think.

demand

Users have file upload needs, and we are responsible for storing the files, since our system may be privately deployed to individual customers (deployment depends as little as possible on Middleware capabilities, etc.) and we will operate as our own SaaS service (ensuring high availability of services, etc.).

So we have two needs:

  1. To store files in the distributed storage system fastDfs in the SaaS version
  2. Store files in a database in a customer's private deployment

thinking

Find similarities and differences

  1. The file upload process is the same regardless of the version deployment, so it's not considered yet.
  2. Files are stored differently, and files are acquired and deleted differently
  3. Save, fetch, and delete responses are the same and are not considered.

Begin abstraction

Currently we are concerned with the storage, acquisition and deletion of files.For the same behavior, for different implementations, we must think of defining an interface:

/**
 * File Storage Interface
 * Identify Represents the unique identification of a file, of any type
 * T Represents a return type for upload and download, and can be of any type
 *
 * @author flyhero
 * @date 2019-02-01 11:18 AM
 */
public interface IStorageService<Identify, T> {

    /**
     * Upload Files
     *
     * @param file
     * @return
     */
    T upload(MultipartFile file);

    /**
     * Download Files
     *
     * @param identify
     * @return
     */
    T download(Identify identify);

    /**
     * Delete Files
     *
     * @param identify
     */
    void delete(Identify identify);
}

Two different implementations

  • Store FastDfs
@Slfj
@Service("fastDfsServiceImpl")
public class FastDfsServiceImpl implements IStorageService<String,FileVo> {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileVo upload(MultipartFile multipartFile){
        logger.info("store in fastDfs……");
    }

    @Override
    public FileVo download(String hash) {
        logger.info("from fastDfs Download Files");
        return null;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(String hash) {
        logger.info("from fastDfs Delete Files");
    }
}
  • Storage database
@Slfj
@Service("databaseServiceImpl")
public class DatabaseServiceImpl implements IStorageService<String,FileVo> {

    @Override
    @Transactional(rollbackFor = Exception.class)
    public FileVo upload(MultipartFile file) {
        logger.info("store in database……");
        return null;
    }

    @Override
    public FileVo download(String hash) {
        logger.info("from database Download Files");
        return null;
    }

    @Override
    public void delete(String hash) {
        logger.info("from database Delete Files");
    }
}
  • call
@Service
public class FileServiceImpl implements FileService {

//  The same interface calls different implementations based on different names
//  @Qualifier("databaseServiceImpl")
    @Qualifier("fastDfsServiceImpl")
    @Autowired
    private IStorageService storageService;
  
    public void save(MultipartFile file){
        if (null == file) {
            throws new Exception("File cannot be empty");
        }
        FileVo fileVo = storageService.upload(file);
    }
}

Doubt

Someone might say: How can this be different from what I know about the strategy model? Isn't that the strategy model?

You are right!But this is a design pattern that doesn't have any framework at all, and we're now using the Spring framework in general, so where's the Context in the standard strategy pattern?

  • The Role of Introducing Context

First of all, we need to know the role of introducing Context in order to avoid high-level direct interaction with policy interfaces. Why?Because our Policy Mode interface functions are relatively simple, some high-level modules may require some more complex interactions.

  1. If you call the interface directly, you need to add logic to each implementation;
  2. If the enhancement logic is executed prior to direct invocation, there will be duplicate enhancement logic when used in multiple places and it may be forgotten.

At this point, introducing Context is the best way to solve the problem.

Our FileServiceImpl is equivalent to Context. Since we use the Spring framework and a three-tier architecture, exposing uploaded files, downloaded files, and deleted files is in three different ways (or in different controllers) in the controller layer, to avoid using different storage strategies in several places, I specify the strategies to use directly in the Context, if necessaryIt is also very convenient to switch, simply change the annotation of the IStorageService.

summary

Advantage

  • Policy implementation classes can switch freely
  • Easy to extend, and if there are new policies, simply add an implementation class to the policy interface
  • You don't have to use conditional statements to decide which strategy to use

shortcoming

  • Once the number of policy classes increases, callers need to be aware of the differences between the various strategies

If you have different opinions, don't hesitate to give advice.

More exciting technical articles on WeChat Public Number: Number Actual

Posted by irvieto on Sat, 11 May 2019 20:25:13 -0700