zoukankan      html  css  js  c++  java
  • [原创]牛刀小试-重构并实现邮件内容生成功能

    案例

    近期团队中多个项目均有邮件发送功能,邮件内容采用html格式,各项目独立开发,无统一实现方案。

    举例:

    某类型EmailSendService

    类型拥有多个String字段 content1、content2 ... content7,均为html文本

    生成邮件内容直接使用字符串连接

    context1 + userName + content2 + inviteCode +
    content3 + money + content4 + year +
    content5 + month + content6 + day +
    content7

    整体不足200来行的代码文件,html字符串就占去了100多行

    我对这段代码的评价无疑是 负分 滚出~

    存在的问题及改进点

    1、Html代码混杂在java代码中,且被拆分,邮件内容及样式不易更改

    2、邮件内容发送变化,则代码需要重写(正常的项目,邮件模板会发送变更是显而易见的实事)

    重构

    目标

    1、Html模板存放于Html文件资源中,java代码以资源文件路径指定模板

    2、模板支持参数替换功能,参数可以命名

    半小时后重构完成,形成如下类型

    文本模板类型

    支持参数批量替换,以"${argName}"格式指定参数

    public class TextTemplate {
        private String template;
    
        public TextTemplate() {
            setTemplate("");
        }
    
        public TextTemplate(String template) {
            setTemplate(template);
        }
    
        public void setTemplate(String template) {
            this.template = template;
        }
    
        // 填充指定模板,返回结果字符串
        // 模板参数替换 ${key} 替换为对应 value
        public String fillTemplate(LinkedHashMap<String, String> kvs) {
            List<String> searchList = new ArrayList<>();
            List<String> replacementList = new ArrayList<>();
    
            for (Map.Entry<String, String> entry : kvs.entrySet()) {
                searchList.add("${" + entry.getKey() + "}");
                replacementList.add(entry.getValue());
            }
            int size = searchList.size();
            return StringUtils.replaceEachRepeatedly(template, searchList.toArray(new String[size]),
                                                            replacementList.toArray(new String[size]));
        }
    }

    这里使用的org.apache.commons.lang包中的StringUtils工具类,其方法replaceEachRepeatedly可以快速完成字符串替换

    资源文件加载工具类

    提供工具方法接受资源文件相对路径,返回资源文本

    public class ResourceUtility {
        public static String getResourceFullText(String path) {
            return getResourceFullText(path, "UTF-8");
        }
    
        public static String getResourceFullText(String path, String encoding) {
            ClassPathResource resource = new ClassPathResource(path);
            StringWriter writer = new StringWriter();
    
            try {
                IOUtils.copy(resource.getInputStream(), writer, encoding);
            } catch (IOException e) {
                throw new RuntimeException("cannot load resource data", e);
            }
            return writer.toString();
        }
    }

    Email内容生成器类型

    public class EmailHtmlContentGenerator {
        private TextTemplate textTemplate;
    
        public EmailHtmlContentGenerator(String resourcePath) {
            textTemplate = new TextTemplate(ResourceUtility.getResourceFullText(resourcePath));
        }
    
        public String generateContent(LinkedHashMap<String, String> properties) {
            return textTemplate.fillTemplate(properties);
        }
    }

    重构上层类型EmailSendService

    1、抽取所有Html代码至独立的Html文件中,放置与resources目录下,可采用路径如 emailTemplates/welcome.html

    2、所有表示Html代码的字符串字段删除

    3、删除邮件Html代码合成逻辑,替代如下代码,独立至一个方法中

        public String generateEmailContent(String userName, String invitationCode, String amount, int year, int month,
                                           int day) {
            LinkedHashMap<String, String> properties = new LinkedHashMap<>();
            properties.put("userName", userName);
            properties.put("amount", amount);
            properties.put("year", String.valueOf(year));
            properties.put("month", String.valueOf(month));
            properties.put("day", String.valueOf(day));
            return emailHtmlContentGenerator.generateContent(properties);
        }

    单元测试

    对TextTemplate类型进行单元测试

    public class TextTemplateTests {
        @Test
        public void testFillTemplateWithNoParams() {
            TextTemplate textTemplate = new TextTemplate("Hello World");
            assertEquals("Hello World", textTemplate.fillTemplate(new LinkedHashMap<String, String>()));
        }
    
        @Test
        public void testFillTemplateWithOneParam() {
            TextTemplate textTemplate = new TextTemplate("Hello ${name}");
            LinkedHashMap<String, String> properties = new LinkedHashMap<>();
            properties.put("name", "Ant");
            assertEquals("Hello Ant", textTemplate.fillTemplate(properties));
    
            properties.put("name", "Man");
            assertEquals("Hello Man", textTemplate.fillTemplate(properties));
        }
    
        @Test
        public void testFillTemplateWithMultiParams() {
            TextTemplate textTemplate = new TextTemplate("===== ${greet} ${name} =====");
            LinkedHashMap<String, String> properties = new LinkedHashMap<>();
            properties.put("greet", "Hello");
            properties.put("name", "Ant");
            assertEquals("===== Hello Ant =====", textTemplate.fillTemplate(properties));
        }
    }

    对EmailSendService类型新增方法进行单元测试

        @Test
        public void testGenerateEmailContent() {
            String emailContent = emailSendService.generateEmailContent("Ant", "999.99", 2015, 2, 4);
            assertTrue(emailContent.contains("尊敬的Ant"));
            assertTrue(emailContent.contains("充值999.99元。"));
            assertTrue(emailContent.contains("2015年2月4日"));
        }
  • 相关阅读:
    Python for i 循环
    Python 输入分数并评
    用户名和密码的输入
    cocos2d-x 3.0学习
    VS2008 ShotKey
    Cocos2d-x 3.0的安装方法
    VFC
    一、在WIN7 64位系统平台,VS2013环境下安装WTL90_4090_RC1(2014-04-01)
    http://www.vcf-online.org/
    Win7 64位 VS2012 安装 Qt5
  • 原文地址:https://www.cnblogs.com/gods/p/4273135.html
Copyright © 2011-2022 走看看