极客大学架构师训练营 框架开发 模式与重构 JUnit、Spring、Hive 核心源码解析 第 6 课

用户头像
John(易筋)
关注
发布于: 2020 年 06 月 25 日

说明



讲师:李智慧



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 单元测试的执行时序图



这里用到了三个策略模式:

  1. Eclipse定义了接口,策略的接口,策略的实现。定义一个plugin的规范。显示有多少个用例通过,多少个用例失败。

  2. Eclipse 调用runBare方法,调用TestCase。

  3. TestCase只有一个,实现有多个,XyzTests有多个。



模板方法模式(Template Method)

模板方法模式是扩展功能的最基本模式之一

  • 它是一种 ”类的行为模式“



它是通过 ”继承“ 的方法来实现扩展

  • 基类负责算法的轮廓和骨架

  • 子类负责算法的具体实现



组合 vs. 继承

  • 基于 ”继承“ 的模板方法比 ”组合“ 更容易实现

  • 在很多情况下,可以适当使用这种模式。



模板方法的形式

抽象方法

  • protected abstract void step1();

  • 强制子类实现该步骤。



具体方法

  • protected void doSomething() { ... }

  • 子类不需要覆盖,但也可以覆盖之。

  • 如想明确告诉子类 ”不要覆盖它“,最好标明: final



钩子方法

  • protected void setUp() { }

  • 空的实现(缺省适配器模式)

  • 子类可选择性地覆盖之,以便在特定的时机做些事。



Java Servlet 中的模板方法

Java Servlet中有两个模板方法

  1. 模板方法: service() 判断使用哪种方式处理网络请求GET, POST, PUT等。

  2. 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)

  • 取代原先的 VectorHashtable 等同步类。



Java I/O 类库简介

  • 核心 - 流, 即数据的有序排列,将数据从源送达目的地。

  • 流的种类

InputStreamOutputStream - 代表 byte 流 (八位字节流)

ReaderWriter - 代表 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



开源地址:

https://github.com/zhihuili/project-panthera-ase/tree/master/ql/src/java/org/apache/hadoop/hive/ql/parse/sql



An analytical SQL engine for MapReduce

Exists Case Study

SQL 转换为语法树

复杂的问题通过装饰者模式变得简答

transformer 把Oracle SQL转换为join

generator 把Oracle SQL转换为Hive SQL



开闭原则



难的问题解决了,后人就比较容易理解了。

比如爱因斯坦的相对论,现在高中生都在学习。

牛顿力学,现在初中生都在学习。



复杂的问题理解了,就算前任理解了,后人理解还是比较难理解复杂的问题。



注意:以上信息如有侵权,请联系作者删除,谢谢。



发布于: 2020 年 06 月 25 日 阅读数: 49
用户头像

John(易筋)

关注

问渠那得清如许?为有源头活水来 2018.07.17 加入

工作10+年,架构师,曾经阿里巴巴资深无线开发,汇丰银行架构师/专家。开发过日活过亿的淘宝Taobao App,擅长架构、算法、数据结构、设计模式、iOS、Java Spring Boot。易筋为阿里巴巴花名。

评论 (1 条评论)

发布
用户头像
Mark 最近在做junit全覆盖
2020 年 06 月 26 日 01:39
回复
没有更多了
极客大学架构师训练营 框架开发 模式与重构 JUnit、Spring、Hive核心源码解析 第6课