說明
講師:李智慧
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
開閉原則
難的問題解決了,後人就比較容易理解了。
比如愛因斯坦的相對論,現在高中生都在學習。
牛頓力學,現在初中生都在學習。
複雜的問題理解了,就算前任理解了,後人理解還是比較難理解複雜的問題。
注意:以上信息如有侵權,請聯繫作者刪除,謝謝。