项目展示文案生成设计
开发过程中,会涉及到文案展示的动作。这些文案可能需要从持久化或者程序处理过程中的内容来进行展示。最蛋疼的事情,由于其中文案的变动性或者设计问题,导致这些文案在程序的各个位置,通过各种stringbuffer、stringbuilder甚至直接各种 "" + "" 之类的拼接,导致程序千疮百孔,不够“优雅”不说,后面维护的成本简直不谈了。
其实大部分情况下,文案如果不涉及到需要根据持久化数据活着计算的数据进行展示的话,其实是可以很简单提到一个配置中心去,数据库也罢、配置文件也罢,甚至类似zookeeper之类的中间件也都可以。但是如果需要根据某些数据实时替换里面的内容,那就显得费劲了。
我们知道Java提供了一个叫做MessageFormat的类,里面直接format一些一些固定格式。比如我们定义一句话叫做:满{0}减{1}元,然后我们传入给你MessageFormat.format一个数组,数组的内容是:{100,10},于是我们可以根据MessageFormat获得文案为:满100减10元。
其实这个是比较简单和易于理解的,我们只需要在程序中把需要动态生成的内容填充到一个数组中,然后将文案配置在另外一个位置,然后不停的调用MessageFormat去获得需要的文案,这样子也可以。然而这种做法有一个问题,就是数组的含义不明确,严重依赖填入顺序,同时在外面配置文案的时候也并不明白那个0或者1到底是个什么东西。
既然我们知道哪些地方该填充什么内容,其实不如我们整理系统所有文案,然后抽象和公用一些关键词,通过约定的关键词填充在文案中,与此同时,程序里面将数组的结构转换为Map结构,map的key就是这些关键词,value就是刚才数组的内容了。如此我们可以摆脱对于数组顺序的要求,同时文案的配置也可以从开发同学的手中转移到PM甚至运营同学的手中。
然而我们还需要确定这种方案的性能以及可用性,于是我们可以做一个实验:
import com.google.common.base.Stopwatch; import org.apache.commons.lang3.StringUtils; import java.text.MessageFormat; import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * created by @author <a href="mailto:congshaogang@meituan.com">Shaogang Cong</a> on 16/5/31. */ public class EffiencyTest { private static String aaa = "满{sum}元减{credit}元,在线支付再减{cre1},限{start}到{endtime}"; private static String aaa1 = "满{0}元减{1}元,在线支付再减{2},限{3}到{4}"; static Object[] aaa1Array = {100, 19, 20, "23:30", "23:45"}; static Map<String, Object> parameterMap = new HashMap<String, Object>(); static { parameterMap.put("sum", 100); parameterMap.put("credit", 19); parameterMap.put("cre1", 20); parameterMap.put("start", "23:30"); parameterMap.put("endtime", "23:45"); } public static void main(String[] args) { Stopwatch stopwatch = Stopwatch.createStarted(); for (int i = 0; i < 100000; i++) { MessageFormat.format(aaa1, aaa1Array); } System.out.println("messageformat" + stopwatch.elapsed(TimeUnit.MILLISECONDS)); String patternString = "\\{(" + StringUtils.join(parameterMap.keySet(), "|") + ")\\}"; System.out.println(patternString); stopwatch.reset(); stopwatch.start(); Pattern pattern = Pattern.compile(patternString); for (int i = 0; i < 100000; i++) { Matcher matcher = pattern.matcher(aaa); StringBuffer sb = new StringBuffer(); while (matcher.find()) { matcher.appendReplacement(sb, parameterMap.get(matcher.group(1)).toString()); } matcher.appendTail(sb); System.out.println(sb.toString()); } System.out.println("pattern" + stopwatch.elapsed(TimeUnit.MILLISECONDS)); } }
执行结果:
messageformat910 \{(endtime|start|sum|cre1|credit)\} pattern229
我们会发现,Map的方式不但容易理解,而且效率要远高于messageformat的方式。同时,我们完全可以做到正则表达式里面的pattern提前compile 暂存起来,如果确实有变化,还可以通过刷新缓存的方式进行更新。
最后,我们讲Map中的key统一到一个常量类活着其他结构中,供开发、产品或者运营童鞋使用即可了。