设计模式无处不在,我们可以在各种开源框架中识别到各种设计模式是如何被巧妙地应用的。BlackStar,虽然并不刻意,但也使用到很多设计模式。我们来看看BlackStar的Reporter,看一些常用的设计模式是如何使其更加灵活和可扩展的。
1.什么是Reporter
BlackStar的JVM Monitor会进行JVM监控数据的采集,采集后的数据如何处理呢,就交个Reporter去进行数据的处理。
2.策略模式
数据处理可能记录到文件、也可能调用远端服务器接口进行记录、也可能存储到数据库,对于JVM Monitor来说,不会关心Reporter如何去存储数据,这就是策略模式。
如下上策略接口定义
public interface ResultReporter
{
void report(ReportData data);
}
3.装饰模式
为了不影响JVM Monitor继续后续的工作,对于结果记录我们希望采用异步的方式去处理,然而如果每种策略实现者都需要自己去处理异步,当然是一个很冗余乏味的工作,我们使用装饰模式来处理
下面是异步Reporter的实现(代码做了裁减)
public class AysnResultReporter implements ResultReporter
{
private final static Log LOGGER = LogFactory.getLog(AysnResultReporter.class);
private ResultReporter targetResultReporter;
private ExecutorService executorService;
public AysnResultReporter(ResultReporter targetResultReporter,
ExecutorService executorService)
{
super();
this.targetResultReporter = targetResultReporter;
this.executorService = executorService;
}
public void report(final ReportData data)
{
this.executorService.execute(new Runnable()
{
public void run()
{
targetResultReporter.report(data);
}
});
}
}
看下面的示例
ResultReporter reporter = new AsynResultReporter(new FileReporter(...), executorService);
//异步、文件Reporter
ResultReporter reporter = new AsynResultReporter(new HessianReporter(...), executorService);
//异步、Hessian Reporter
4.组合模式
我们的目标当然不仅仅这么简单,为了保证采集的数据一定会被记录,我们可能要求首先使用远程模式记录,而远程模式记录错误时,则使用文件记录,或者我们可能要求使用多个Reporter同时记录,我们看看组合模式如何处理的
看看ListResultReporter的实现
public class ListResultReporter implements ResultReporter
{
private final static Log LOGGER = LogFactory
.getLog(ListResultReporter.class);
private boolean isAndMode = true;
private List<ResultReporter> list = new ArrayList<ResultReporter>();
public ListResultReporter()
{
this(true);
}
public ListResultReporter(boolean isAndMode)
{
this.isAndMode = isAndMode;
}
public void addResultReporter(ResultReporter reporter)
{
this.list.add(reporter);
}
public void report(ReportData data)
{
for (ResultReporter reporter : list)
{
if (isAndMode)
{
reporter.report(data);
} else
{
try
{
reporter.report(data);
return;
} catch (Exception e)
{
LOGGER.error("Error When Report[" + reporter + "]", e);
}
}
}
}
}
看看怎么使用
ListResultReporter reporter = new ListResultReporter();
reporter.addResultReporter(new FileReporter(...));
reporter.addResultReporter(new HessianReporter(...));
5.适配器模式
我们知道,对于数据库的写入,一次批量写总是要比多次写要高效地多,因此对于数据库的Reporter,会使用一个BatchResultReporter的接口,而Monitor使用的是ResultReporter接口,因此需要在它们之间做一个适配
BatchResultReporter接口
public interface BatchResultReporter
{
void reportBatch(List<ReportData> datas);
}
适配器
public class BatchReporterAdapter implements ResultReporter
{
private final static Log LOGGER = LogFactory
.getLog(BatchReporterAdapter.class);
private final Object LOCK = new Object();
private BatchResultReporter targetBatchResultReporter;
private long maxBatchSize = 1000L;
private List<ReportData> datas = new ArrayList<ReportData>();
public BatchReporterAdapter(BatchResultReporter batchResultReporter)
{
super();
this.targetBatchResultReporter = batchResultReporter;
}
public void setMaxBatchSize(long maxBatchSize)
{
this.maxBatchSize = maxBatchSize;
}
public void report(ReportData data)
{
List<ReportData> oldDatas = null;
synchronized (LOCK)
{
this.datas.add(data);
if (this.datas.size() >= this.maxBatchSize)
{
oldDatas = this.datas;
this.datas = new ArrayList<ReportData>();
}
}
if (oldDatas != null)
{
commit(oldDatas);
}
}
public void commit()
{
List<ReportData> oldDatas = null;
synchronized (LOCK)
{
oldDatas = this.datas;
this.datas = new ArrayList<ReportData>();
}
commit(oldDatas);
}
protected void commit(List<ReportData> datas)
{
LOGGER.info("commit batch report data[" + datas.size() + "]");
try
{
this.targetBatchResultReporter.reportBatch(datas);
} catch (Exception e)
{
LOGGER.error("Error When commit batch datas", e);
}
LOGGER.info("commit success");
}
}
6.工厂模式
如何将上面这些各种各样的Reporter创建出来呢,我们可以选择使用简单xml配置的方式来管理这些Reporter并创建,我们也可以依赖于其他的创建工厂,譬如Spring,这就是工厂模式
工厂接口如下
public interface ReporterFactory
{
ResultReporter createReporter(String name);
}