2013年10月16日 星期三

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