说明
讲师:李智慧
JUnit 中的设计模式
如何写单元测试
public class BubbleSorterTests extends TestCase {
private Integer[] array;
private Sorter sorter;
protected void setUp() {
array = new Integer[] { 5, 4, 9, 7, 6, 3, 8, 1, 0, 2};
sorter = new BubbleSorter();
}
public void testSort() {
Sortable sortable = new ArraySortable(array);
Comparator comparator = new IntegerComparator();
sorter.sort(sortable, comparator);
for (int i = 0; i < 10; i++ ) {
assertEquals(i, array[i].intValue());
}
}
}
实现一个单元测试的步骤
创建测试类, 从 TestCase 派生
初始化
- 覆盖基类的方法:
protected void setUp()
消除环境
- 覆盖基类的方法:
protected void tearDown()
书写测试方法
- 命名规则:
public void testXyz()
JUnit 单元测试是如何执行的?
public abstract class TestCase extends Assert implements Test {
public boid runBare() throws Throwable {
Throwable exception = null;
setUp();
try {
runTest();
} catch (Throwable running) {
exception = running;
} finally {
try {
tearDown();
} catch (Throwable tearingDown) {
if (exception == null) exception = tearingDown;
}
}
if (exception != null) throw exception;
}
protected void runTest() throws Throwable {
// 利用动态机制调用 testXyz()
}
protected void setUp() throws Exception { }
protected void tearDown() throws Exception { }
}
JUnit 单元测试的执行
Idea 中运行单元测试通过的图片。
JUnit 单元测试的执行时序图
这里用到了三个策略模式:
- Eclipse定义了接口,策略的接口,策略的实现。定义一个plugin的规范。显示有多少个用例通过,多少个用例失败。
- Eclipse 调用runBare方法,调用TestCase。
- TestCase只有一个,实现有多个,XyzTests有多个。
模板方法模式(Template Method)
模板方法模式是扩展功能的最基本模式之一
- 它是一种 ”类的行为模式“
它是通过 ”继承“ 的方法来实现扩展
- 基类负责算法的轮廓和骨架
- 子类负责算法的具体实现
组合 vs. 继承
- 基于 ”继承“ 的模板方法比 ”组合“ 更容易实现
- 在很多情况下,可以适当使用这种模式。
模板方法的形式
抽象方法
protected abstract void step1();
- 强制子类实现该步骤。
具体方法
protected void doSomething() { ... }
- 子类不需要覆盖,但也可以覆盖之。
- 如想明确告诉子类 ”不要覆盖它“,最好标明:
final
钩子方法
protected void setUp() { }
- 空的实现(缺省适配器模式)
- 子类可选择性地覆盖之,以便在特定的时机做些事。
Java Servlet 中的模板方法
Java Servlet中有两个模板方法
- 模板方法:
service()
判断使用哪种方式处理网络请求GET, POST, PUT等。 Servlet interface
也是个模板方法,因为Servlet的生命周期方法,一定是固定的。
测试 Sortable
public abstract class SortableTests extends TestCase {
protected Sortable<Integer> sortable;
protected void setUp() {
Integer[] data = new Integer[10];
for (int i = 0; i < 10; i++) {
data[i] = i;
}
sortable = createSortable(data);
}
protected abstract Sortable<Integer> createSortable(Integer[] data);
public final void testGet() {
for (int i = 0; i < 10; i++ ) {
assertEqual(i, sortable.get(i).intValue());
}
try {
sortable.get(-1);
fail();
} catch (RuntimeException e) {
}
}
public final void testSet() {
for (int i = 0; i < 10; i++) {
sortable.set(i, 100);
assertEquals(100, sortable.get(i).intValue());
}
try {
sortable.set(-1, 999);
fail();
} catch (RuntimeException e) {
}
try {
sortable.set(10, 999);
fail();
} catch (RuntimeException e) {
}
}
public final void testSize() {
assertEquals(10, sortable.size());
}
}
测试 ArraySortable
public class ArraySortableTests extends SortableTests {
@Override
protected Sortable<Integer> createSortable(Integer[] data) {
List<Integer> list = Arrays.asList(data);
return new ListSortable<Integer>(list);
}
}
测试 ListSortable
public class ListSortableTests extends SortableTests {
@Override
protected Sortable<Integer> createSortable(Integer[] data) {
return new ArraySortable<Integer>(data);
}
}
测试排序程序类图
策略模式(Strategy)
策略模式是扩展功能的另一种最基本的模式
- 它是一种 ”对象的行为模式“
它是通过 ”组合“ 的方法来实现扩展
什么时候使用策略模式?
系统需要在多种算法中选择一种
重构系统时
- 将条件语句转换成对于策略的多态性调用
策略模式的优点(对比模板方法)
- 将使用策略的人与策略的具体实现分离
- 策略对象可以自由组合
策略模式可能存在的问题:
- 策略模式仅仅封装了 ”算法的具体实现“,方便添加和替换算法。但它并不关心何时使用何种算法,这个必须由客户端来决定。
策略模式和模板方法的结合
参数化的单元测试
public abstract class ComparatorTests<T> extends TestCase {
protected T o1;
protected T o2;
protected boolean ascending;
protected boolean isBefore;
public ComparatorTests(T o1, T o2, boolean ascending, boolean isBefore) {
super("testIsBefore");
this.o1 = o1;
this.o2 = o2;
this.ascending = ascending;
this.isBefore = isBefore;
}
public void testIsBefore() {
assertEquals(isBefore, createComparator(ascending).isBefore(o1, o2));
}
protected abstract Comparator<T> createComparator(boolean ascending);
}
public class IntegerComparatorTests extends ComparatorTests<Integer> {
public static Test suite() {
TestSuite suite = new TestSuite("IntegerComparatorTests");
suite.addTest(new IntegerComparatorTests(1, 1, true, false));
suite.addTest(new IntegerComparatorTests(1, 2, true, true));
suite.addTest(new IntegerComparatorTests(2, 1, true, false));
suite.addTest(new IntegerComparatorTests(1, 1, false, false));
suite.addTest(new IntegerComparatorTests(1, 2, false, false));
suite.addTest(new IntegerComparatorTests(2, 1, false, true));
return suite;
}
public IntegerComparatorTests(Integer o1, Integer o2, boolean ascending, boolean isBefore) {
super(o1, o2, ascending, isBefore);
}
@Override
protected Comparator<Integer> createComparator(boolean ascending){
}
}
上述代码生成一个 ”测试包“
生成更复杂的 ”测试包“
public class AllTests {
public static Test suite() {
TestSuite suite = new TestSuite("sort");
suite.addTestSuite(BubbleSorterTests.class);
suite.addTestSuite(InsertionSorterTests.class);
suite.addTestSuite(ArraySorterTests.class);
suite.addTestSuite(ListSorterTests.class);
suite.addTestSuite(IntegerComparatorTests.class);
suite.addTestSuite(ComparableComparatorTests.class);
return suite;
}
}
组合模式(Composite)
组合模式
- 是一种 ”对象的结构模式“
组合模式的应用
- 文件系统
- AWT 控件
测试排序程序的性能
冒泡排序和插入排序,谁更快?
- 这种测试必须重复多次(如10,000次)侧能比较准确地计算出性能。
- 如何让 BubbleSorterTests 和 InsertionSorterTests 重复运行多次,而不需要修改他们的代码?
- 如何计算时间?
运用 JUnit 扩展包中的辅助类:
junit.extensions.TestSetup
junit.extensions.RepeatedSetup
性能测试程序
public class PerformanceTests extends TestSetup {
private long start;
private int repeat;
public PerformanceTests() {
super(new RepeateedTest(test, repeat));
this.repeat = repeat;
}
public void setUp() throws Exception {
start = System.currentTimeMillis();
}
protected void tearDown() throws Exception {
long duration = System.currentTimeMillis() - start;
System.out.printf("%s repeated %d times, takes %d ms\n", getTest(), repeat, duration);
}
public static Test suite() {
TestSuite suite = new TestSuite("performance");
Test bubbleTests = new TestSuite(BubbleSorterTests.class);
Test insertionTests = new TestSuite(InsertionSorterTests.class);
suite.addTest(new PerformanceTests(bubbleTests, 10000));
suite.addTest(new PerformanceTests(insertionTests, 10000));
return suite.
}
}
性能测试核心源码
public class TestDecorator extends Assert implements Test {
protected Test fTest;
public TestDecorator(Test test) {
fTest = test;
}
@Override
public void run(TestResult result) {
for (int i = 0; i < fTimesRepeat; i++) {
if (int i = 0; i < fTimesRepeat; i++) {
if (result.shouldStop()) {
break;
}
}
super.run(result);
}
}
}
装饰器模式(Decorator)
装饰器模式
- 是一种 ”对象的结构模式“
装饰器模式的作用
- 在不改变对客户端的接口的前提下(对客户端透明)
- 扩展现有对象的功能
- 思考 PerformanceTests 的客户端是指谁?
装饰器模式也被笼统地称为 ”包装器“(Wrapper)
- 适配器也被称作 ”包装器“,区别在于适配器是转换成另一个接口,而装饰器是保持接口不变。
- 包装器形成一条 ”链“。
装饰器模式 ”包装器“例子(Wrapper)
- 明月装饰了梦装饰了你
- 梦装饰了明月装饰了你
public interface Anything {
void exe();
}
public class Moon implements Anything {
private Anything a;
public Moon(Anything a) {
this.a = a;
}
public void exe() {
System.out.print("明月装饰了");
a.exe();
}
}
public class Dream implements Anything {
private Angthing a;
public Dream(Anything a) {
this.a = a;
}
publci void exe() {
System.out.print("梦装饰了");
a.exe();
}
}
public class You implements Anything {
private Angthing a;
public You(AnyThing a) {
this.a = a;
}
public void exe() {
System.out.print("你");
}
}
public class MainClass {
public static void main(String[] args) {
Anything t = new Moon(new Dream(new You(null)));
t.exe();
// 明月装饰了梦装饰了你
Anything t1 = new Dream(new Moon(new You(null)));
t1.exe();
// 梦装饰了明月装饰了你
}
}
装饰器模式的优缺点
装饰器和模板方法、策略模式的比较
- 装饰器保持对象的功能不变,扩展其外围的功能。
- 模板方法和策略模式则保持算法的框架不变,而扩展其内部的实现。
装饰器和继承的比较
- 都可以用来扩展对象的功能
- 但装饰器是动态的,继承是静态的
- 装饰器可以任意组合
☞ 但这也使装饰器更复杂,有可能会组合出荒谬的结果
装饰器模式的应用
Java Servlet 中的应用
HttpServletRequest
/HttpServletRequestWrapper
HttpServletResponse
/HttpServletResponseWrapper
同步化装饰器
Collections.synchronizedList(list)
- 取代原先的
Vector
、Hashtable
等同步类。
Java I/O 类库简介
- 核心 - 流, 即数据的有序排列,将数据从源送达目的地。
- 流的种类
☞InputStream
、OutputStream
- 代表byte
流 (八位字节流)
☞Reader
、Writer
- 代表char
流(Unicode
字符流) - 流的对称性
☞ 输入 - 输出对称
☞Byte
-Char
对称
☞ 因此我们只要学习任意一种流,就可以基本了解其它所有的流。
依赖注入 DI 与控制反转 IOC
DI、 IOC 的应用例子
public class Client {
private UserService userService;
public setUserService(UserService userService) {
this.userService = userService;
}
}
<bean id="userService" class="com.hello.UserDaoImpl">
<property name="userDao" ref="userDao" />
</bean>
<bean id="client" class="com.hello.Client">
<property name="userService" ref="userService" />
</bean>
DI、 IOC 的核心源码
private static void parseBeanElement() throws Exception {
String id = beanElement.attributeValue("id");
String clsName = beanElement.attributeValue("class");
// 获取 Class 对象
Class<?> cls = Class.forName(clsName);
// 直接调用无参数构造函数,实例化一个对象
Object beanObj = cls.getDeclaredConstructor().newInstance();
beanMap.put(id, beanObj);
// 获取属性节点,并调用 setter 方法设置属性
List<Element> subElement = beanElement.elements();
for (Element subElem: subElemList) {
// 获取属性名称
String name = subElem.attributeValue("name");
// 获取属性值
String ref = subElem.attributeValue("ref");
Object refObj = beanMap.get(ref);
// 根据属性名称构造 setter 方法名: set + 属性首字母大写 + 属性其它字符,例: setUserDao
String methodName = "set" + (char)(name.charAt(0) - 32) + name.substring(1);
// 获取 Method 对象
Method method = cls.getDeclaredMethod(methodName, refObj.getClass().getInterface()[0]);
// 调用 setter 方法,设置对象属性
method.invoke(beanObj, refObj);
}
}
Spring 中的单例模式
private final Map singletonObjects = new HashMap();
protected Object getSingleton(String beanName) {
// 检查缓存中是否存在实例
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// 如果为空,则锁定全局变量并进行处理。
synchronized (this.singletonObjects) {
// 调用工厂的 getObject 方法
singletonObject = singletonFactory.getObjet();
// 记录在缓存中
this.earlySingletonObjects.put(beanName, singletonObject);
}
}
return (singletonObject != NULL_OBJECT ? singletonObject : null);
}
单例怎么获取: beanMap.get(ref)
Spring MVC 模式
@RestController
@RequestMapping("/user/")
public class QueryUserController extends FlowerController {
@Autowired
OrderNoService orderNoService;
@RequestMapping(value = "query")
public void hello(String userId) {
logger.info("gain request: {}", userId);
doProcess(userId);
}
}
public boolean handle(ServletRequest req, ServletResponse res) {
String uri = ((HttpServletRequest) req).getRequestURI;
Object[] parameters = new Object[args,length];
for (int i = 0; i < args.length; i++) { //
parameters[i] = req.getParameter(args[i]);
}
Object ctl = controller.newInstance(uri);
Object response = method.invoke(ctl, parameters);
res.getWriter().println(response.toString());
return true;
}
SQL in Hadoop Eco-system
- Hive
- Impala
- Presto
- Phoenix
- Shark
Hive Architecture
An analytical SQL engine for MapReduce
Exists Case Study
SQL 转换为语法树
复杂的问题通过装饰者模式变得简答
transformer 把Oracle SQL转换为join
generator 把Oracle SQL转换为Hive SQL
开闭原则
难的问题解决了,后人就比较容易理解了。
比如爱因斯坦的相对论,现在高中生都在学习。
牛顿力学,现在初中生都在学习。
复杂的问题理解了,就算前任理解了,后人理解还是比较难理解复杂的问题。
注意:以上信息如有侵权,请联系作者删除,谢谢。