2013年12月30日 星期一

Android 開發 (九) Facebook GraphApi Explorer

GraphApi Explorer 是Facebook的開發協助工具,
在開發facebook相關功能時,可以使用他協助debug。

在開發Facebook相關功能可以使用相關的SDK,不過這樣就必須Login,並且給予使用權限,
假設實作的功能並不需要 Login 即可使用 (例如觀看某些粉絲團的文章),可以使用以下的方法


首先使用以下網址取得 access_token

https://graph.facebook.com/oauth/access_token? client_id=app-id&client_secret=app-secret &grant_type=client_credentials

其中 app-id and app-secret 必須前往 Facebook Developers

                                                                 建立新的應用程式

接著會出現類似這樣的格式

其中 app id 與 app secret就是必須填入的資料

取得 access_token之後就可以取得相關資料
例如我想取得 brunomars粉絲專業的資訊
就可以使用
https://graph.facebook.com/brunomars?fields=feed&access_token=User_Token


若要取得某個粉絲團的圖片可以使用以下的網址,其中 575140352564386_576727512405670
要換成該文章的 id
https://graph.facebook.com/fql?access_token=User_Token&q=SELECT attachment FROM stream WHERE post_id="575140352564386_576727512405670"

這個取圖的方法有個問題就是只能取得縮圖,無法取得原圖,若要取得原圖就必須多發一次request,使用的Api如下 https://graph.facebook.com/{object_id}/picture?access_token={access_token}
object_id可從 fields=feed 或 fields=posts中取得,不過這也不是正規的解法,不知道是否有其他的做法,可以直接取得原圖,這部分可能還必須多做研究才行。

Android 開發 (八) Parcelable 使用

序列化

在android中,將資料在頁面間傳遞的其中一個方法就是將資料序列化以後保存在bundle內
Serializable是java的序列化,要實作非常簡單,只需要將以下的code 輸入即可

class PersonSerializable implements Serializable
{

 private static final long serialVersionUID = 1L;
 String name;
        //...other properties
}


Parcelable 是android內部輕量級,高度最佳化的序列化協定,
簡單的說,在程序間的溝通建議使用 Parcelable。


如何使用Parcelable?

1.使用parcelable需要實作 Parcelable (廢話....)
2.必須實作 writeToParcel
3.必須實作 CEATOR(public static final 變數)
sample 如下


class Person implements Parcelable {

 String name;
 

    protected Person(Parcel in) {
        name = in.readString();
    }

    public Person() {
  // TODO Auto-generated constructor stub
 }

 @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
    }

    public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() {
        @Override
        public Person createFromParcel(Parcel in) {
            return new Person(in);
        }

        @Override
        public Person[] newArray(int size) {
            return new Person[size];
        }
    };
}

說實話... Parcelable實在不是很好實作,尤其是class 裡面有其他class或者 property一堆的時候
不過為了最佳化我還是花了點時間survey,
最後,我找到了下面這個網址  http://devk.it/proj/parcelabler/
使用方法很簡單,將以下的程式丟入Code section內  並按下 Build

class Person implements Parcelable {
 String name;
}

他就自動幫你產生好所有必要的程式碼了!!~
最後,讓我們大喊一聲自動化萬歲~

2013年12月25日 星期三

Git (六) 分支

分支是甚麼?

分支就是在專案中開闢新的開發線,當切換分支時檔案也會同步為新分支的檔案
例如
在 hello2中
做了三個commit
vi MasterA
vi MasterB
vi MasterC
git add MasterA
git commit -m "MasterA"
git add MasterB
git commit -m "MasterB"
git add MasterC
git commit -m "MasterC"

然後 git checkout -b pp   創建一個分支名叫 pp 並將分支切換到  pp
並做一個commit
vi  branch
git add branch
git commit -m "branch"
這時候開發圖會長得如下圖所示


這章圖代表甚麼呢?

代表在同一個專案中目前有兩個分支分別是 pp 以及 hello2
兩個專案中所擁有的檔案並不會相同 hello2中擁有 masterA masterB masterC
而 pp中只有 branch這個檔案

簡單的說可以在同一個資料夾底下同時存有多個不同版本的檔案


為什麼要開分支不開另一個資料夾?

原因其實很簡單,使用分支的好處太多了,
舉個例子,假設我今天有兩個分支
ACompany 和 BCompany 這兩個分支所代表的意義分別是給A公司的客製化版本,另一個是給B公司的客製化版本,基本上程式架構都相同,只有某些功能有些差異,
假設今天在ACompany分支上完成了一個新的feature,
然後BCompany分支也要導入這個功能時,我們只需要使用cherry-pick就可以將功能導入,
我們可以很輕易的將功能導入。

如果是使用另一個資料夾就必須花時間apply patch或者重新將功能導入(我想不會有人想這麼做)
而且很難做兩個版本之間的控管


何時要使用分支?

  • 當出現多種版本要維護時,例如出了1.0版本 2.0版本但是兩個版本都還有人使用這時候就可以開分支,當出現bug時可以同時做兩個版本的修復
  • 開發版本,測試版本,正式版本,這種作法是為了幫助自動化測試,在正式版本或測試版本通常會有dayly build 和 unit test 在run 只要有新的commit 就會做測試, 開發人員將開發好的版本merge 進測試版本 經過測試之後再進到正式版本。
  • 開發新功能時(這是本人較習慣的做法),在開發新功能時先開立一個新的分支在功能完成後再將該分支merge回去。這樣可以避免主支幹擁有尚未完成的功能,而其他人還是可以繼續做commit 的動作
養成好的分支觀念,可以讓開發以及維護更順利,遇到以上的情形,just Branch It  XD



Git (五) reset 應用 merge 和 rebase之後的倒回

由  Git 常用指令 (一) git reset  可以瞭解 reset 可以將檔案倒回,
這裡要說明的是,當merge 或 rebase之後要怎麼將版本倒回尚未下指令之前,
首先要介紹一下幾個特別符號

HEAD  

為分支上最新的送交

ORIG_HEAD

在merge 或 rebase 時 ,會先將原本的HEAD記錄在 ORIG_HEAD ,
所以可以使用ORIG_HEAD來做恢復的動作

FETCH_HEAD

是抓取的上一個分支HEAD,並且僅在抓取指令之後有效,使用這個符號可以找到git fetch 指令抓取的HEAD

MERGE_HEAD

合併再執行時,另一個分支的HEAD會被暫時的記錄在MERGE_HEAD中


所以,回到merge 或 rebase時該怎麼倒回
就是使用 git reset --hard ORIG_HEAD  將檔案倒回就可以了


2013年12月24日 星期二

OOAD原則 (九) 封裝

何謂封裝?

對資料所做的任何計算都受到保護,因為資料不會被存取。
也就是隱藏程式執行內部事物的部分。

舉個例子

Ted.gotoTaipeiStation()
Bob.gotoTaipeiStation()

有時候我們只在乎 Ted and Bob 都到了台北,我們不會去care
Ted 住在台北  所以他搭捷運到北車
Bob住在台南,所以他必須搭高鐵到北車

封裝隱藏了實際實作的部分。



封裝在某些情況也避免了使用者錯誤的操作

例如

networkParser.getUserData()
dbParser.getUserData()

networkParser的動作可能包含了
1.連到網路 2.取得正確網址 3.取得資料 4. 擷取UserData

dbParser得動作可能包含了
1.連線到db 2.query db table資料並取得資料

但是使用者不需要知道詳細的情況,使用者只需要知道現在要拿userdata就好了
封裝讓使用者輕易的取得UserData,而不需要實際去操作,這避免了使用者錯誤的使用


該如何使用封裝?
可以看看以下的範例


interface GotoTaipeiBehavior
{
 void gotoTaipeiStation();
}
class Ted implements GotoTaipeiBehavior
{

 @Override
 public void gotoTaipeiStation() {
  // TODO Auto-generated method stub
  System.out.println("Tkae SubWay");
 }
 
}

class Bob implements GotoTaipeiBehavior
{

 @Override
 public void gotoTaipeiStation() {
  // TODO Auto-generated method stub
  System.out.println("Tkae HightSpeedRail");
 }
 
}
public class person {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  GotoTaipeiBehavior behavior = new Ted();
  behavior.gotoTaipeiStation();
  behavior = new Bob();
  behavior.gotoTaipeiStation();
 }

}

這個範例就說明了 Ted 和 Bob去台北車站的例子,
main裡面就是client使用時呼叫 ted 和 bob 要去台北車站,
可以注意到這邊我們只在乎他們的行為(GotoTaipeiBehavior )不在乎他們是誰,
在我們將Ted 或Bob 創建出來之後,我們將他們一視同仁(都是behavior),然後要去台北車站,
這樣寫的好處是,不管是誰要去台北車站都可以使用gotoTaipeiStation這段程式,
程式的可用性提高了,而且使用者不需要知道裡面實際做了甚麼事情,只需要知道人到了台北,這同時也提升了程式的可讀性。


結論

封裝會隱藏物件的實做,以及保護資料避免被user操作,同時他也可以將某些user不需要知道資料隱藏起來(例如某些控制開關的flag)。
封裝可以降低程式的耦合度,同時若加上良好的命名封裝同時也可以提升程式的可讀性,進一步提升了程式的維護性。

2013年12月23日 星期一

Git (四) Rebase vs Merge (續)

在上一篇有講述rebase 和 merge上的差異,這篇要講的是線圖上的差異

先看看下面三張圖表


原圖



Merge


Rebase


rebase 可以將線圖拉直,merge則是會將兩個線圖合併
rebase的好處是可以將branch的紀錄消除(在線圖上就不會看到兩條線)
在使用上是看個人喜好,不過在使用rebase的時候要特別注意
網路上有人認為rebase is evil,他們認為

When you rebase a branch, you are changing its history in a way that will cause problems for anyone who already has a copy of the branch in their repository and tries to pull updates from you.

意思是如果在某個很多人使用的branchA下,rebase另一個branchB,然後又將rebase之後的結果push出去,會使得該branchA的共同使用者遇到問題。

大部分的人都建議 rebase只用在private branch,


使用情境大概是

我在master上開一個branch 然後在上面實作了自己的功能,經過一段時間之後,我發現master經歷了許多版本的update,所以我希望將branch update到最新版本,以便於測試是否有問題,
這時候就可以使用 rebase 將我自己的branch update到最新版本。

不過..我的做法是會再新開一個branchC 然後將branchC 做rebase,就如同上面英文所說的,
rebase會更改歷史記錄,假設rebase失敗了,那local的檔案也會跟著毀掉,所以保險起見,我還是會先做個備份。


不建議的做法是

在一個眾多人參考的branch上開發了自己的功能,經過一段時間之後,發現跟server上的branch版本有差異,所以使用rebase 將版本 update到最新版本。

這兩個的差異就是在於一個是私人的branch 不管怎麼改都不會影響到別人
但是共用的branch如果操作錯了不僅整個分支會出問題,還會連帶影響到其他的開發人員,
這是很可怕的事情。


2013年12月22日 星期日

敏捷開發(一) 開始研讀敏捷開發書籍的原因

趁我還有印象的時候,寫下之前不好的開發經驗

1.RD 包攬 PM QT的事情

是的,RD需要常常跟其他部門溝通,提供需求給其他部門,跟各窗口討論我們需要的功能,還得去開會了解一些進度的問題,這樣來來回回非常浪費時間。
而QT測回來的東西,RD常常會很難複製,或者QT描述的複製手法很不清楚,必須經過多方溝通,非常浪費時間。

這部分我深深覺得哪裡有問題,但是我也不知道該如何下手。
或許需要將合作的單位都拉在附近一起做事吧,有問題可以直接溝通,直接反應,直接解決,這樣或許就可以省去大量來回的時間。

2.定好的SPEC總是很輕易的翻盤 

UI這禮拜說要這樣畫,然後下禮拜又說要換另一個形式,總是在修改,彷彿RD不需要花時間去架構設計coding一樣,然而project的deadline並沒有因為這些修改而改變,
我其實很好奇這樣的做法是否真的正確,感覺是在哪一環出了問題。

仔細思考之後感覺是上層主管太慢看到成品,通常demo的都是成品,然而這時候才有意見要修改,就必須花很多時間修改,感覺應該要有個代理人,我們可以在每個禮拜或每個月release一個版本給他看,如果代理人有任何的想法應該在那時反應,在這個階段修改,然後之後就不應該再有修改,除非是等到新的版本或者下個schedule才將新的修改加進去。

3.schedule總是定得很短

這使得RD沒有時間可以架構設計,而是直接將手邊有的想法直接倒入程式內,沒有經過多餘的討論,代價就是可怕的架構,難以維護的程式碼。

4.spec更改,RD最後一個知道

某個案子需要跟別的team合作,有些資料必須靠他們的api傳遞給我們,但是...常常spec更新了或修改了我們這邊都沒有人被告知,直到QT測出問題了,我們這邊才知道出事了。
又或者某次UI做了變動,卻沒有同步道所有的人,大家的認知不同,做出來的東西也有誤差。

這部分我深深覺得是彼此溝通的問題,資料沒有同步,我想各單位合作時必須要有個共同溝通平台,或者是能夠同時update彼此最新的進度,並check是否有哪邊是沒有同步到的



run過這樣的案子之後,我對現行run的方式有很大的疑問...
真的是這樣run的嗎???  這句話不斷在我腦海中重複出現

為了能夠解除我內心的疑惑,我花了點時間開始翻閱一些書籍,然後偶然間聽到了敏捷開發,發現裡面的一些想法似乎能解決掉上面大部分的問題,所以,我就這麼踏進了敏捷開發這個世界....也期許在這裡能夠找到我要的答案!!

2013年12月19日 星期四

Git (三) cherry-pick

Cherry-Pick

這個指令讓我們能夠將branch中,某一次commit所做的修改,直接套用到另外一個branch
如下圖所示


上圖 develop 為主要的開發支幹,
然後我們開了一個branch "feature/TestFeature" 然後在那個branch做了一些資料的更改,
接著我們希望將其中four 所做的變更套用至 develop 中,這時候就可以使用cherry-pick


接下來我們要將four這個commit 套用至 develop中
所以首先我們要先知道 four 這個commit 所代表的"代號"  也就是 b784dfe 這組數字
接著在develop branch中下指令 git cherry-pick b784dfe 就可以了


如下圖所示,four這個commit直接被套用到 develop中



cherry-pick 可以讓我們將部分的修改直接套用到另一個branch


使用的情境

同時在維護兩個類似的project
當修正完A project的 bug之後  只需要使用cherry-pick將相同的修正套用到B project即可
這算是十分方便的功能

2013年12月17日 星期二

OOAD原則 (八) Interface Seggregation Principle

ISP (接口隔離原則)主要強調的是,interface必須是內聚的,也就是說不應該出現 "胖" interface,
如果出現了 "胖" interface 代表他應該是可以分解成多組。

底下是一個 "胖" interface 的範例




首先一開始客戶要求我們做一台車,很簡單的我們直接使用了 interface Vehicle ,
之後不管是摩托車,跑車,房車,中古車  都可以使用他

interface Vehicle
{
 void Run();
}
class Porsche911 implements Vehicle
{

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("Porsche911 Running");
 }
 
}
public class Car implements Vehicle{

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Vehicle carVehicle = new Car();
  carVehicle.Run();
  carVehicle = new Porsche911();
  carVehicle.Run();
 }

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("Car Running");
 }

}


但是好景不常,某一天客戶提出了新的需求,
他們公司生產了飛天車,這也代表著我們的程式也必須提供飛天車的資料
下面的作法是比較不好的作法

interface Vehicle
{
 void Run();
 void Fly();
}
class Porsche911 implements Vehicle
{

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("Porsche911 Running");
 }

 @Override
 public void Fly() {
  // TODO Auto-generated method stub
  System.out.println("Porsche911 not Flying");
 }
 
}
class FlyCar implements Vehicle
{

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("FlyCar Running");
 }

 @Override
 public void Fly() {
  // TODO Auto-generated method stub
  System.out.println("FlyCar flying");
 }
 
}
public class Car implements Vehicle{

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Vehicle carVehicle = new Car();
  carVehicle.Run();
  carVehicle = new Porsche911();
  carVehicle.Run();
 }

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("Car Running");
 }

 @Override
 public void Fly() {
  // TODO Auto-generated method stub
  System.out.println("Car not Flying");
 }

}

可以很清楚的發現,在interface裡新增了 fly這個功能,但是這其實是災難的開始,
這使得原本跑的很正常的 car and Porsh911 這兩個物件也新增了fly的功能,
但是他們並不會飛阿,而且他們也不想要會飛,可是他們現在逼不得以也必須implement Fly這個功能,這樣寫法非常不好的原因在於,

  1. implement的功能並不是我們所需要的
  2. 現在只有兩個class需要做修正(Car and Porsh911),當我們有一千個class要修改的時候我相信修改的人一定會......!@#$%
由以上的例子,告訴了我們這是一個不好的設計,然後ISP也提到這就是一個 "胖" interface ,
所以根據上面的例子我們可以做以下的修改

interface Vehicle
{
 void Run();
}
interface FlyVehicle extends Vehicle
{
 void Fly();
}
class Porsche911 implements Vehicle
{

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("Porsche911 Running");
 }

}
class FlyCar implements FlyVehicle
{

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("FlyCar Running");
 }

 @Override
 public void Fly() {
  // TODO Auto-generated method stub
  System.out.println("FlyCar flyning");
 }
 
}
public class Car implements Vehicle{

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Vehicle carVehicle = new Car();
  carVehicle.Run();
  carVehicle = new Porsche911();
  carVehicle.Run();
  carVehicle = new FlyCar();
  ((FlyVehicle)carVehicle).Fly();
 }

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("Car Running");
 }

}


我們使用interface FlyVehicle繼承 Vehicle 並新增 fly的功能,
這邊其實跟SingleResponsibilityPrinciple的感覺很像,每個interface都有單一的職責(注意不是單一method)這使得我們只需要修改FlyVehicle就可以了,而且新增功能對於其他的class並不會造成額外的影響。

當然這種修改方式僅限於開發階段,

  • 在小怪物還沒有長成千年老妖之前處理是很容易的...


如果是處在維護階段的話,上面的作法可能會牽一髮而動全身,如果能動就動吧,如果不能動就....,試試下面的方法吧


interface Vehicle
{
 void Run();
 void Fly();
}
abstract class VehicleAdapter implements Vehicle
{
 abstract public void Run();
 public void Fly()
 {
  System.out.println("Do Nothing");
 }
 
}
class Porsche911 extends VehicleAdapter
{

 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("Porsche911 Running");
 }
}
class FlyCar extends VehicleAdapter
{

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("FlyCar Running");
 }

 @Override
 public void Fly() {
  // TODO Auto-generated method stub
  System.out.println("FlyCar flyning");
 }
 
}
public class Car extends VehicleAdapter{

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Vehicle carVehicle = new Car();
  carVehicle.Run();
  carVehicle = new Porsche911();
  carVehicle.Run();
  carVehicle = new FlyCar();
  carVehicle.Fly();
 }

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("Car Running");
 }


}

上面使用了一個adapter轉接器將Fly這個功能在adapter中做掉,使得繼承vehicleAdapter的物件不再需要實做fly這個功能,然後Flycar可以override掉原本的功能,改成fly的功能。用這樣的方式就可以使之後想使用vehicle interface但不想fly的人,可以使用新的選擇而不再需要去實做Fly這個功能。

ISP原則感覺就是將SRP原則套在interface身上,希望每個interface都是單一原則(不是單一method),而不是一個interface擁有多個責任,太胖的interface會造成許多問題,
善用這個方法,可以降低耦合,使程式碼更加彈性。

2013年12月15日 星期日

OOAD原則 (七) Dependency Injection (DIP依賴倒置原則 - 續2)

相依性注入 (DI) 是實踐DIP的方法
  • Constructor Injection
  • Property Injection
  • Interface Injection
以下要直接介紹這三個方法



Constructor Injection

class Behavior
{
 public void Run()
 {
  System.out.println("Run");
 }
}
public class ConstructorInjection {

 /**
  * @param args
  */
 
 private Behavior behavior;
 public ConstructorInjection(Behavior _be)
 {
  behavior = _be;
 }
 public void Execute()
 {
  behavior.Run();
 }
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  ConstructorInjection injection = new ConstructorInjection(new Behavior());
  injection.Execute();
 }
}

上面的方法是將物件在class 初始化的時候將所需要的Behavior直接注入,來避免高偶合(繼承)的問



Property Injection

class TomRun
{
 public void Run()
 {
  System.out.println("Run");
 }
}
public class PropertyInjection {

 /**
  * @param args
  */
 private TomRun behavior;
 public TomRun GetTomRun()
 {
  return behavior;
 }
 public void setTomRun(TomRun _be)
 {
  behavior = _be;
 }
 public void Execute()
 {
  behavior.Run();
 }
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  PropertyInjection injection = new PropertyInjection();
  injection.setTomRun(new TomRun());
  injection.Execute();
 }

}

上面的方法是利用getter and setter 讓我們將所需要的 TomRun注入  來避免高耦合(繼承)的情形


Interface Injection

interface RunBehavior
{
 void Run();
}
public class interfaceInjection implements RunBehavior {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  RunBehavior behavior = new interfaceInjection();
  behavior.Run();
 }

 @Override
 public void Run() {
  // TODO Auto-generated method stub
  System.out.println("Run");
 }

}

上面的方法是利用 interface 讓class implements 該interface 並實作之
利用interface 注入來避免高耦合(繼承)的問題


利用這些方法,我們可以避免使用繼承,使用相依注入,降低耦合,
當然,第一個例子和第二個例子更好的寫法是使用interface 然後注入使用的參數是interface而不是class  這樣又可以呼應到DIP的要點



依賴抽象類別,不要依賴具象類別。



利用抽象來當作class之間溝通的方式,這樣寫出來的程式耦合度會更低,會更好maintain

Android 開發 (七) DB 的使用介紹

在Android中要使用DataBase的功能,可以實作 SQLiteOpenHelper 或 SQLiteDatabase 接著只需在裡面使用相關的sql command即可 在這邊就稍微介紹一下該如何下指令



interface DAO_DB
{
 public void createTable();
 public void insertData(Object obj);
 public void deleteData(Object obj);
 public void updateData(Object obj);
 public void FindAllData();
 public void FindLastID();
 public void close();
}
public class DBHelper extends SQLiteOpenHelper implements DAO_DB{
 private SQLiteDatabase dbDatabase;
 private String TABLE_NAME = "MyTab";
 
 public DBHelper(Context context) {
  super(context, "TED_DE", null, 1);
  // TODO Auto-generated constructor stub
 }


 @Override
 public void onCreate(SQLiteDatabase db) {
  // TODO Auto-generated method stub
  dbDatabase = db;
  createTable();
 }

 @Override
 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
  // TODO Auto-generated method stub
  
 }

 @Override
 public void createTable() {
  // TODO Auto-generated method stub
  String cmd = "CREATE TABLE " + TABLE_NAME + " ("
    + _ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " +
    DBColumns.NAME + " TEXT, " +
    DBColumns.TEL + " TEXT, " +
    DBColumns.EMAIL + " TEXT);"; 
  dbDatabase.execSQL(cmd);
 }

 @Override
 public void insertData(Object obj) {
  HashMap<String, String> map = (HashMap<String, String>)obj;
     String sql = "INSERT INTO "+ TABLE_NAME + 
       " values("
       +"\"" + map.get(DBColumns._ID) +"\","
       +"\"" + map.get(DBColumns.NAME) +"\","
       +"\"" + map.get(DBColumns.TEL) +"\","
       +"\"" + map.get(DBColumns.EMAIL) +"\""
       + ");";

     getWritableDatabase().execSQL(sql);
 }

 @Override
 public void deleteData(Object obj) {
  // TODO Auto-generated method stub
  HashMap<String, String> map = (HashMap<String, String>)obj;
  String sql = String.format("DELETE FROM "+ TABLE_NAME+" WHERE _id = '%s' ", map.get(DBColumns._ID));
  getWritableDatabase().execSQL(sql);
 }


 @Override
 public void updateData(Object obj) {
  HashMap<String, String> map = (HashMap<String, String>)obj;
     ContentValues values = new ContentValues();
     
     String sql = "UPDATE "+ TABLE_NAME + 
       " SET "+DBColumns.NAME+" = ?, "
       +DBColumns.TEL+" = ?, "
       +DBColumns.EMAIL+" = ? "+
       "WHERE _ID = ? ";
     Object[] bindArgs = new Object[]{map.get(DBColumns.NAME),map.get(DBColumns.TEL),map.get(DBColumns.EMAIL),map.get(DBColumns._ID)};
     getWritableDatabase().execSQL(sql,bindArgs);
 }


 @Override
 public void FindAllData() { 
   String query = "SELECT * FROM "+TABLE_NAME;
   Cursor cursor = getWritableDatabase().rawQuery(query, null);
    
    
    StringBuffer sf = new StringBuffer();
    cursor.moveToFirst();
    while (!cursor.isAfterLast()) {
     sf.append(cursor.getInt(0)).append(" : ")
     .append(cursor.getString(1)).append(" : ")
     .append(cursor.getString(2)).append(" : ")
     .append(cursor.getString(3)).append("\n");
     cursor.moveToNext();
     }
    Log.d("Ted", sf.toString());
 }


 @Override
 public void FindLastID() {
  Cursor cur = getWritableDatabase().rawQuery("select LAST_INSERT_ROWID() ",null);
        cur.moveToFirst();
        Log.d("Ted","LAST ID"+ String.valueOf(cur.getLong(0)));
 }

}

上面實作了簡單的 insert ,update, del ,FindLastId , FindAllData的方法
當然其實也可以簡單的使用getWritableDatabase().insert getWritableDatabase().update getWritableDatabase().del  只是作者想順便練練execSQL的寫法而已 XD


若有興趣看看sampleCode 可以使用下面的網址

Sample Code

2013年12月11日 星期三

OOAD原則 (六) Ioc Inversion of Control (DIP依賴倒置原則 - 續)

DIP 是個理論,而如何達到這個目標就必須使用IoC這個技巧,

IoC容器

  • 會實踐Dependency injection
  • 提供設定dependency的方法
講白話點就是
  • 創造dependency /object 
  • 注射或傳遞dependency



接著我們來看一下範例

根據  DIP 依賴倒置原則 這篇文章,我們可以將該範例的UML畫出來如下圖



class EatRiceBehavior 
{
 void eat()
 {
  System.out.println("Person_EATRice");
 }
}
public class Person
{
 EatRiceBehavior behavior=new EatRiceBehavior();
 public void eat() {
  // TODO Auto-generated method stub
   behavior.eat();
 }
 public static void main(String[] args) 
 {
  Person person = new Person();
  person.eat();
 }
 
}

人吃飯,人擁有吃飯的物件來實做吃飯該做的事情,但是依賴於具象是不好的,會造成程式的高耦合性,應該依賴於抽象,所以我們更改架構變成下圖


interface Behavior
{
 void eat();
}
class EatRiceBehavior implements Behavior
{

 @Override
 public void eat() {
  // TODO Auto-generated method stub
  System.out.println("eat rice");
 }

}
class EatNoodleBehavior implements Behavior
{

 @Override
 public void eat() {
  // TODO Auto-generated method stub
  System.out.println("eat  EatNoodle");
 }
}
class EatDumplingBehavior implements Behavior
{

 @Override
 public void eat() {
  // TODO Auto-generated method stub
  System.out.println("eat  Dumpling");
 }
}

public class Person
{
 Behavior behavior;
 public void eat(Behavior behavior) {
  behavior.eat();
 }
 public static void main(String[] args) 
 {
  Person person = new Person();
  person.eat(new EatRiceBehavior ());

 }
 
}


我就沒有畫出noodle 還有 dumpling的class了 ,但是他的意義其實是,



用程式的意義來說明

原本Person 依賴於 EatRiceBehavior ,當人想要eat的時候就會將指令傳達給EatRiceBehavior
當要吃麵的時候就會將指令傳達給EatNoodleBehavior 這樣的缺點是當想吃其他的東西時參數並無法共用,會產生出許多參數造成管理不便,
但是經過我們修改之後我們將控制權交給了interface Behavior
使得 EatNoodleBehavior、 EatDumplingBehavior、 EatRiceBehavior 皆依賴於Behavior
,只要有人擁有Behavior就擁有他們的控制權,然而Person恰巧擁有Behavior,這使得Person能夠輕易的控制那三個Behavior。



用注射傳遞的方式來說明

我們將Behavior 注射/傳遞至Person內  Person就是Ioc容器他會負責幫我們在class之間做溝通
讓我們可以在上層與下層之間做溝通而不依賴於彼此

OOAD原則 (五) DIP 依賴倒置原則

Dependency Inversion Principle 依賴倒置原則主要有個要點
  • 依賴抽象類別,不要依賴具象類別。



簡單的說就是class 之間用interface/abstract 來溝通
下面是個簡單的人吃飯的例子。

class EatRiceBehavior 
{
 void eat()
 {
  System.out.println("Person_EATRice");
 }
}
public class Person
{
 EatRiceBehavior behavior=new EatRiceBehavior();
 public void eat() {
  // TODO Auto-generated method stub
   behavior.eat();
 }
 public static void main(String[] args) 
 {
  Person person = new Person();
  person.eat();
 }
 
}


但是當我想新增人吃麵的行為,原本的程式就不能使用了,因為他依賴於具象類別(EatRiceBehavior ),所以我就必須在class 裡面增加 EatNoodle Nbehavior; 類似這樣的參數,接著當我新增了越來越多的物件時這段程式就會變成類似下面這樣


public class Person
{
 EatBehavior behavior;
        EatNoodle  Nbehavior;
              Eatdumpling Dbehavior;  
public void eat(string val) { // TODO Auto-generated method stub if(val.equals("rice")) behavior.eat();
                else if(val.equals("noodle")) Nbehavior.eat();
                else Dbehavior.eat();
 }
 public static void main(String[] args) 
 {
  Person person = new Person();
  person.eat("rice");
 }
 
}


這樣的程式是非常可怕的,難以維護且重複性極高,
這時候還好有Dependency Inversion Principle,他告訴我們依賴於抽象而不依賴於具體類別
所以我們就可以寫出下面的例子


interface Behavior
{
 void eat();
}
class EatRiceBehavior implements Behavior
{

 @Override
 public void eat() {
  // TODO Auto-generated method stub
  System.out.println("eat rice");
 }

}
class EatNoodleBehavior implements Behavior
{

 @Override
 public void eat() {
  // TODO Auto-generated method stub
  System.out.println("eat  EatNoodle");
 }
}
class EatDumplingBehavior implements Behavior
{

 @Override
 public void eat() {
  // TODO Auto-generated method stub
  System.out.println("eat  Dumpling");
 }
}

public class Person
{
 Behavior behavior;
 public void eat(Behavior behavior) {
  behavior.eat();
 }
 public static void main(String[] args) 
 {
  Person person = new Person();
  person.eat(new EatRiceBehavior());
 }
 
}


現在不管新增了甚麼行為進來,都不至於造成太大的影響。

2013年12月3日 星期二

OOAD原則 (四) 委派 vs 合成 vs 聚合

委派

將特定工作的責任委派給一個類別或方法
譬如下面的例子:
老闆說要蓋大樓,接著當然不會是老闆蓋大樓,
老闆會將這個工作委託給工人們,由工人們就去蓋大樓。



class Worker
{
 public void Work()
 {
  System.out.println("Do Work");
 }
}

class Boss
{
 private Worker labor = new Wroker();
 public void Build()
 {
  labor.Work();
 }
}
public class delegate {

 /**
  * @param args
  */

 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Boss boss = new Boss(); 
  boss.Build();  
 }

}


合成

擁有一組其他類別的行為,
還有一個重點是當合成的物件消失他所擁有的其他類別的行為也會消失。
下面的例子:
ArmsDealer擁有Weapon,
當ArmsDealer 被殺掉的話使用者就無法取得購買Weapon的通路。


interface Weapon
{
 public void Attack();
}
class Sword implements Weapon
{ 
 public void Attack()
 {
  System.out.println("Sword Attack");
 }
}
class Bow implements Weapon
{
 public void Attack()
 {
  System.out.println("Bow Attack");
 }
}
class ArmsDealer
{
 ArrayList<Weapon> list = new ArrayList<Weapon> ();
 public ArmsDealer()
 {
  list.add(new Bow());
  list.add(new Sword());
 }
 public Weapon GetWeapon(int index)
 {
  return list.get(index);
 }
}
public class Game {

 
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  ArmsDealer dealer= new ChooseWeapon();
  dealer.GetWeapon(0).Attack();
 }

}

聚合

跟合成最明顯的差異就是,當聚合物件被摧毀時,他所擁有的物件並不會跟著一起銷毀。
下面的例子:
Ted是個司機當車子報廢的時候Ted還是可以去開motoBike並不會因為車子報廢Ted就跟著一起消失...如果這樣也太可怕了XD

abstract class Driver
{
 abstract public void Drive();
}
class Ted extends Driver
{
 public void Drive()
 {
  System.out.println(" Driving");
 }
}

class Car
{
 private Driver driver;
 public Car(Driver ted)
 {
  this.driver= ted;
 }
 public void Move()
 {
  driver.Drive();
 }
}

class MotoBike
{
 private Driver driver;
 public MotoBike(Driver ted)
 {
  this.driver= ted;
 }
 public void Move()
 {
  driver.Drive();
 }
}
public class Aggregation {

 /**
  * @param args
  */
 public static void main(String[] args) {
  Ted ted = new Ted();
  Car car = new Car(ted);
  car.Move();

 }

}


結論

委派是將某個行為委託給別人去做
ex:老闆叫員工去寫code

合成是擁有其他類別的資訊且當該類別被destroy的時候他所擁有的資訊也會一併消失
ex:冰淇淋被吃掉,上面的配料也都一併進到肚子裡去(消失)。

聚合也是擁有其他類別的資訊但是當該類別被destroy的時候他所擁有的資訊並不會一併消失
ex:司機開車,假設車子報廢,司機還是可以開新車。

2013年12月2日 星期一

Eclipse Theme

這是一個可以改變Eclipse主題的套件,如下圖所示


使用步驟如下


首先
進入Help -> Install New Software 安裝該軟體






在name可以隨便Key
在網址處key 入 http://eclipse-color-theme.github.io/update/
接著next 按到底就完成了


接著
到 http://eclipsecolorthemes.org/ 選擇喜歡的主題 點擊進去之後





接著點選Download底下的 (EPF) Eclipse Preference
下載下來之後將檔案import進去  點選Preferences




將剛剛下載的檔案Import進來





接著新的主題就出現了



2013年11月25日 星期一

Git (二) Rebase vs Merge

rebase 跟 merge 做的事情其實很像

主要的差別在於
假設我在 branch下指令  git merge master,
merge 會一次性的將所有master的commit 直接merge進 branch
rebase的話  則會分成多次commit 進來




舉個例子來看看


上面分別有master and branch 兩個分支
我在 branch 上做了兩次commit  branchC  and branchD
然後我在 master 上做了三次commit masterA ,masterB and masterC



Merge

首先我們先使用merge 指令
在branch分支下指令 git merge master
master的資料將會在一次commit 中將 masterA , masterB and masterC
都加進我的branch分支內,我們看到的commit結果會是如上圖
branchC, branchD ,Merge branch 'master' into branch ,
要倒回去的話只需要下指令git reset --hard HEAD^
就可以恢復merge之前的狀態



Rebase 

接著使用 rebase 指令
在branch分支下指令 git rebase master



可以看到原本分為兩個的分支結合再一起了
可以看到git rebase master之後的結果會是  
masterA , masterB, masterC , branchC and branchD 跟git merge master的結果有些差別
不過上圖並不好解釋會何會變成這樣,下面的圖比較能解釋他的原因

原本的基準點為左圖最下的藍色點,
當我們使用 git rebase master時,branch分支會將基準點從最下方的藍色點rebase到 master 的最上方的點,也就是如右圖所示(可以把他想成pointer 將指標指過去的感覺),
所以最後會只看到一條線。
經過rebase之後的分支要倒回並不是那麼的容易,
我的做法會是在create一個新的分支,然後將這次的Rebase都倒回之後再用cherry-pick將原本的commit 拉回來。



結論

            git merge master是一次性的commit ,然而git rebase master是多次性的commit 。


git merge 的commit是依照順序的 branchC, branchD 然後將master commit 進來masterA masterB masterC,
所以結果會是:branchC, branchD masterA masterB masterC
然而git rebase 的commit是先rebase到master分支再將原本的commit apply回來
所以順序會是: masterA masterB masterC branchC, branchD


Merge和Rebase的用途也不太相同

通常Rebase的用途在於  有同事在遠端commit了新的code進來
我希望我現在修改的版本能base到最新的版本
這時候就可以使用rebase command 讓手邊的程式更新到最新的版本

Merge的話比較像是我在某個分支完成了一個大型修正或功能,而且也經過大量測試之後
想要將這個功能導入我現在的working directory,
這時候就可以使用merge, 記住 merge是一次性的commit 所以所有關於該feature的功能將會一次性的導入你並不會在working directory中看到有關於該feature的其他commit




2013年11月24日 星期日

Git (一) git reset

今天要談的是git  reset的功能, git reset有分三個選項

git reset --soft
git reset --mixed 也就是 git reset
git reset --hard

首先我們要分成三個部分說明






當我們新增了一個 hello.txt 檔 到 working directory之後,
我們希望將該file commit 進  git directory,
首先我們必須下指令 git add hello.txt , 將我們希望更改的檔案放進 staging area,
接著再下指令  git commit -m "add hello"     ("add hello" 為 commit的文字描述),
然後資料就會進到git directory了。
                     
                                            太好了所有的修改都加進了git directory 我可以收工了。

可惜....  事與願違,當初做 add hello 這個修改時,我除了修改了這個檔案 hello.txt之外,我還修改了world.txt這個檔案。
     
                            糟了,我commit出了差錯老闆知道的話一定會把我臭罵一頓。

還好git 有支援倒回的指令,
我們只需要下指令 git reset --soft HEAD^ 就會將這次的commit 取消,
然後我們只需要再下  git add world.txt -> git commit -m "add hello"  ,這樣就神不知鬼不覺的將資料加進這次commit了。

當然我們也可以下指令 git reset --HEAD^ ,直接取消掉 commit 以及 staging area , 回到 working directory ,這個階段所有的資料都尚未被加入 staging area,
然後接著下指令 git add hello.txt -> git add world.txt -> git commit -m "add hello"
就好了。

然後值得注意的是 git reset HEAD^git reset --hard HEAD^ 的差別在於
git reset --hard HEAD^ 會將該次的修改也一併倒回,也就是當我下了這個指令之後
hello.txt and world.txt將回到上一次commit 的版本 (這次所做的所有修改也都會消失)

                            除非你知道正在做甚麼,
                                 否則不要輕易下這個指令,不然辛苦修改的成果可能就在瞬間被銷毀。

git reset 也可以用於 fast forward 只需要知道想要fast forward的SHA
使用 git reset --hard xxxxxx        xxxxxx 為 head的SHA
這樣就會使分支跳到指定的位置

2013年11月23日 星期六

[UML] 物件導向分析與設計

介紹

今天花了點時間看了物件導向分析與設計,看了前四章的感覺是,這本書花了很多時間在指導我們如何分析客戶的需求,如何寫出良好的 user cases ,當然對於程式的封裝也有稍微提及。
不過今天要介紹的是UML圖




UML
         
             是系統分析以及設計的重要產物,也是實作以及測試的依據。

下面這張圖是 head first 物件導向分析與設計裡面的一個 sample,
使用者希望能有一個 Remote 能夠在按下按鈕的時候, DogDoor 會打開/關閉,
然後也希望家裡的狗 Bark 的時候, DogDoor 可以 Recognize 是自己家的狗然後開門。


上圖中  Remote -> DogDoor 之間的數字  1 代表著  
Remote has 1 個 DogDoor
相同的  BarkRecognizer also has 1個 DogDoor
DogDoor has n 個 Bark  而DogDoor 可以在 allowedBarks的屬性裡儲存任意數量的Bark


看完了UML圖以及解釋之後接下來就是實作部分



Sample Code


Bark.java
  
public class Bark {

 /**
  * @param args
  */
 private String sound;
 public Bark(String sound)
 {
  this.sound = sound;
 }
 public String getSound()
 {
  return sound;
 }
 public boolean equals(Bark bark)
 {
  if(bark.getSound().equals(this.sound))
   return true;
  return false;
 }
} 


BarkRecognizer.java

public class BarkRecognizer {

 /**
  * @param args
  */
 private DogDoor door;
 public BarkRecognizer(DogDoor door)
 {
  this.door = door;
 }
 public void recognize(Bark bark)
 {
  java.util.List<Bark> allowedBarks = door.getAllowedBarks();
  for (Bark allowedBark : allowedBarks) {
   if(bark.equals(allowedBark))
   {
    door.Open();
    return;
   }
  }
  System.out.println("Door is not Open");
 }
}

DogDoor.java

public class DogDoor {

 /**
  * @param args
  */
 private List<Bark> allowedBarks = new ArrayList<Bark>();   
 private boolean Open=false;
 public void Open()
 {
  System.out.println("Door Open");
  final Timer timer = new Timer();
  timer.schedule(new TimerTask(){

   @Override
   public void run() {
    // TODO Auto-generated method stub
    Close();
    timer.cancel();
   }
   
  },5000);
 }
 public void Close()
 {
  System.out.println("Door Close");
 }
 public boolean IsOpen()
 {
  return Open; 
 }
 public void addAllowedBark(Bark bark)
 {
  allowedBarks.add(bark);
 }
 public List<Bark> getAllowedBarks()
 {
  return allowedBarks;
 }
}

Remote.java

public class Remote {

 /**
  * @param args
  */
 private DogDoor door;
 public Remote(DogDoor door)
 {
  this.door = door;
 }
 public void pressButton()
 {
  if(door.IsOpen())
   door.Close();
  door.Open();
 }
}

接著拿出head first 裡面所提供的測試檔案


Test



 public static void main(String[] args) {
  // TODO Auto-generated method stub
  DogDoor door = new DogDoor();
  door.addAllowedBark(new Bark("rowlf"));
  door.addAllowedBark(new Bark("rooowlf"));
  door.addAllowedBark(new Bark("rawlf"));
  door.addAllowedBark(new Bark("woof"));
  BarkRecognizer recognizer = new BarkRecognizer(door);
  Remote remote = new Remote(door);
  
  System.out.println("Bruce starts barking.");
  recognizer.recognize(new Bark("rowlf"));
  
  System.out.println("Bruce has gone outside...");
  
  try{
   Thread.currentThread().sleep(6000);
  }catch(InterruptedException ex)
  {
   
  }
  
  System.out.println("Bruce's all done...");
  System.out.println("but he's stuck outside...");
  
  Bark smallDogBark = new Bark("open the door");
  System.out.println("A small dog starts barking.");
  recognizer.recognize(smallDogBark);
  
  System.out.println("Bruce starts barking");
  recognizer.recognize(new Bark("rowlf"));
  System.out.println("Bruce's Back inside");
  
 }


Result

Bruce starts barking.
Door Open
Bruce has gone outside...
Door Close
Bruce's all done...
but he's stuck outside...
A small dog starts barking.
Door is not Open
Bruce starts barking
Door Open
Bruce's Back inside

Door Close



結論

這次展示了如何從UML 到程式的coding
UML就有點像是蓋房子之前的藍圖,
有了藍圖就可以降低user想蓋巴黎鐵塔結果developer卻蓋出金字塔的問題。
我想這對user 來說應該也算是一大福音吧 XD



2013年11月20日 星期三

CleanCode 讀書筆記 (六) 用程式說故事

命名是個藝術


clean code裡面有提到,越多的註解代表程式寫得越差。
最近一直在體會這句話的涵意,讓我們來看看下面的例子:

            
            bool bo = Dic.TryGetValue("hello",out val);
            if (bo)
                Console.WriteLine("do something");




從上面的例子我們知道當bo為true時,我們必須 do something。
再看看下面的例子:
            
            bool HasKey = Dic.TryGetValue("hello",out val);
            if (HasKey)
                Console.WriteLine("do something");

上面的例子讓我們感覺像是在讀文章

first, we get a variable HasKey and then if  it hsa key  we will do something.

接著我們再看一段簡單的程式:
  
            int x = 0;
            int y = 0;
            int z =0;
            while (z < 3)
            {
                if (IsTrue())
                    x++;
                else
                    y++;

                if (x == 3)
                    x_event();
                if (y == 4)
                {
                    y_event();
                }
            }

我是故意寫那麼醜的,我相信大多數的人看完都只有一個感想  WhatThe....
接著我們再看看修改過的程式:


            static int Three_People_Out =3;
            int Strike = 0;           
            int ball = 0;
            int out_People =0;
            while (out_People < Three_People_Out)
            {
                if (IsInStrikeZone())
                    Strike++;
                else
                    ball++;

                if (Strike == 3)
                    StrikeOut();
                if (ball == 4)
                {
                    base_on_balls();
                }
            }

相信看到修改過的程式應該可以看出這是在寫有關棒球的程式。

if IsInStrikeZone 則 好球++ 否則壞球++
if 三好球  則 StrikeOut
if 四壞球  則 保送 ...

善用命名可以讓我們不需要使用大量的註解,卻可以寫出可讀性高的程式。
命名真是一門藝術!!

2013年11月9日 星期六

[SourceCodeControl] Bitbucket 介紹

只要是開發project都需要做版本控管還有Issue Tracking,目前用過的版控工具有 SVN 和 Git
兩個的差異主要是:集中式管理以及分散式管理的差別。



使用上的差異

用了Git 之後就可以很快發現他的便利性,
例如: cherry-pick  可以將一次commit所做的變更快速的套至其他的Branch 甚至是project,
以及  Git可以在local端自行開發,然後可以只push部分的資料到remote端,
也就是說local 跟 remote 可以是不同的線,但是SVN的話就不行,server跟local是同一條線。


使用Git

既然要使用Git那就必須有remote端 網路上常常會聽到有人使用 GitHub,他是免費的server  但是免費的版本只能創建 public 的 repository 這讓我有點困擾。
有些project我希望他不是公開的,這個原因讓我去搜尋了其他可用的網站。


BitBucket

跟GitHub一樣提供remote端,但是BitBucket額外提供了  private 的 repository
雖然project的人數有限制,但對於小團隊來說是足夠的 ,並且他還提供了Issue Tracking的工具


以及類似Agile的任務領取指派介面



對我來說這樣的功能算是非常的充裕,最後....讓我們開始開發吧!! XD

[UI] 挖洞初體驗

最近跟Design Team的要了一些工具  然後開始嘗試設計

這次嘗試的是如何挖洞....

以下是成品


這個洞挖了我兩個多小時.....

感覺還有很大的進步空間  

2013年11月6日 星期三

Android 開發 (六) Fragment replace

Fragment 能使我們在多個頁面之間做切換
以下是簡單的範例

MainActivity.java

public class MainActivity extends Activity 
 implements myFragment.OnClickListener{

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  myFragment newFragment = new myFragment();
        FragmentTransaction transaction = this.getFragmentManager().beginTransaction();

        transaction.add(R.id.rela, newFragment, "first");
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        transaction.commit();
 }
    
 public void onClick()
 {
  Log.d("Ted","what");
  NxtFragment newFragment = new NxtFragment();
        FragmentTransaction transaction = this.getFragmentManager().beginTransaction();
                     
        transaction.replace(R.id.rela, newFragment);
        transaction.addToBackStack(null);
        transaction.commit();
 }


首先 我們在myFragment的class 裡面製做了一個介面 myFragment.OnClickListener
當click event被觸發時 來呼叫切換的動作

在onCreate裡 我們將myFragment 加入至 Main page 裡

onClick 當click事件被觸發時 將 Main page裡的 fragment做更換


myFragment.java


public class myFragment extends Fragment {
 OnClickListener mCallback;

    
    public interface OnClickListener {       
        public void onClick();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

        try {
            mCallback = (OnClickListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString()
                    + " must implement OnClickListener");
        }
    }
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.fragment_layout, container, false);
        Button btn = (Button)v.findViewById(R.id.ClickBtn);
        btn.setOnClickListener(new Button.OnClickListener()
        {

   @Override
   public void onClick(View arg0) {
    // TODO Auto-generated method stub
    mCallback.onClick();
   }         
        });
        return v;
    }

在onAttach 裡將 MainActivity 傳給 myFragment

使 myFragment能在 click event被觸發時能呼叫 onClick 的method 來做換頁的動作


 以下是這個範例的示範圖

這是第一頁  當按下 Click me 之後會跳至第二頁

 按下之後的結果


小結:

1.Fragment 使 Developer在製作手機與平板之間的UI時  相對上較便利
如上範例所示兩個fragment之間的切換 
假設在平板上一個是在左邊一個是在右邊  在手機上變成第一頁另一個變成第二頁
用fragment 只需要判斷  是要將fragment add到 R.id.rela (平板)  還是  replace掉(手機)

2.Fragment 支援  Back的功能  這跟windows metro 的 navigate back功能蠻像的
就是回到前一頁

2013年11月5日 星期二

Android 開發 (五) 如何讀取圖片

利用以下的程式碼能將圖片讀取出來

 public void GetImage()
 {
  String[] projection = {MediaStore.Images.Thumbnails.DATA};
  
 
      Cursor cursor = this.getContentResolver().query( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
          projection, 
          null,      
          null,
          null);
  

  int columnIndex = cursor.getColumnIndex(MediaStore.Images.Thumbnails.DATA);
  for(int i =0;i<cursor.getCount();i++){
      cursor.moveToPosition(i);
 
     
      Bitmap bm = BitmapFactory.decodeFile(cursor.getString(columnIndex));
      map.put("img",  bm );
  }
  cursor.close();
 }



接著只需要將 map裡的資料 餵給imageview 即可

2013年11月1日 星期五

[Other] 如何將 Syntaxhighlighter 到blog裡

最近在思考如何在blog裡用較明顯的方式顯示程式
所以找到了 Syntaxhighlighter
首先先找到範本 -> 點擊編輯Html



接著使用搜尋找到</head>

接著在</head>之上貼入以下的文字

<!-- SyntaxHighlighter -->
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shCore.css" rel="stylesheet" type="text/css" />
<link href="http://alexgorbatchev.com/pub/sh/current/styles/shThemeEmacs.css" rel="stylesheet" type="text/css" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js" type="text/javascript" />

<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushAS3.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushBash.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushColdFusion.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCSharp.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCpp.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushCss.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushDelphi.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushDiff.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushErlang.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushGroovy.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJScript.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJava.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushJavaFX.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPerl.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPhp.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPlain.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPowerShell.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushPython.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushRuby.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushScala.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushSql.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushVb.js" type="text/javascript" />
<script src="http://alexgorbatchev.com/pub/sh/current/scripts/shBrushXml.js" type="text/javascript" />
 
<script language="javascript" type="text/javascript">
 SyntaxHighlighter.config.bloggerMode = true;
 SyntaxHighlighter.all();
</script>
<!-- SyntaxHighlighter END -->




其中前兩個shCore 還有  shThemeEmcs 是可以做替換的
可以將圖片中的任意一個名稱輸入做替換
接著只需要在編寫文章的時候加入  
<pre class="brush:java;">....程式們 </pre>
就可以了

2013年10月31日 星期四

Android 開發 (四) custom Gridview Selector

在設計app 時通常不會使用default的設計
所以客製化的技巧也成為重要的課題
今天寫的是讓使用者在點選 GridView時
可以看到顏色的feedback



Main

Main 只是簡單的將 gridview 做資料的設定
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridView gv = (GridView) this.findViewById(R.id.gridview);
String[] item = new String[] {"1", "2","3","4","5","6","7","8","9"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
                      android.R.layout.simple_expandable_list_item_1,item);
gv.setAdapter(adapter);
}


activity_main

activity_main 在一個relativelayout裡放一個Gridview 並限定每行只能有三個item
其中android:listSelector="@drawable/customselect" 我們使用了我們客製化的selector


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity" >

    <GridView
        android:id="@+id/gridview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:listSelector="@drawable/customselect"
        android:numColumns="3"/>
</RelativeLayout>


customselect.xml

在customselect中 我們設定了在 pressed state時  更改顏色

<?xml version="1.0" encoding="UTF-8"?>
<selector
  xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape > <solid android:color="#77007777" />   </shape>
        </item>
</selector>

結果圖示


尚未做點選的圖案





點選時的圖片


2013年10月25日 星期五

CleanCode 讀書筆記 (五) 錯誤處理II 從呼叫者角度定義例外類別

先看看下面的例子
public void GetContain()
{
  try{
      str.contains("KeyWord");
  catch(NullPointerException ex)
  {
       throw new NullPointerException ();
  }
 }
 public String executeHttpGet(String Url) {
  HttpClient httpClient = new DefaultHttpClient();
  String result="";
  try{
   HttpResponse response = httpClient.execute(request);
   if(response.getStatusLine().getStatusCode()==HttpURLConnection.HTTP_OK)
   {
      result = EntityUtils.toString(response.getEntity());    
   }
  }
  catch(NullPointerException ex)
  {
       throw new NullPointerException ();
  }
  finally
  {
       httpClient.getConnectionManager().shutdown();
  }
  return result;   
 }

以上的code 在出現例外時會丟出 NullPointerException 
然而這樣子的寫法使用者實際上並不知道發生甚麼事情
到底NullPointerException是由誰丟出來的呢?
 比較好的寫法應該像第二個範例


public void GetContain()
 {
  try{
        str.contains("KeyWord");
  catch(NullPointerException ex)
  {
       throw new DataAccessException("Get Contain Fail",ex);
  }

 }
 public String executeHttpGet(String Url) {
  HttpClient httpClient = new DefaultHttpClient();
  String result="";
  try{  
   HttpResponse response = httpClient.execute(request);
   if(response.getStatusLine().getStatusCode()==HttpURLConnection.HTTP_OK)
   {
       result = EntityUtils.toString(response.getEntity());    
   }
  }
  catch(NullPointerException ex)
  {
      throw new DataAccessException("Http Request Fail",ex);
  }
  finally
  {
   httpClient.getConnectionManager().shutdown();
  }

  return result;   
 } 

以上的code可以很清楚的知道例外是由誰產生
這樣的好處 讓我們將原本無法理解的exception變成我們所能理解exception 
讓我們知道確切的問題點在哪

接著下面的例子是將第三方API可能產生的exception 利用單一例外處理使得我們更好管理
將所有的例外使用單一的例外(DataAccessException)去處理
由於第三方的API 出現例外時不管錯誤為何
通常處理的方式都會類似
所以可以使用這樣的方式來處理
可以將該 API 包成一個class 專門處理該API

 
ThirdPartyHandler{
    private ThirdPartyAPI ThirdParty;
    public ThirdPartyHandler(ThirdParty)
    {
       this.ThirdParty = ThirdParty;
    }
    public String executeThirdPartyAPI(String Url) {
    try{  
        result = ThirdParty.Execute();
    }
    catch(NullPointerException ex)
    {
       throw new DataAccessException(ex);
    }
    catch(IOException ex)
    {
       throw new DataAccessException(ex);
    }
    finally
    {
      ...
    }
    return result;   
   }
}

從呼叫者角度定義例外類別告訴我們
利用封裝自定義 exception或者是增加message 讓我們更輕易的管理例外

2013年10月23日 星期三

Android 開發 (三) 擷取圖片的一半

 以下的程式示範如何在android裡  只顯示圖片的左半部


 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  ImageView myImageView = (ImageView)findViewById(R.id.imageview);
  Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.image1);  
  Bitmap resizedbitmap = Bitmap.createBitmap(bmp , 0, 0,bmp.getWidth()/2 , bmp.getHeight());
  myImageView.setImageBitmap(resizedbitmap);

        ImageView myImageView2 = (ImageView)findViewById(R.id.imageview2);
        myImageView2.setImageResource(R.drawable.image1);
        
 }
利用 BitmapFactory.decodeResource 取得 Bitmp之後
再使用  Bitmap.createBitmap 給定  start_x , start_y ,width, height
取得希望得到的Bitmap範圍

2013年10月21日 星期一

OOAD原則 (三) dependency injection 相依性注入

相依性注入(DI)是一個能夠幫助我們解耦合的方法
舉例來說 Strategy Pattern 就有用到這個idea
    abstract class Sorting
    {
        abstract public void Process();
    }
    class SortAlgorithm
    {
        private Sorting algorithm;
        public SortAlgorithm(Sorting algorithm)
        {
            this.algorithm = algorithm;
        }
        public void Sort()
        {
            algorithm.Process();
        }
    }
    class QuickSort : Sorting
    {
        public override void Process()
        {
            System.Diagnostics.Debug.Write("QickSort Algorithm");
        }
    }
    class MergeSort : Sorting
    {
        public override void Process()
        {
            System.Diagnostics.Debug.Write("MergeSort Algorithm");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Sorting quicksort = new QuickSort();
            SortAlgorithm sortalgorithm = new SortAlgorithm(new QuickSort());
            sortalgorithm.Sort();
            sortalgorithm = new SortAlgorithm(new MergeSort());
            sortalgorithm.Sort();
        }
    }


從例子來說   Sorting 真正的實體是由  QuickSort 或 MergeSort來決定的
這部分就是 DI的精神  這樣子寫就可以讓 SortAlgorithm與 QuickSort 以及 MergeSort 解耦合
如果之後有需要做SortAlgorithm的測試時  可以 丟入一個  寫死的演算法 TestSort  回傳固定的排序  這樣就可以測試 SortAlgorithm是否有問題

OOAD原則 (二) Single Responsibility Principle 單一責任原則

SingleResponsibilityPrinciple主張 一個類別只有一個被修改的理由  一個類別只有一個職責
當一個類別可以做很多事情時  我們會稱他為God class  
通常這種類別的重覆使用性很低  因為他會跟很多其他的類別耦合  很難重覆使用

    class GodClass
    {
        private bool IsTurnOnTV = false;
        private bool IsTurnOnRadio = false;
        private bool IsTurnOnLight = false;
        public void TurnOnTV()
        {
            IsTurnOnTV = true;
            Console.WriteLine("TurnOnTV");
        }
        public void TurnOffTV()
        {
            IsTurnOnTV = false;
            Console.WriteLine("TurnOffTV");
        }
        public void TurnOnRadio()
        {
            IsTurnOnRadio = true;
            Console.WriteLine("TurnOnRadio");
        }
        public void TurnOffRadio()
        {
            IsTurnOnRadio = false;
            Console.WriteLine("TurnOffRadio");
        }

        public void TurnOnLight()
        {
            IsTurnOnLight = true; ;
            Console.WriteLine("TurnOnLight");
        }
        public void TurnOffLight()
        {
            IsTurnOnLight = false;
            Console.WriteLine("TurnOffLight");
        }

        public void Execute(string command)
        {
            switch (command)
            {
                case "TV":
                    if (IsTurnOnTV)
                        TurnOffTV();
                    else
                        TurnOnTV();
                    break;
                case "Light":
                    if(IsTurnOnLight)
                        TurnOffLight();
                    else
                        TurnOnLight();
                    break;
                case "Radio":
                    if (IsTurnOnRadio)
                        TurnOffRadio();
                    else
                        TurnOnRadio();
                    break;
                default:
                    throw new Exception();
            }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            GodClass godclass = new GodClass();
            godclass.Execute("Light");
            godclass.Execute("Light");
            Console.ReadKey();
        }
    }

上面是一個 god 類別的例子  他可以做 TV的開關  Light的開關 以及  Radio的開關
簡單的說就是甚麼都可以做  這樣寫好像不錯  所有控制的項目都整理在一個類別裡
但是  當有新的功能要要加入時  就得對execute的程式做修改  這樣就違反了OpenClosePrinciple
然後當新的需求不斷進來  最後  這個類別就會變成一個龐大而且充滿flag的類別
這樣的程式是非常難以維護的
但是  我們可以用下面的方式來做修改


  abstract class Command
    {
        public abstract void Execute();
    }
    class LightCommand : Command
    {
        private bool IsTurnOnLight = false;
        public void TurnOnLight()
        {
            IsTurnOnLight = true; ;
            Console.WriteLine("TurnOnLight");
        }
        public void TurnOffLight()
        {
            IsTurnOnLight = false;
            Console.WriteLine("TurnOffLight");
        }
        public override void Execute()
        {
            if (IsTurnOnLight)
                TurnOffLight();
            else
                TurnOnLight();
        }
    }

    class TVCommand : Command
    {
        private bool IsTurnOnTV = false;
        public void TurnOnTV()
        {
            IsTurnOnTV = true;
            Console.WriteLine("TurnOnTV");
        }
        public void TurnOffTV()
        {
            IsTurnOnTV = false;
            Console.WriteLine("TurnOffTV");
        }
        public override void Execute()
        {
            if (IsTurnOnTV)
                TurnOffTV();
            else
                TurnOnTV();
        }
    }

    class RadioCommand : Command
    {
        private bool IsTurnOnRadio = false;
        public void TurnOnRadio()
        {
            IsTurnOnRadio = true;
            Console.WriteLine("TurnOnRadio");
        }
        public void TurnOffRadio()
        {
            IsTurnOnRadio = false;
            Console.WriteLine("TurnOffRadio");
        }
        public override void Execute()
        {
            if (IsTurnOnRadio)
                TurnOffRadio();
            else
                TurnOnRadio();
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Command LightCommand = new LightCommand();
            LightCommand.Execute();
            LightCommand.Execute();
            Console.ReadKey();
        }
    }
基本上這兩段程式碼做的事情是一樣的
但是上面這一段程式碼
當新的需求進來時   例如我想新增電腦開關
不論我在新的類別裡面修改或新增了多少東西
並不會影響到其他已經寫好的類別
對維護者來說  這樣的類別也是相對好維護的  flag少而且執行的功能明確
這就是把責任切開的好處

2013年10月17日 星期四

CleanCode 讀書筆記 (四) 邊界

邊界主要是描述使用第三方API時如何想辦法
將API與我們內部的程式分隔開來   減低耦合
裡面有提到可以使用 Adapter  (轉接器)
Adapter是系統 與 API 之間的橋梁
使得API有所更動時  不會影響到系統  只需要在 Adapter上做修改即可

以下兩個範例  廠商在開發初期出版了  FactoryAPI_0
以及過了一陣子出版了新版  FactoryAPI_1
在兩版之間  裡面有一個method做了修正
如果沒有使用Adapter  會使得主程式必須做修正  修正後也可能會衍生出bug
但是使用Adapter的話  由於修改都在一個class內   相對來說比較不會衍生出新問題

廠商舊版FactoryAPI_0


    public class FactoryAPI_0
    {
        public void Make()
        {
            System.Diagnostics.Debug.Write("Make");
        }
    }

    public class FactoryAdapter
    {
        private FactoryAPI_0 ApI;
        public FactoryAdapter(FactoryAPI_0 API)
        {
            this.ApI = API;
        }
        public void Process()
        {
            ApI.Make();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            FactoryAPI_0 api = new FactoryAPI_0();
            FactoryAdapter adapter = new FactoryAdapter(api);
            adapter.Process();
            Console.Read();

        }
    }



廠商新版 FactoryAPI_1


    public class FactoryAPI_1
    {
        public void Execute()
        {
            System.Diagnostics.Debug.Write("Make");
        }
    }
    public class FactoryAdapter
    {
        private FactoryAPI_1 ApI;
        public FactoryAdapter(FactoryAPI_1 API)
        {
            this.ApI = API;
        }
        public void Process()
        {
            ApI.Execute();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            FactoryAPI_0 api = new FactoryAPI_0();
            FactoryAdapter adapter = new FactoryAdapter(api);
            adapter.Process();
            Console.Read();

        }
    }
由上面兩個範例可以發現    主程式並沒有被更動到
所有的更動都在 class 內部
造成的影響也都在class內   主程式成功的與API 分開

簡潔的程式邊界
當改變來臨時
好的設計能適應變化
不好的設計必須重新改寫或投入大量時間做修正

API 可以用封裝的方式  或者使用adapter
使API 被修正時  我們只需要做及少的修正

2013年10月16日 星期三

CleanCode 讀書筆記 (三) 錯誤處理I

裡面提到了一些例外的處理方式
其中有一點我覺得蠻重要的
不要回傳null  
與其回傳null不如回傳一個special case
這樣呼叫這個class的人就不需要去做 if(xx ==null)的判斷
也不會出現 NullPointerException  以下是範例


    abstract class EmployeeData
    {
        protected string name;
        protected string birthday;
        protected string address;
        public string GetName()
        {
            return name;
        }
    }
    class Ted : EmployeeData
    {
        public Ted()
        {
            name = "Ted";
            birthday = "1009";
            address = "Taipei";
        }
    }
    class NullEmployeeData : EmployeeData
    {
        public NullEmployeeData()
        {
            name = "";
            birthday = "";
            address = "";
        }
    }
    class EmployeeList
    {
        public EmployeeData GetData(string name)
        {
            switch (name)
            {
                case "Ted":
                    return new Ted();
                default:
                    return null;
            }

        }
    }
    class EmplyeeListBetter
    {
        public EmployeeData GetData(string name)
        {
            switch (name)
            {
                case "Ted":
                    return new Ted();
                default:
                    return new NullEmployeeData();
            }

        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            EmployeeData employeedata;
            EmployeeList badEmployeeList = new EmployeeList();

            employeedata = badEmployeeList.GetData("Marry");
            if (employeedata!=null)
                employeedata.GetName();


            EmplyeeListBetter betterEmployeeList = new EmplyeeListBetter();
            employeedata = betterEmployeeList.GetData("Marry");
            employeedata.GetName();
            Console.ReadKey();
        }
    }

上面的範例說明了當 user想要去Get Marry的資料時  系統內並沒有這個資料
badEmployeeList  return 了 null
然而 betterEmployeeList 回傳了  NullEmployeeData這個物件
這樣寫的好處是就算user沒有做  if (employeedata!=null) 這個判斷式
也不需要擔心 NullPointerException 的發生  系統還是可以繼續 run 下去