2015年10月17日 星期六

Android Unit Test (一)

Android Unit Test (一)

由於最近上了SkillTreeTDD課程,加上學費也不少…沒有整理的話應該很快就會忘,所以我就嘗試以Android 的方式來重現上課的內容。觀念會稍微提到而已,最主要的內容會是實做為主。


Unit Test 的觀念

這些觀念其實網路上都有很多資源可以查的到,在這裡只是提供關鍵字

3A 原則

  • Arrange : 初始化
  • Act : 執行測試動作
  • Assert : 驗證

FIRST

  • Fast : 快速驗證
  • Independent : 測試之間不會有相依關係
  • Repeatable : 每次執行的測試都是會是一樣的
  • Self-Validating : 能自我驗證
  • Timely : 測試碼要比production code 早完成

Android 建置環境

接下來的幾篇都會以下列的環境為主

  • Android Studio 1.4
  • JDK 7

開始第一個Unit Test

這邊要驗證的是一個簡單的加法,為了快速上手。暫時不採用TDD的做法(先寫測試)

1. 建立空白專案

要開甚麼專案都行,Android TV , Android Wear 也沒關係,本節重點是放在測試程式

2. 建立 MyMath 類別並實做加法


public class MyMath {

    public int add(int first, int second){
        return first + second;
    }
}

3. 建立測試類別

請見下圖,Android Studio 專案的測試程式預設是放在 src/androidTest/java/(packageName) 底下,在這裡建立一個”MyMathTest”的類別

4. 撰寫測試程式碼

要驗證加法是不是正確,我們需要輸入實際輸出、以及期待的輸出結果。在這個例子中,輸入是12,期待的輸出結果是3,要是實際輸出等於期待輸出,本次測試就會通過。

public class MyMathTest extends AndroidTestCase{

    public void test_add_first_1_second_2_equals_3(){
        //arrange
        int first = 1;
        int second = 2;
        int expect = 3;
        MyMath myMath = new MyMath();

        //act
        int actual = myMath.add(first, second );

        //assert
        assertEquals(expect, actual);
    }

}

Tip:

  • 為了能夠運行測試,MyMathTest 要繼承AndroidTestCase
  • assert為單元測試的基礎,assert的結果會決定這次測試的是否通過,在本例中expect 與 actual 值一樣的話 assertEquals()就會通過

5. 執行測試

可選擇使用模擬器或實機來執行測試

等待一段時間後,可以看到綠燈,代表測試通過!!


2015年4月11日 星期六

Android 魔術方塊開發筆記(四) 投影與旋轉

投影

不知道各位有沒有注意到一件事
上一章的正方形不是正的是長的!!

造成這個的原因
就是因為我們的GLSurfaceView本來就是長的
畫出來的正方形就會因此而拉長!!


(取自google API guide)

解決方法其實也很簡單
只要將我們的座標點
經由矩陣運算投影
就能畫出真正的正方形

1. 更改shader code

    我們可以將座標與矩陣的運算交給底層,JAVA層的運算越少越好
    在GLShaderFactory做了以下修改:



    MVP 是 Model View Projection的縮寫
    在這裡將uMVPMatrix帶進去做運算

2. 建立Projection Matrix


    在GLES20Randerer 的 onCreate有View的長寬資訊:width, height
    將width, height相除得到長寬的比值ratio
    接著呼叫Native function : Matrix.frustumM(float[] m, int offset, float left, float right, float bottom, float top, float near, float far)
    得到4*4矩陣mProjectionMatrix


攝影機

接下來我們可以決定從哪裡觀察
下面這張圖可以讓大家有個初步的概念
這裡一樣是矩陣運算

3. 建立 View Matrix

    Matrix.setLookAtM(...)可以很簡單的建立上述的矩陣

4. 組合MVP Matrix

   要注意矩陣是沒有交換律的,矩陣相乘順序不能放錯
   完整程式碼:

5. 改寫Square

   這邊其實也沒做太大的變動
   只有取得uMVPMatrix的handle
   並將我們計算得到的MVP Matrix傳給底層

   如此一來已經大功告成,正常顯示出一個正方形了
   *可以更改看看frustumM(), setLookAtM()的參數,會更加了解上面這幾張圖所代表的意義

旋轉

直到現在都是很無聊的一張圖
加個旋轉讓他動起來吧!!

6. 建立Rotation Matrix

Matrix一樣提供了一個簡單的函式來計算旋轉矩陣


這樣就有動畫效果啦!!

Source Code:
Version : e013248db0

Reference:



2015年4月6日 星期一

Android 魔術方塊開發筆記(三) 繪製多邊形



1.繪製多邊形有以下三種方法:


上圖的數字代表繪圖的順序
也就是我們給OpenGL  ByteBuffer的順序

跟三角形一樣
先定義出正方形四個點的座標
然後其餘程式碼不變


對照上圖,top left 為編號1
                    bottom left 為編號2
                    bottom right 為編號3
                    top right 為編號4

依GL_TRIANGLE_FAN 的繪圖次序就會繪製出一個正方形
以下分別為GL_TRIANGLES以及GL_TRIANGLE_STRIP的畫面 


如果要讓GL_TRIAGLES 也能顯示正方形,
需要在squareCoords[] 中加入bottom right, top left兩點
 
GL_TRIANGLE_STRIP則是squareCoords[]中的順序調一下即可

2.glDrawElements()

除了glDrawArrays()之外,
OpenGL還提供了另外一個繪製方法glDrawElements
這邊的順序就由我們自己來定義
跟vertex相同,需要一個byteBuffer將值傳給OpenGL


結果:



完整程式碼:

Source Code:
版本:a3ffd93481

Reference:


2015年4月5日 星期日

Android 魔術方塊開發筆記(二) shader以及繪製三角形

接下來這邊對C不熟悉的人可能會痛苦一點了
OpenGL ES的語法其實都是直接CALL native Function
跟JAVA的思維不一樣

1. 建立3D座標

這裡所繪製的是3維座標,
我們要設定一點就必須給他x,y,z座標
以下triangleCoords就代表了一個三角形的3個點的座標

要繪製圖形除了座標之外還需要顏色
跟Android resource的順序有點不太一樣,這邊是RGBA
而且範圍在0.0~1.0之間

Android 底層閱讀Byte的順序可能與Java不一樣
我們要傳送給Opengl就要先轉成ByteBuffer
並設定為native order
才能讓Opengl理解

2. OpenGL ES Shading Language


如上圖
Shading Language會在橘色區塊中執行
這邊已經很底層的元件了
我們必須將他撰寫成String
再交給OpenGL去編譯
----> GLES20.glCompileShader(shader)

這邊所使用的是最基本用法

SHADER_CODE_VERTEX
用來處理點座標
SHADER_CODE_FRAGMENT
用來處理渲染效果

這邊的vPosition, vColor是我們定義的變數名稱
用來連結openGL ES及我們撰寫的java

gl_Position, gl_FragColor則是openGL ES所認得的變數名稱
我們經過運算後必需要傳到這些變數
才能顯示出來

其他部分有興趣的可以參考SPEC


如果只有接觸過上層程式語言的人可能會很納悶
為什麼這邊都是用int來進行操作
其實這邊的int可以把它當成物件的ID
OpenGL ES 將會根據這些ID做相對應的處理

這邊就不一行一行解說
從method的名稱就大概知道裡面的作用了

完整程式碼:

3. 建立Triangle class

這邊就結合上面2步
連結我們的3D座標以及Shader

這邊直接看draw()
mProgram 是剛剛GLShaderFactory裡得到的ID
藉由他我可以拿到vPosition, vColor的ID(positionHandle, colorHandle)
再來呼叫glVertexAttribPointer(),glUniform4fv()
將數值帶進去
最後呼叫glDrawArrays()
三角形就出來啦~~

還有一步



Android 魔術方塊開發筆記(一) OPENGL ES 的 hello world

有鑑於網路上有關於android OpenGL的資源並不多(幾乎都英文)
再加上我想要做這個已經想很久了
於是我就來邊開發邊寫一下筆記
希望能夠幫助到想學習OpenGL ES的初學者
(也希望我會堅持寫下去...)

小小介紹一下OpenGL ES

OPENGL ES(OpenGL for Embedded Systems)

是專門為嵌入式系統設計的OpenGL
API用法其實跟原來版本的OpenGL沒差多少
Android 中所有畫面其實也是由OpenGL ES畫出來的

1. 建立專案

    在這裡我使用的是Android studio,開啟新Blank Activity,這裡就不詳細描述了

2. 檢查OpenGL 版本

    本專案我以OpenGL 2.0為主
    下圖來自Android Developers官網(2015/4/5)
    https://developer.android.com/about/dashboards/index.html?utm_source=suzunone
    可見現在所有的裝置已經至少都2.0以上了
    網路上1.0的舊資料基本上可以跳過不用看了


    上述程式碼用來檢查手機的GLES版本
    如果熟悉Android的開發者應該馬上就知道這邊做了甚麼事
    參考一下官方文件:

public int reqGlEsVersion

Added in API level 4
The GLES version used by an application. The upper order 16 bits represent the major version and the lower order 16 bits the minor version.
   簡單的來說2開頭就是支援到2.0,3開頭支援到3.0

   知道了GLES版本後我們就來做個小小的限制
   讓不支援2.0版本的裝置無法安裝
   在AndroidManifest.xml 裡加入


3. GLSurfaceView

    顯示文字我們用TextView, 
    要顯示OpenGL 就要使用GLSurfaceView了
    跟一般Android APP一樣
    第一步先建立好xml檔

    

    再來在MainActivity加入下列程式碼


    這邊有一個重點
    GLSurfaceView 一定要搭配GLSurfaceView.Renderer才能渲染出圖形
    這個Renderer的責任包含了視角的轉換以及圖形的繪製等等
    然而原始的GLSurfaceView.Renderer是abstract class
    我們必須實做他所有的Methods

4. Renderer

    總共要實做3個Method
    onSurfaceChanged(GL10 gl, int width, int height) : surface 改變大小時呼叫
    onSurfaceCreated(GL10 gl, EGLConfig config) : surface 建立時呼叫
    onDrawFrame(GL10 gl) : 繪圖時呼叫

    *當手機進行休眠狀態時,onSurfaceCreated會去重新呼叫一次,
      如果想避免他重新呼叫,
      就要在GLSurfaceView初始化時setPreserveEGLContextOnPause(boolean) 設為true
      並且在Activity 的onPause(),onResume()中加入
      GLSurfaceView.onPause(), GLSurfaceView.onResume()

    由於我們使用的是2.0版本,已經不需要GL10這參數
    我參(ㄊㄡ)考(ㄌㄞˊ)了網路上的高手
    改寫成以下版本



    改寫成這版本後只需要注意兩件事
    1. 初始化時需要做甚麼
    2. 每個Frame我們應該要Show甚麼

5. 執行

    以上做完後就可以得到一個國防布啦
    下一篇就會開始繪製圖形


    


SourceCode:
版本號671fb71eaa

Reference:



2014年9月14日 星期日

TTD note: mock, stub and fake


坦白說我英文不好,
導致mock跟stub在看網路教學時都看不懂
A網站說是這樣,可是在B網站得到不一樣解釋
到底誰說的是對的
根本就不知道
於是我就去找了線上辭典對這些字的定義
然後就豁然開朗!!!!



1.mock
noun
7.
a contemptuous or derisive imitative action or speech; mockery orderision
    輕蔑                       嘲笑
8.
something mocked or derided; an object of derision.
9.
an imitation; counterfeit; fake.
                     假冒
2.stub
noun
1.
a short projecting part.
2.
a short remaining piece, as of a pencil, candle, or cigar.
3.
(in a checkbook, receipt book, etc.) the inner end of each leaf, forkeeping a record of the content of the part filled out and torn away.
4.
the returned portion of a ticket.
5.
the end of a fallen tree, shrub, or plant left fixed in the ground;stump.
6.
something having a short, blunt shape, especially a short-pointed,blunt pen.
3.fake
noun
9.
anything made to appear otherwise than it actually is; counterfeit:
This diamond necklace is a fake.
10.
a person who fakes; faker :
The doctor with the reputed cure for cancer proved to be a fake.
11.
a spurious report or story.
12.
Sports. a simulated play or move intended to deceive an opponent.

簡單的來說:
stub是只有一部分原功能的物件
mock就是可以跟你周旋,把你騙得團團轉的物件
fake就是你知道他現在正確的行為一定是怎樣,但是會因為時間、地點、裝置等因素不得不去做一個假的東西





後記:
對任何新觀念不要先去背(這就是這樣、那個就是那樣)
傳統教育就是這樣教的
應該要先了解為什麼會是選這個字
他所代表的概念、語意是甚麼
然後才是去了解它的用法
最後再思考
是否兩邊有吻合
如果沒有
趕快去找其他資源
一定有其中一邊錯了
不要被誤導
過了很久被人提醒才知道
原來我被網路上XXX寫的東西騙了好幾年

ref:

30天快速上手TDD Day 7 - Unit Test - Stub, Mock, Fake 簡介

Dictionary.com

2014年9月9日 星期二

Design Patterns Explained: A New Perspective Object-Oriented Design (一)

這本書跟一般講design pattern 的書不同
他帶領讀者思考pattern背後的基本原則和動機
不只學習design pattern,對OOP也會有更上一層的理解

繼承、封裝、多型

對這三個物件導向基礎提供了不同於常的見解
下面介紹這本書中一直強調的重心(個人見解)



1.single responsibility principle

簡單的來說就是一個物件只做一件事
這概念很簡單,但是要實行起來卻有一定的難度
像是clean code中,說了盡量不要使用switch
"因為這樣你就做了超過一件事"

??????????????????????????????

這是我看這一句當下的反應....
超過一件事??我看不出來阿??我覺得只做了一件事
但是仔細想想,通常我是怎麼用switch的
裡面塞東塞西(new 新物件、調整UI、改一下flag、加個if else)
甚至在別的地方又遇到同樣問題
然後就把要switch的變數設成global
繼續switch.....

之後沒完沒了,萬一有新需求
這裡加加,那裏減減
漏東漏西,要測試很多次才能確定還有沒有BUG
所以!!!!!!
這就是design pattern強大的地方了


2.encapsulation

這本書中一再強調封裝不只是public, protect, private
這只是以直觀的角度去思考封裝
封裝的概念可以更加抽象
我可以不知道你是甚麼東西(對物件封裝),也可以不知道你怎麼做(對行為封裝)
總之自己去做自己的事就對了
其他不需要知道的東西
全  部  都  藏  起  來



3.program to interface not implementation

這句話我一開始也是看不懂
只寫界面程式就可以跑了嗎???

現在舉個例子
設計出一個暗棋遊戲

在program to implementation中我會:
建立一個物件 chess
其中有屬性 name 表示他是帥還是象、車
                     image 表示他的圖像

好了設計完成!!
在main中建立全部棋子,通通用array存起來當成棋盤
再全部打亂,就可以開始玩拉
怎麼判斷誰可以吃誰?
就全部用if就好啦,每個棋子標上數字,誰大就誰贏
簡單又方便
每吃完一個就檢查一下是不是都吃完了
遊戲結束!!



這樣設計有沒有問題??
不會阿   怎麼可能會有問題

但是今天我想玩一個新玩法:
兵跟卒兩隻以上就可以合體成超級士兵(但是只能存在一回合):

兵 相                     相              相                     相
兵 士       =>    超 士     =>     超          =>    

完了,要改一堆if`,不然就乾脆另外寫一個新的遊戲規則比較快



你沒辦法要求不會有新需求
所以只能盡早的因應即將到來的需求



那如果是program to interface我會怎麼做?

首先想好這遊戲會有哪些功能、責任
1.棋子
2.棋子攻擊的規則
3.棋子行走的規則
4.棋盤
5.遊戲初始設定
6.遊戲勝利條件

這時候我又發現棋盤其實根本不用知道誰可以吃誰、誰可以怎麼走
而且要吃一個棋子之前一定要移動
所以也不用理攻擊規則

所以我就定義一個chessboard 物件
他所要做的事就只有提供空間給棋子=>getChessAt(x,y)、setAllChess()
以及哪個棋子要移動到哪 =>move(A,x,y)
至於這移動對不對,我就交給其他人去做

而這個人叫做 MoveRule,另外一個物件
負責管理這動作合不合乎規則
視情況會呼叫另一個物件AttackRule 
負責管理攻擊的規則

其它的就交給各人發揮



問題來了
這樣設計真的有比較好嗎??
講這麼多
到了MoveRule還不是要靠if判斷

但是!!情況不同了
現在來了上面新需求(超級士兵)
我只要把全部心力花在MoveRule跟AttackRule上面
棋盤、勝利條件我可以通通不要理他(因為責任已經分好)
而我的第一個做法(implementation)就要在main中做修改
改了這邊   另外一邊也要改(因為重複性)

又或者新需求來了
我不要只有4*8棋盤
我要多一個新的十字棋盤
第一個做法(implementation)就已經寫死了
移動、棋子本身全部都放在array裡面
要怎麼改??
把main從頭到尾看一次嗎?

如果是第二個做法
我就可以抽出一個抽象類別 abstractChessBorad
一樣有原本的方法
RectangleBoard,CrossBorad 就可以分別繼承他
MoveRule只知道棋子在abstractChessBorad上行走
根本不用知道棋盤長得怎樣

以上是我對這句話的理解

to be continue......