2014年4月30日 星期三

[OO 設計模式] Simple Factory Pattern : 透過靜態方法來產生物件

轉載自 這裡 
前言 : 
Simple Factory模式又稱Static Factory模式。一個Simple Factory生產成品,而對客戶端隱藏產品產生的細節,物件如何生成,生成前是否與其它物件建立依賴關係,客戶端皆不用理會,用以將物件生成方式之變化 與客戶端程式碼隔離. 

Simple Factory 模式 : 
假設今天,您要建立一個 Message 物件,這個 Message 物件會有一連串的物件實例化與依賴關係建立流程 : 
  1. Session session = someWhere.getSession();  
  2. Message msg = new MimeMessage(session);  
  3. msg.setFrom(new InternetAddress(session.from));  
  4. msg.setSubject(session.subject);  
  5. msg.setText(session.text);  
  6. msg.setHeader(session.headerName, session.headerValue);  
  7. msg.setDate(session.date);  
  8. ...  
  9. mailService.send(msg);  
若這樣的流程是固定的,您可以將以上的細節封裝在一個類別中成為其靜態方法 : 
  1. public class MessageFactory {  
  2.     public static Message getMessage(Session session) {  
  3.         Message msg = new MimeMessage(session);  
  4.         msg.setFrom(new InternetAddress(session.from));  
  5.         msg.setSubject(session.subject);  
  6.         msg.setText(session.text);  
  7.                 msg.setHeader(session.headerName, session.headerValue);  
  8.         msg.setDate(session.date);  
  9.         ...  
  10.         return msg;  
  11.     }   
  12. }  
對使用 MessageFactory 的客戶端,可以這麼取得 Message 實例,而不用理會物件實例化與依賴關係建立的細節 : 
  1. Session session = someWhere.getSession();  
  2. Message message = MessageFactory.getMessage(session);  
  3. mailService.send(message);  
Simple Factory 的類別圖如下所示 : 
 
UML 類別圖中,傳回型態 Product 意指的是具有 Product 所定義之公開協定(物件上的公開方法)的物件,而不是專指 Java 中的 interface 定義. 
Simple Factory 使用靜態方法來簡單地隱藏物件建立細節。撇開靜態方法不談,隱藏物件建立的細節仍是 Factory 模式的重點,可將這個模式推至極緻,而成為一種通用、專門用來生成物件、建立依賴關係、甚至具備管理物件生命週期職責的輕量級容器. 

2014年4月29日 星期二

[ GroovyGN ] Synchronized Annotation for Synchronizing Methods

來源自 這裡 
Preface: 
Since Groovy 1.7.3 we have a new AST transformation: @Synchronized. This annotation is based on the Project Lombok Synchronized annotation. We can use the annotation on instance and static methods. The annotation will create a lock variable in our class (or we can use an existing variable) and the code is synchronized on that lock variable. Normally with the synchronized keyword the lock is on this, but that can have side-effects. 

Example: 
接著來看範例代碼, 知道 @Synchronized 使用的差別. 首先參考代碼如下: 
  1. import groovy.transform.Synchronized  
  2.   
  3. class ThdImpt implements Runnable{  
  4.     public static List Output = new ArrayList()  
  5.     public static int NEXT = 0  
  6.     public int runTimes  
  7.       
  8.     public static NEXT()  
  9.     {  
  10.         return NEXT++;  
  11.     }  
  12.       
  13.     public ThdImpt(int rt){runTimes = rt}  
  14.   
  15.     @Override  
  16.     public void run() {  
  17.         runTimes.times {  
  18.             def next = NEXT()             
  19.             synchronized(Output)  
  20.             {  
  21.                 Output.add(next);  
  22.             }  
  23.         }         
  24.     }     
  25. }  
  26.   
  27. int ThreadOpenCount = 10  
  28. ThreadGroup tg = new ThreadGroup()  
  29. ThreadOpenCount.times {  
  30.     new Thread(tg, new ThdImpt(100)).start()  
  31. }  
  32.   
  33. while(tg.activeCount() > 0) sleep(100)  
  34. printf "\t[Info] Done (%d)!\n", ThdImpt.Output.size();  
  35. Set intSet = new TreeSet()  
  36. ThdImpt.Output.each { num->  
  37.     if(num==null)   
  38.     {  
  39.         printf "Contain null data!\n"  
  40.         return  
  41.     }  
  42.     if(!intSet.add(num))  
  43.     {  
  44.         printf "%d is duplicate!\n", num  
  45.     }  
  46. }  
執行結果可能會發現 "??? is duplicate", 這是因為 NEXT() 方法在多線程情況下, 可能發生下面狀況: 
 

此時 ThreadA 與 ThreadB 就會拿到相同的 NEXT=0. 因此我們需要對 NEXT() API 進行 Synchronization. 在 Groovy 可以使用 @Synchronized 在 static 函數, 確保該函數每次只能被一個線程執行以避免 Race condition. 也就是說在 NEXT() API 加上 @Synchronized 便可以解決 "??? is duplicate" 的問題. 

Supplement: 
JavaTutorial - Synchronized Methods 
Java Synchronized Blocks 
Groovy groovy.transform.Synchronized annotation

2014年4月22日 星期二

[ Java 文章收集 ] Write Binary File in Java to be read by a C Program

來源自 這裡 
Preface: 
考慮有下面 C 代碼從檔案讀入 Binary 資料並轉為對應 struct 結構 "mystruct": 
  1. #include   
  2. #include   
  3.   
  4. struct mystruct  
  5. {  
  6.     unsigned char x;  
  7.     unsigned short y;  
  8.     unsigned int z;  
  9. };  
  10.   
  11. int main(void)  
  12. {  
  13.     FILE* myfile;  
  14.     struct mystruct ms;  
  15.   
  16.     myfile = fopen("data.bin""rb");  
  17.     if(!myfile)  
  18.     {  
  19.         printf("Unable to open the binary!\n");  
  20.         return EXIT_FAILURE;  
  21.     }  
  22.   
  23.     fread(&ms, sizeof(struct mystruct), 1, myfile);  
  24.     printf("mystruct size=%x\n", sizeof(ms));  
  25.     printf("unsigned byte=%x (%d)\n", ms.x, sizeof(ms.x));  
  26.     printf("unsigned short=%x (%d)\n", ms.y, sizeof(ms.y));  
  27.     printf("unsigned int=%u (%d)\n", ms.z, sizeof(ms.z));  
  28.   
  29.     fclose(myfile);  
  30.     return EXIT_SUCCESS;  
  31. }  
那有沒有辦法從 Java 代碼去產生 struct 結構 "mystruct" 的 Binary data? 下面的範例代碼告訴你怎麼做. 

Example: 
首先根據 C 資料結構 定義 unsigned char 佔 1 個 byte; unsigned short 佔 2 個 bytes; unsigned int 佔 2~4 bytes (我的機器是 4 bytes). 因此我們可以推知 mystruct 結構應該佔 1+2+4=7 個 bytes. 接著來看 Java 代碼如何產生 mystruct 的 binary 檔案: 
- NativeDataOutputStream.java 
  1. import java.nio.ByteBuffer  
  2. import java.nio.ByteOrder  
  3.   
  4.   
  5. class NativeDataOutputStream extends FilterOutputStream{  
  6.     public NativeDataOutputStream(){  
  7.   
  8.     }  
  9.   
  10.     public NativeDataOutputStream(OutputStream out) {  
  11.         super(out);  
  12.     }  
  13.   
  14.     public void writeShort(short value) throws IOException {  
  15.         ByteBuffer buffer = ByteBuffer.allocate(2).order(ByteOrder.nativeOrder());  
  16.         buffer.putShort(value);  
  17.         out.write(buffer.array());  
  18.     }  
  19.   
  20.     public void writeInt(int value) throws IOException {  
  21.         ByteBuffer buffer = ByteBuffer.allocate(4).order(ByteOrder.nativeOrder());  
  22.         buffer.putInt(value);  
  23.         out.write(buffer.array());  
  24.     }  
  25.   
  26.     public void writeLong(long value) throws IOException {  
  27.         ByteBuffer buffer = ByteBuffer.allocate(8).order(ByteOrder.nativeOrder());  
  28.         buffer.putLong(value);  
  29.         out.write(buffer.array());  
  30.     }  
  31.   
  32.     static void main(args) {  
  33.         short unsignedByte = 1;  
  34.         int unsignedShort = 2;  
  35.         long unsignedInt = 3;  
  36.   
  37.         NativeDataOutputStream out = new NativeDataOutputStream(new BufferedOutputStream(new FileOutputStream("data.bin"false)));  
  38.   
  39.         try {  
  40.             out.write     ((byte)  unsignedByte );  
  41.             out.writeShort((short) unsignedShort);  
  42.             out.writeInt  ((int)   unsignedInt  );  
  43.             out.flush();  
  44.         } finally {  
  45.             out.close();  
  46.         }  
  47.     }  
  48. }  
上面代碼透過 ByteBuffer 暫存資料的 byte array, 並透過 ByteOrder 給定系統的 Endianness (Big-endian or Little-endian, 就是系統怎麼解釋這個 array, 是從大到小還是從小到大.) 執行完畢後檢查產出的 "data.bin" 資訊確定 byte 數目如預期: 
 

接著使用 C 編譯完的程式 a.out 去讀 data.bin, 這時問題發生了: 
> ./a.out
mystruct size=8
unsigned byte=1 (1)
unsigned short=100 (2)
unsigned int=0 (4)
 # 1 怎麼不見了!!!

由 unsigned short=100 可以知道系統是 Big-Endian, 但是問題在於 unsigned int 的值居然是 0!!!! 後來知道 struct 在 compiler 的 align purpose 之下會自動加 padding
You're assuming that sizeof(struct) is the sum of the sizes of the members (7 bytes) and they follow each other one after the other with no gaps; this is actually unlikely. Usually structs are padded to 4 or even 8-byte boundaries, depending on the compiler. I think most compilers these days would pack that struct into 8 bytes: one for the char, an unused byte, two for the short, and four for the int. Therefore when you read the data, it's aligned incorrectly, so you get weird numbers; and likewise with the Java code (which doesn't confirm that it reads the number of bytes it expects, either, and gets one more byte than the 7 it wants.)

So the sad truth is that the layout of the C struct is compiler dependent, and you'll have to determine what your compiler does in terms of padding, and then match it with your Java program. Once you do that, you should be fine.

於是我使用下面代碼使用 C 代碼輸出 mystruct 的 binary 資料: 
  1. #include   
  2.   
  3. struct mystruct{  
  4.     unsigned char x;  
  5.     unsigned short y;  
  6.     unsigned int z;  
  7. };  
  8.   
  9. int main()  
  10. {  
  11.     FILE *pFile;  
  12.     pFile = fopen("data.bin""wb");  
  13.     if(!pFile)  
  14.     {  
  15.         printf("Fail to open file!\n");  
  16.         return -1;  
  17.     }  
  18.   
  19.     struct mystruct s = {1,1,1};      
  20.     fwrite(&s, sizeof(s), 1, pFile);  
  21.     fclose(pFile);  
  22.     return 0;  
  23. }  
上面產生的 data.bin 的 Hex string 如下: 
01 BC 01 00 01 00 00 00

因此由上可以知道當輸出 byte 時, 需要加上 padding "BC", 有了這個發現, 我們可以改寫 Java 代碼在 writeByte 的地方: 
  1. public void writeByte(byte value) throws IOException  
  2. {         
  3.     if(bo.equals(ByteOrder.BIG_ENDIAN))  
  4.     {  
  5.         out.write(value);  
  6.         out.write(HexByteKit.Hex2Byte("BC")); // Add Padding  
  7.     }  
  8.     else  
  9.     {  
  10.         out.write(HexByteKit.Hex2Byte("BC")); // Add Padding  
  11.         out.write(value);             
  12.     }  
  13. }  
這次產生的 data.bin 的 binary 內容如下: 
 

此時使用 C 讀入 data.bin 的結果就正常了: 
> a.out
mystruct size=8
unsigned byte=1 (1)
unsigned short=1 (2)
unsigned int=1 (4)

Supplement: 
flib - HexByteKit 
Java Primate Data Type

[ Py DS ] Ch3 - Data Manipulation with Pandas (Part5)

Source From  Here   Pivot Tables   We have seen how the  GroupBy  abstraction lets us explore relationships within a dataset. A pivot ta...