package com.ljn.base; import java.io.BufferedReader; import java.io.FileInputStream; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.io.IOUtils; import org.joda.time.DateTime; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; /** 《代码大全》-表驱动法-消息打印 问题描述: Suppose you’re writing a routine to print messages that are stored in a file. The file usually has about 500 messages, and each file has about 20 kinds of messages. The messages originally come from a buoy and give water temperature, the buoy’s location, and so on. Each of the messages has several fields, and each message starts with a header that has an ID to let you know which of the 20 or sokinds of messages you’re dealing with. 书上对于这个问题的解法,我看得不是很明白,动手写了一下,就有了以下代码,基本达到目的: */ public class TableDriven2 { private static final Map<String, Message> messageMap; /** * 正如书上所说,“消息表”可以硬编码到程序中;也可以定义在配置文件里,程序初始化时读取 * 定义在配置文件的优点是,消息格式变动时,不需要改动java代码;缺点是要解析配置文件 * 这里简单起见,只定义三种消息,且硬编码到程序中 */ static { messageMap = new HashMap<String, Message>(); Message temperature = new Message("001", "Temperature Message"); temperature.addField(new FloatField("Average Temperature")); temperature.addField(new IntegerField("Number of Samples")); temperature.addField(new StringField("Location")); temperature.addField(new DayField("Time of Measurement")); messageMap.put(temperature.getId(), temperature); Message drift = new Message("002", "Drift Message"); drift.addField(new FloatField("Change in Latitude")); drift.addField(new FloatField("Change in Longtitude")); drift.addField(new DayField("Time of Measurement")); messageMap.put("002", drift); Message location = new Message("003", "Location Message"); location.addField(new FloatField("Latitude")); location.addField(new FloatField("Longtitude")); location.addField(new IntegerField("Depth")); location.addField(new DayField("Time of Measurement")); messageMap.put(location.getId(), location); } //示例代码。实际开发中,还应该考虑很多细节,例如每行前后的空格要去掉,要检查每行的数据是否合法,等等 public static void main(String[] args){ FileInputStream fs = null; try { fs = new FileInputStream("C:/Users/lijinnan/Desktop/message.txt"); BufferedReader br = new BufferedReader(new InputStreamReader(fs)); String line = null; boolean messageBegin = true; Message message = null; int fieldIndex = 0; while ((line = br.readLine()) != null) { if (messageBegin) { String messageId = line; message = messageMap.get(messageId); System.out.println("#" + message.getName() + "#"); messageBegin = false; continue; } if (message != null && message.getFields() != null) { if (fieldIndex < message.getFields().size()) { Field curField = message.getFields().get(fieldIndex++); curField.readAndPrint(line); } if (fieldIndex == message.getFields().size()) { messageBegin = true; fieldIndex = 0; System.out.println(); } } } } catch (Exception e) { //ignore for test } finally { IOUtils.closeQuietly(fs); } } } /* 定义消息字段的类型 只定义四种:FloatField IntegerField StringField DayField 当然这四种类型也可以定义为enum */ abstract class Field { private String label; public abstract void readAndPrint(String value); public Field(String label) { this.label = label; } public String getLabel() { return label; } public void setLabel(String label) { this.label = label; } } class FloatField extends Field { public FloatField(String label) { super(label); } /** * 假设浮点数输出的格式是保留两位小数点 */ @Override public void readAndPrint(String value) { float val = Float.parseFloat(value); String formattedValue = String.format("%.2f", val); System.out.println(getLabel() + ":" + formattedValue); } } class IntegerField extends Field { public IntegerField(String label) { super(label); } /** * 假设整数的输出格式为原样输出 */ @Override public void readAndPrint(String value) { System.out.println(getLabel() + ":" + value); } } class StringField extends Field { public StringField(String label) { super(label); } /** * 假设字符串的输出格式为原样输出 */ @Override public void readAndPrint(String value) { System.out.println(getLabel() + ":" + value); } } class DayField extends Field { public DayField(String label) { super(label); } /** * 假设时间的输入格式是dd/MM/yyyy HH:mm:ss * 输出格式是yyyy-MM-dd * 时间的处理用到了joda-time */ @Override public void readAndPrint(String value) { DateTimeFormatter formatter = DateTimeFormat.forPattern("dd/MM/yyyy HH:mm:ss"); DateTime dt = formatter.parseDateTime(value); System.out.println(getLabel() + ":" + dt.toString("yyyy-MM-dd")); } } class Message { private String id; //消息ID private String name; //消息名 private List<Field> fields; //消息字段 public Message(String id, String name) { this.id = id; this.name = name; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public List<Field> getFields() { return Collections.unmodifiableList(fields); } public void addField(Field field) { if (fields == null) { fields = new ArrayList<Field>(); } fields.add(field); } } /* 测试数据,message.txt的内容: 001 1.111 11 East 111 01/01/2011 11:11:11 001 1.010 10 West 001 11/11/2011 11:11:11 002 2.0 2.1 02/02/2012 22:22:22 003 3.0 3.123 3000 03/03/2013 23:33:33 002 22.0 22.1 22/02/2012 22:22:22 003 30.0 30.123 3000 13/03/2013 23:33:33 程序输出: #Temperature Message# Average Temperature:1.11 Number of Samples:11 Location:East 111 Time of Measurement:2011-01-01 #Temperature Message# Average Temperature:1.01 Number of Samples:10 Location:West 001 Time of Measurement:2011-11-11 #Drift Message# Change in Latitude:2.00 Change in Longtitude:2.10 Time of Measurement:2012-02-02 #Location Message# Latitude:3.00 Longtitude:3.12 Depth:3000 Time of Measurement:2013-03-03 #Drift Message# Change in Latitude:22.00 Change in Longtitude:22.10 Time of Measurement:2012-02-22 #Location Message# Latitude:30.00 Longtitude:30.12 Depth:3000 Time of Measurement:2013-03-13 */