極客大學架構師訓練營 框架開發 模式與重構 JUnit、Spring、Hive核心源碼解析 第6課 聽課總結

說明

講師:李智慧

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

開閉原則

難的問題解決了,後人就比較容易理解了。
比如愛因斯坦的相對論,現在高中生都在學習。
牛頓力學,現在初中生都在學習。

複雜的問題理解了,就算前任理解了,後人理解還是比較難理解複雜的問題。

注意:以上信息如有侵權,請聯繫作者刪除,謝謝。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章