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 下去

CleanCode 讀書筆記 (二) 物件及資料結構

在這個章節提到的重點是
結構話化程式碼容易添加新的函式  而不需要變動已有的資料結構
而物件導向得程式碼則是容易添加新的類別而不用變動已有的函式


以下是結構化的程式碼
 
    class SalaryCalculator
    {
        public double Calculate(string person)
        {
            if (person.Equals("boss"))
            {
                return 500 * 22 * 24;
            }
            else if (person.Equals("DayWorker"))
            {
                return 200 * 22 * 24;
            }
            else if (person.Equals("partTime"))
            {
                return 100 * 22 * 24;
            }
            throw new NullReferenceException();
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            SalaryCalculator calculator = new SalaryCalculator();
            Console.WriteLine(calculator.Calculate("boss"));
            Console.ReadLine();
        }
    }
假設要添加一個SetWorkDays的函式
是蠻容易而且不會變動到已有的結構但是如果要新增一個
NightWorker (夜班) 人員的薪資  就是必要更改現有的函式

    class SalaryCalculator
    {
        private int workHours = 24;
        public void SetWorkHours (int workHours )
        {
            this.workHours = workHours ;
        }
        public double Calculate(string person)
        {
            if (person.Equals("boss"))
            {
                return 500 * 22 * workHours ;
            }
            else if (person.Equals("DayWorker"))
            {
                return 200 * 22 * workHours ;
            }
            else if (person.Equals("partTime"))
            {
                return 100 * 22 * workHours ;
            }
            else if (person.Equals("NightWorker"))
            {
                return 300 * 22 * workHours ;
            }
            throw new NullReferenceException();
        }
    }


接著我們看看  物件導向的程式

    
    interface CalculatorBase
    {        
        public double Calculate();
    }
    class BossSalaryCalculate : CalculatorBase
    {
        private int workHours = 8;
        public override double Calculate()
        {
            return 500 * 22 * workHours;
        }
    }
    class DayWorkerSalaryCalculate : CalculatorBase
    {
        private int workHours = 8;
        public override double Calculate()
        {
            return 200 * 22 * workHours;
        }
    }
    class partTimeSalaryCalculate : CalculatorBase
    {
        private int workHours = 8;
        public override double Calculate()
        {
            return 100 * 22 * workHours;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            //SalaryCalculator calculator = new SalaryCalculator();
            CalculatorBase calculator = new BossSalaryCalculate();
            Console.WriteLine(calculator.Calculate());
            Console.ReadLine();
        }
    }


    interface  CalculatorBase
    {             
       public double Calculate();
       public void SetWorkHours(int workHours);
    }
    class BossSalaryCalculate : CalculatorBase
    {
        private int workHours = 8;
        public void SetWorkHours(int workHours)
        {
            this.workHours = workHours;
        }
        public double Calculate()
        {
            return 500 * 22 * workHours;
        }
    }
    class DayWorkerSalaryCalculate : CalculatorBase
    {
        private int workHours = 8;
        public void SetWorkHours(int workHours)
        {
            this.workHours = workHours;
        }
        public double Calculate()
        {
            return 200 * 22 * workHours;
        }
    }
    class partTimeSalaryCalculate : CalculatorBase
    {
        private int workHours = 8;
        public void SetWorkHours(int workHours)
        {
            this.workHours = workHours;
        }
        public double Calculate()
        {
            return 100 * 22 * workHours;
        }
    }
    class NightWorkerSalaryCalculate : CalculatorBase
    {
        private int workHours = 8;
        public void SetWorkHours(int workHours)
        {
            this.workHours = workHours;
        }
        public override double Calculate()
        {
            return 300 * 22 * workHours;
        }
    }
上面的程式使用了interface 將計算的方式分隔開來使我們在新增NightWorker的時候
並不會更改到其他的class  但是當我們需要添加新的函式時反而造成了很大的困擾
必須針對每個class 實做SetWorkHours  當class一多  這樣是很痛苦的

所以必須觀察系統所需要的
若要增加worker的薪資計算的彈性  就要使用物件導向的設計
若要增加worker的函式功能的彈性  就要使用結構化的設計

2013年10月14日 星期一

Android 開發 (二) reflection java 動態載入class的方法

為了妥善利用resource 所以使用動態的方式載入class
以下為android的範例

先create一個interface

package Printer;

public interface MessgaePrinter {
 public String print();
}

接著create一個class
package Printer;

public class HelloWorldPrinter implements MessgaePrinter{

 /**
  * @param args
  */
 
 public static void main(String[] args) {
  // TODO Auto-generated method stub

 }

 @Override
 public String print() {  
  return "Hello World!";
 }

}
接著將 檔案 export 成 jar 檔  export的file會在 project路徑底下
接著使用 cmd 將 java 轉成android可識別的檔案
在資料夾  sdk\build-tools\android-4.2.2 底下打入
dx --dex --output=test.jar printer.jar


接著在要執行的地方打入
public class MainActivity extends Activity {

 @SuppressLint("NewApi")
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  DexClassLoader cl = new DexClassLoader("FilePath",
    "FilePath", null, getClassLoader());
  try {
   Class dynamiclib = cl.loadClass("Printer.HelloWorldPrinter");
   MessgaePrinter lib = (MessgaePrinter)dynamiclib.newInstance();
   Log.d("Ted",lib.print());
  } catch (ClassNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (InstantiationException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IllegalAccessException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }
 }

2013年10月6日 星期日

CleanCode 讀書筆記 (一) 有意義的命名

最近正在讀 Clean Code 第二章剛讀完  就順便來整理一下讀後心得
有意義的命名  目的就是在讓人讀完程式碼之後就知道在幹嘛
舉例來說
C#
        
        public List<int[]> GetList(List<int[]> LJ)
        {
            List<int[]> ListK = new List<int[]>();
            foreach (int[] i in LJ)
            {
                if (i[0] == 1)
                {
                    ListK.Add(i);
                }
            }
            return ListK;
        }
看完上面的範例之後其實完全不知道程式在幹嘛
讓我們在來看看下面的範例
          
        public List<int[]> GetOpenTVList(List<int[]> TVList)
        {
            List<int[]> TV_OnList = new List<int[]>();
            foreach (int[] TV in TVList)
            {
                if (TV[State] == State_TV_On)
                {
                    TV_OnList .Add(TV);
                }
            }
            return TV_OnList;
        }
上下兩段程式的執行結果其實是一模一樣的
但是我相信下面的code看起來比較知道在幹嘛......吧(至少我是這樣覺得~_~)


這個function的工作就是從所有的電視中找出目前是開著的電視


關於變數的命名盡量使用有意義的命名 例如 ListJ vs TVList

使用可發音的變數  LJ vs TVList
使用不可發音的變數  會讓讀code的人去猜測他的用處  這是非常不好的情況

關於數字盡量使用variable去代替  例如  1 vs State_TV_On
當程式變得龐大時  搜尋 1 和 State_TV_On 的結果是完全不同的

在介面實作部分
舉例來說許多人習慣這樣寫
 
    abstract class ICellPhoneFactory
    {
    }
    class CellPhoneFactory
    {
    }
這樣寫其實並不好  我們並不希望使用者知道我們給他的interface
所以與其這樣寫不如
 
    abstract class CellPhoneFactory
    {
    }
    class OEMCellPhoneFactory
    {
    }
使用者只需知道這是一個CellPhoneFactory

關於類別的命名  應該要清楚  例如  Customer vs Data

關於方法的命名  要取出資料就應該有 Get   例如  GetCustomerData
                              要設定資料就應該有 Set    例如  SetCustomerId
                              要判斷資料就應該有 Is      例如  IsMyCustomer

書中有特別強調  命名的時候不要裝可愛 不要發揮幽默感  請正常的命名
沒有人會知道  IsMmmmmonsterKill 這個function是幹嘛用的 XDD
請用正常的命名方式  IsKillManyPeople

在方法得命名時不要出現兩種命名方式  例如  GetData 和  AccessData
請統一用同一種方式命名

在套用某些pattern或演算法時  盡量使用那些術語  讓讀程式的人能一眼看出這是在做甚麼
例如  CustomerFactory  閱讀的人一眼就知道這是個factory

結論  :
寫程式的命名就像是在寫作文一般
最好的programmer  就是讓使用者一眼就看出你現在想幹嘛
使用者根本不需要去看  function裡面實踐甚麼
只需要看主架構就知道程式的功能
好好的學習命名    這也是一門學問

2013年10月5日 星期六

Android 開發 (一) Singleton

在Android 使用 Singleton的時候要特別小心
由於Android 的 App 在 離開的時候並不會把static 的class destory掉
所以當 App 重新開啟時  可能會造成某些問題
例如
當App重開時  由於Activity已經更換了但是假設有某些item是base在舊的Activity
就會出現error


當App重開時 在App重開時將新的Activity做更換可以避免掉這個問題
或者是使用下面的方式也可以避免掉一些錯誤

public class Dialog extends Application{

       private static Dialog instance;

       public static Dialog GetInstance()

       {

                  return instance;

       }

       public final void onCreate(){

              super.onCreate();

              instance = this;

       }

}



設計模式 (七) Observable Pattern

觀察者模式
 當某個狀態改變時就通知所有訂閱者
 目的是要讓互動的物件做鬆綁 兩個物件可以作互動 
但彼此之間並不太清楚彼此細節
下面的範例是 當有個人Post一篇文章
 所有他的定閱者都會收到通知 然後都可以做他們相對應的動作

        public interface Notifier
        {
             void Notify();            
        }
        public class Paul : Notifier
        {
            public void Notify()
            {
                Console.WriteLine("Paul 立刻去看文章");
            }
        }
        public class Ted : Notifier
        {
            public void Notify()
            {
                Console.WriteLine("Ted 把文章加入書籤");
            }
        }
        public class Tina : Notifier
        {
            public void Notify()
            {
                Console.WriteLine("Tina 不理會");
            }
        }
        public class ArticlePoster
        {
            private List<Notifier> PersonList = new List<Notifier>();
            public void Add(Notifier person)
            {
                PersonList.Add(person);
            }
            public void PostArticle()
            {
                foreach (Notifier person in PersonList)
                {
                    person.Notify();
                }
            }
        }
        static void Main(string[] args)
        {
            ArticlePoster poster = new ArticlePoster();
            poster.Add(new Paul());
            poster.Add(new Tina());
            poster.Add(new Ted());
            poster.PostArticle();
            Console.ReadKey();
        }

2013年10月4日 星期五

設計模式 (六) Iterator Pattern

Iterator Pattern 為反覆器讓我們能夠取得collection內的元素(string)而不需要取得collection (List<string>,string[]) 這樣的好處讓我們可以將這些元素  一視同仁(printDevice)
讓我們方便處理

下面的sample  我們希用使用printDevice 去印出Ted 和 Paul身上的配件
但是他們兩個是使用不同的collection  Ted是使用List<string>  而 Paul是使用string[]
通常都會使用以下的寫法
 
      

        class Ted_Devices

        {

            private List<string> items = new List<string>();

            public Ted_Devices()

            {

                items.Add("Phone");

                items.Add("Bag");

                items.Add("Pad");

            }

            public List<string> GetItems()

            {

                return items;

            }

        }

        class Paul_Devices

        {

            private string[] items = new string[3];

            public Paul_Devices()

            {

                items[0]="Pencil";

                items[1]="Watch";

                items[2]="book";

            }

            public string[] GetItems()

            {

                return items;

            }

        }

        static void Main(string[] args)

        {

            Ted_Devices TD = new Ted_Devices();

            List<string> TD_list = TD.GetItems();

            Paul_Devices PD = new Paul_Devices();

            string[] PD_list = PD.GetItems();

            Console.WriteLine("===Ted list====");

            printDevice(TD_list);

            Console.WriteLine("====Paul list====");

            printDevice(PD_list);

            Console.ReadLine();

        }



        private static void printDevice(object obj)

        {

            if (obj is List<string>)

            {

                foreach (string item in obj as List<string>)

                {

                    Console.WriteLine(item);

                }

            }

            else

            {

                foreach (string item in obj as string[])

                {

                    Console.WriteLine(item);

                }

            }

        }
Result:
===Ted list====
Phone
Bag
Pad
====Paul list====
Pencil
Watch
book
這樣的缺點是
當需要印更多人的裝置而每個人使用的collection的格式都不同時
例如出現了 int[] , List<CustomData>, ....等等的資料時
printDevice 就會長的越來越可怕  判斷式越來越多
如果這時出現新的需求不是只需要印device
還需要針對每個人身上的配件去做其他項目的處理時....
這樣的code就會變的非常難以maintain以及新增功能

下面的寫法是使用Iterator的方式

public interface Iterator
    {
        bool hasNext();
        Object next();
    }
    class TedIterator : Iterator
    {
        private List<string> items;
        int position = 0;
        public TedIterator(List<string> items)
        {
            this.items = items;
        }
        public bool hasNext()
        {
            if (position &gt;= items.Count)
                return false;
            else
                return true;
        }

        public object next()
        {
            string CurrentItem = items[position];
            position++;
            return CurrentItem;
        }
    }
    class Ted_Devices
    {
        private List<string> items = new List<string>();
        public Ted_Devices()
        {
            items.Add("Phone");
            items.Add("Bag");
            items.Add("Pad");
        }
        public Iterator CreateIterator()
        {
            return new TedIterator(items);
        }
    }
    class PaulIterator : Iterator
    {
        private string[] items;
        int position = 0;
        public PaulIterator(string[] items)
        {
            this.items = items;
        }
        public bool hasNext()
        {
            if (position &gt;= items.Length || items[position]==null)
                return false;
            else
                return true;
        }

        public object next()
        {
            string CurrentItem = items[position];
            position++;
            return CurrentItem;
        }
    }
    class Paul_Devices
    {
        private string[] items = new string[3];
        public Paul_Devices()
        {
            items[0] = "Pencil";
            items[1] = "Watch";
            items[2] = "book";
        }
        public Iterator CreateIterator()
        {
            return new PaulIterator(items);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            Ted_Devices TD = new Ted_Devices();
            Iterator TD_list = TD.CreateIterator();
            Paul_Devices PD = new Paul_Devices();
            Iterator PD_list = PD.CreateIterator();
            Console.WriteLine("===Ted list====");
            printDevice(TD_list);
            Console.WriteLine("====Paul list====");
            printDevice(PD_list);
            Console.ReadLine();
        }

        private static void printDevice(Iterator obj)
        {
            while (obj.hasNext())
            {
                Console.WriteLine(obj.next());
            }
        }
    }
Result:
===Ted list====
Phone
Bag
Pad
====Paul list====
Pencil
Watch
book
從Main function就可以很清楚的看到  兩個人的collection  List<string> 和 string[]都被轉為iterator
若有需要對data做進一步的處理  他們可以使用相同的寫法(printDevice) 去印出他們身上的device
若有進一步的處理  也可以使用同樣一套寫法 (不需要用if else去區分是誰的device)
這樣子在maintain以及開發上就會相對輕鬆許多

2013年10月3日 星期四

設計模式 (五) Visitor Pattern

參觀者模式  其實概念就是將演算法跟結構做分離
下面的範例使用將學生的成績利用兩個演算法做排序 ( BigToSmall , SmallToBig)
使用訪問者模式的好處是
1.針對結構新增演算法時只需要新增class即可
2.參觀者所做的操作都集中在class 裡 trace 較方便
缺點是

1.當需要針對該結構做更改時  會影響到所有演算法 (更改會較困難)



    abstract class Visitor

    {

        abstract public void Visit(Element element);

    }



    class BigToSmallVisitor : Visitor

    {



        public override void Visit(Element element)

        {

            ScoreSheet sheet = (ScoreSheet)element;



            sheet.ScoreList.Sort((x, y) => { return -x.Score.CompareTo(y.Score); });



            foreach (ScoreData item in sheet.ScoreList)

            {

                Console.WriteLine(item.Name + " / " + item.Score);

            }

        }

    }



    class SmallToBigVisitor : Visitor

    {



        public override void Visit(Element element)

        {

            ScoreSheet sheet = (ScoreSheet)element;



            sheet.ScoreList.Sort((x, y) => { return x.Score.CompareTo(y.Score); });



            foreach (ScoreData item in sheet.ScoreList)

            {

                Console.WriteLine(item.Name + " / " + item.Score);

            }

        }

    }



    class ScoreData

    {

        private int score;

        private string name;

        public int Score

        {

            get { return score; }

            set { score = value; }

        }

        public string Name

        {

            get { return name; }

            set { name = value; }

        }



        public ScoreData(string name, int score)

        {

            this.Name = name;

            this.Score = score;

        }

    }



    abstract class Element

    {

        abstract public void Accept(Visitor visitor);

    }



    class ScoreSheet : Element

    {

        public List<ScoreData> ScoreList = new List<ScoreData>();

        public void Add(ScoreData data)

        {

            ScoreList.Add(data);

        }

        public override void Accept(Visitor visitor)

        {

            visitor.Visit(this);

        }

    }

    public class VisitorApp

    {

        public static void Main(string[] args)

        {

            ScoreSheet sheet = new ScoreSheet();

            sheet.Add(new ScoreData("John", 100));

            sheet.Add(new ScoreData("Marry", 90));

            sheet.Add(new ScoreData("Ted", 60));

            sheet.Add(new ScoreData("Bob", 70));

            BigToSmallVisitor bigtosmall = new BigToSmallVisitor();

            SmallToBigVisitor smalltobig = new SmallToBigVisitor();

            Console.WriteLine("=================");

            sheet.Accept(bigtosmall);

            Console.WriteLine("=================");

            sheet.Accept(smalltobig);

            Console.ReadKey();

        }

    }
Result:
 =================
 John / 100 
 Marry / 90 
 Bob / 70 
 Ted / 60 
 ================= 
 Ted / 60 
 Bob / 70 
 Marry / 90 
 John / 100