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進來





接著新的主題就出現了