Talk about the design patterns used in the project

1: Strategy Mode

Business scenario: When publishing a notification, you need to select the user object to receive the notification. You can choose from three options: [Department], [Test Center], and [School]. At this time, most small partners implement this way

 if(type == 部门) {
    //通过部门找到所有的用户
 }
 else if(type == 考点) {
    //通过考点找到所有的用户
 }
 else if(type == 学校) {
    //通过学校找到所有的用户
 }
 
复制代码

The disadvantages of this implementation are obvious:

  • 1: If you add a new type now, then you need to add another judgment and continue to implement business logic, which will cause this method to become more and more bloated, difficult to expand, difficult to maintain, and less readable.
  • 2: If you add a new type, it violates the open and closed principle of object-oriented programming and the single principle

Like this branch logic with multiple if...else, we can use the strategy pattern to solve

1: Define a generic interface

public interface NoticeUserInterface {

    //查询处所有需要接收该通知的用户的集合
    List<User> queryAll(Integer type,Integer paramId);

    //类型:1:部门 2:考点 3:学校
    Integer getType();
}
复制代码

2: Define different strategy implementations

// 部门
@Service
public class OrganNoticeUserInterfaceImpl  implements NoticeUserInterface{

    @Override
    public List<User> queryAll(Integer type,Integer depId) {
         //具体的实现逻辑,根据部门查找所有接收该通知的用户
    }

    @Override
    public Integer getType() {
        return 1;
    }

}
复制代码
// 考点
@Service
public class KDNoticeUserInterfaceImpl  implements NoticeUserInterface{

    @Override
    public List<User> queryAll(Integer type,Integer kdId) {
         //具体的实现逻辑,根据考点查找所有接收该通知的用户
    }

    @Override
    public Integer getType() {
        return 2;
    }

}
复制代码
// 学校
@Service
public class SchoolNoticeUserInterfaceImpl  implements NoticeUserInterface{

    @Override
    public List<User> queryAll(Integer type,Integer schoolId) {
         //具体的实现逻辑,根据学校查找所有接收该通知的用户
    }

    @Override
    public Integer getType() {
        return 3;
    }

}
复制代码

3: Use strategy mode


@Component
public class NoticeUserComponent implements ApplicationContextAware {


    private Map<Integer, NoticeUserInterface> iCheckStrategyMap = new ConcurrentHashMap<>();

    public List<User> queryResult(Integer type, Integer paramId) {
        NoticeUserInterface iCheckInterface = iCheckStrategyMap.get(type);
        if (iCheckInterface != null) {
            return iCheckInterface.queryAll(paramId);
        }
        return null;
    }

    //把不同策略放到map
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, NoticeUserInterface> tmepMap = applicationContext.getBeansOfType(NoticeUserInterface.class);
        if(!tmepMap.isEmpty()) {
            for(String beanName : tmepMap.keySet()) {
                NoticeUserInterface noticeUserInterface = tmepMap.get(beanName);
                Integer type = noticeUserInterface.getType();
                iCheckStrategyMap.put(type,noticeUserInterface);
            }
        }
    }
}
复制代码

1: Template mode

In the project, a template needs to be created. The fields of the template are stored in the database. When generating the template, you need to get all the fields of the template from the database, and then generate the corresponding excel, dbf, and csv files. This can be achieved at this time.


class ExcelHandle {
   void createFile(Long templateId) {
      //从数据库找到该模版所有的字段
      List<String> rows = queryFieldsByTemplateId(templateId);
      //生成文件
      createFile(rows);
   }
}

class DbfHandle {
   void createFile(Long templateId) {
      //从数据库找到该模版所有的字段
      List<String> rows = queryFieldsByTemplateId(templateId);
      //生成文件
      createFile(rows);
   }
}

class CsvHandle {
   void createFile(Long templateId) {
      //从数据库找到该模版所有的字段
      List<String> rows = queryFieldsByTemplateId(templateId);
      //生成文件
      createFile(rows);
   }
}

复制代码

In fact, whether it is to generate Excel, dbf or csv, all fields of the template need to be found from the database. Only when a specific file is generated, there is no way to determine it. I just don't know whether to generate Excel, DBF or CSV. kind of we can use the template pattern

1: Define an abstract class

public abstract class CreateFileComponents {

    /**
     * 定义抽象方法,根据模版生成Excel,DBF,CSV等文件,具体的实现在子类
     */
    public abstract void create(ArrayList<Map<String, Object>> rows, String name);

    /**
     * 公用方法,生成文件需要从数据库中找到对应的字段生成文件
     * @param templateId
     * @return
     * @throws Exception
     */
    public final void createFile(Integer templateId) throws Exception{
        // 根据模版ID找到该模版所有的字段
        List<String> rows = queryFieldsByTemplateId(templateId);

        //在本地生成预览文件
        create(rows);

        //然后将该文件上传到minio上
        uploadToMinio();
    }
}
复制代码

2: Define different subclasses

//生成Excel文件
public class ExcelFile extends CreateFileComponents{

    @Override
    public void create(ArrayList<Map<String, Object>> rows) {
        //生成Excel
    }
}
复制代码
//生成DBF文件
public class DdfFile extends CreateFileComponents{

    @Override
    public void create(ArrayList<Map<String, Object>> rows) {
        //生成Dbf
      
    }
}
复制代码
//生成CSV文件
public class CsvFile extends CreateFileComponents{

    @Override
    public void create(ArrayList<Map<String, Object>> rows) {
        //生成Dbf
      
    }
}
复制代码

3: Use Template Mode


CreateFileComponents components =CreateFileFactory.createFileComponents(template.getType());
components.createFile(id);
复制代码

In fact, the simple factory pattern is also used here

public class CreateFileFactory {

    public static CreateFileComponents createFileToViewComponents(Integer type) {
        switch (type){
            case 0:
                return new ExcelFile();
            case 1:
                return new DBFFile();
            case 2:
                return new CSVFile();
        }
        return null;
    }
}
复制代码

Guess you like

Origin juejin.im/post/7085639227844591629