在日常开发功能时,同一种功能可能会有多种实现方式。我们需要做一个取舍。
最常见的条件就是性能、可读性、可维护性。
本篇文章,我们主要讨论“性能”。
假设我们现在需要计算一段代码的运行时间。
最常见的写法是,在执行这段代码前,获得一下当前的时间戳,在执行这段代码后,获取一下当前的时间戳,然后俩时间相减,就是花费时间了。
但有时,我们需要将这段代码执行多次。
这种场景是,执行的时间戳很小,没有可比性。比如执行一段代码,运行时间是 0 或者 2毫秒。
这种时候你可能会说,那就用一个for循环,执行多次,计算平均时间就好了。
问题来了,如果这种相似的操作,写的多了呢,用的地方很多呢?
我们现在就需要将它给写成一套模板,只需要简单的填充参数,调用,就能实现上述的功能。
这个接口主要是用于填充代码段,因此设计为函数式接口,方便调用。
package org.feng.calc;/*** 运行的方法内容:使用Lambda** @version V1.0* @author: fengjinsong* @date: 2022年11月15日 19时56分*/
@FunctionalInterface
public interface MethodBody {void run();
}
package org.feng.calc;import lombok.Data;import java.util.ArrayList;
import java.util.List;/*** 计算执行时间的结果保存** @version V1.0* @author: fengjinsong* @date: 2022年11月15日 18时59分*/
@Data
public class CalcExecuteTimeResult {/*** 执行代码花费的时间*/private List costTime;public CalcExecuteTimeResult(int size) {costTime = new ArrayList<>(size);}
}
package org.feng.calc;import lombok.Getter;import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;/*** 执行模板** @version V1.0* @author: fengjinsong* @date: 2022年11月15日 19时58分*/
public class ExecuteTemplate {private final List methodBodyList;@Getterprivate CalcExecuteTimeResult calcExecuteTimeResult;public ExecuteTemplate() {this.methodBodyList = new ArrayList<>();}public void addMethod(MethodBody methodBody) {methodBodyList.add(methodBody);}/*** 执行** @param timeUnit 时间单位* @param frequency 频次-单个方法{@link MethodBody}实例执行的次数*/void process(TimeUnit timeUnit, long frequency) {this.calcExecuteTimeResult = new CalcExecuteTimeResult(methodBodyList.size());List costTime = calcExecuteTimeResult.getCostTime();for (MethodBody methodBody : methodBodyList) {long startTime = getTime(timeUnit);for (int i = 0; i < frequency; i++) {methodBody.run();}long endTime = getTime(timeUnit);costTime.add(endTime - startTime);}}private long getTime(TimeUnit timeUnit) {if (!SUPPORTED_TIME_UNIT.contains(timeUnit)) {throw new UnsupportedOperationException("不支持的时间单位:" + timeUnit);}if (TimeUnit.NANOSECONDS.equals(timeUnit)) {return System.nanoTime();}return System.currentTimeMillis();}/*** 当前支持的时间单位*/private static final Set SUPPORTED_TIME_UNIT = Set.of(TimeUnit.MILLISECONDS, TimeUnit.NANOSECONDS);
}
package org.feng.calc;import lombok.Setter;
import lombok.experimental.Accessors;import java.util.concurrent.TimeUnit;/*** 计算执行时间-上下文** @version V1.0* @author: fengjinsong* @date: 2022年11月15日 19时51分*/
@Setter
@Accessors(chain = true)
public class CalcExecuteTimeContext {private TimeUnit timeUnit;private Long frequency;private ExecuteTemplate executeTemplate;public CalcExecuteTimeResult run() {executeTemplate.process(timeUnit, frequency);return executeTemplate.getCalcExecuteTimeResult();}public void addMethod(MethodBody methodBody){executeTemplate.addMethod(methodBody);}
}
package org.feng.calc;import java.util.concurrent.TimeUnit;/*** 测试运行** @version v1.0* @author: fengjinsong* @date: 2022年11月15日 21时03分*/
public class Client {public static void main(String[] args) {// 初始化上下文:设置每个方法执行的次数,以及计算时间时使用的时间单位,执行模板CalcExecuteTimeContext context = new CalcExecuteTimeContext().setFrequency(2L).setTimeUnit(TimeUnit.MILLISECONDS).setExecuteTemplate(new ExecuteTemplate());context.addMethod(() -> {System.out.println(111);System.out.println(111);System.out.println(111);System.out.println(111);System.out.println(111);});context.addMethod(() -> {System.out.println(222);System.out.println(222);System.out.println(222);System.out.println(222);System.out.println(222);});// 计算得到方法运行的结果CalcExecuteTimeResult executeTimeResult = context.run();System.out.println(executeTimeResult);}
}
运行结果:
111
111
111
111
111
111
111
111
111
111
222
222
222
222
222
222
222
222
222
222
CalcExecuteTimeResult(costTime=[0, 1])