Apply “strategic patterns” to real projects

Whether you know this design pattern or not, it must be familiar from your projects. If only talk about theory is necessarily boring, only the combination of theory and actual combat can achieve the realm of the unity of man and sword.

First of all, LET me talk about a requirement. What should you do if you meet it? Stay for a few minutes to come up with your solution, and leave a comment below with your thoughts.

demand

Users have file upload requirements, and we are responsible for file storage, because our system may be individually private deployment to individual customers (deployment as little as possible to rely on middleware services, etc.), at the same time, we will operate our own SaaS services (to ensure the high availability of services, etc.).

So we have two requirements:

  1. Store files in fastDfs, a distributed storage system, in the SaaS version

  2. The customer’s private deployment stores the files in a database

Train of thought

Look for similarities and differences

  1. The file upload process is the same regardless of the deployment version, so it is not considered.

  2. Files are stored in different ways, obtained and deleted in different ways

  3. The response after save, get and delete is also the same and is not considered.

To abstract

Currently we are concerned with storing, retrieving and deleting files. Same behavior, different implementation, we must want to define an interface:

/** * file storage interface * Identify Indicates the unique identification of the file. * T indicates the return type of the upload or download. **@author flyhero
 * @date 2019-02-01 11:18 AM
 */
public interface IStorageService<Identify.T> {

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

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

    /** * delete file **@param identify
     */
    void delete(Identify identify);
}
Copy the code

#### Two different implementations

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

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

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

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

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

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

    @Override
    public void delete(String hash) {
        logger.info("Delete files from database"); }}Copy the code
  • 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); }}Copy the code

confusion

Some people might say: How is this different from the strategic model THAT I understand? Isn’t that the strategic model?

You’re right! But this is a design pattern without any framework, and we are now using the Spring framework in general, so where is the Context in the standard policy pattern?

  • Introduce the role of Context

First of all, we need to understand the purpose of introducing Context to avoid high-level direct interaction with the policy interface. Why? Because our policy mode interface is relatively simple, some high-level modules may require 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 before the direct call, repeated enhancement logic will exist when used in multiple places and may be forgotten.

Introducing Context is the best way to solve the problem.

Our FileServiceImpl acts like the Context. Since we use the Spring framework and use a three-tier architecture, we expose that uploading files, downloading files, and deleting files are in three different methods (or different controllers) at the Controller layer. To avoid using different storage policies in several places, I specify the policy to use directly in the Context. It is also very convenient when you need to switch, just change the IStorageService annotation.

conclusion

advantages

  • Policy implementation classes can be switched freely
  • Easy to extend, if there is a new policy as long as a new policy interface implementation class
  • You don’t have to use conditional statements to decide which policy to use

disadvantages

  • Once the number of policy classes is increased, callers need to be aware of the differences between policies

If you have a different opinion, please feel free to advise.

Find this interview video is good, share with everyone, public account reply: interview video and architect

20190424123323.jpg

More exciting technical articles in wechat public number: code combat