2017年12月24日

GSON基礎教學

GSON 前言

JSON是很常見的資料交換格式,在JAVA領域常用處理JSON的函式庫:GSON、FastXML和JSON-B,本章節會以GSON為主,學習目標如下

  • JSON格式說明
  • GSON 套件函式
  • GSON: 物件轉換JSON字串
  • GSON: JSON字串轉換物件

JSON 格式說明

JSON全名為JavaScript Object Notation,它是一種輕量級的資料交換格式,會大為流行的理由,主要是他比傳統用xml更輕巧且容易處理,JSON表達方式物件會用大括弧{},陣列則是用中括號[]。
用JSON字串來表達Employee的物件內容,由JSON字串可以知道物件name、age、sex和salary屬性。

JSON表示員工資料方式:
{“name”:”Jack Bryant”, “age”:18, “sex”:”M”,”salary”:3500.00}

JSON陣列表示方式:
跟我們使用JAVA的陣列方式類似,內容值可以是數字’、文字、布林、陣列、物件、null等等。
範例:
字串: [“紅”、”橙”、”黃”、”綠”、”青”、”藍”、”紫”}
布林: [true, true, false, false, true, true]

GSON 套件函式

Gson為google所發布的函式庫,主要將物件與json字串之間的轉換時方便使用。當我們將JAVA物件轉換成JSON字串稱為序列化,JSON字串轉換至JAVA物件稱為反序列化

enter image description here

GSON: 物件轉換JSON字串

有了JSON基本概念後,我們進入本章重點,首先我們需要建立員工類別(Employee),定義如下

物件 屬性
員工類別 Employee name 名字
age 年紀
sex 性別
salary 薪水
/**
 *  name:員工類別
 */
public class Employee implements Serializable {
    //constructor
    public  Employee(String name, double salary){
        this.name = name;
        this.salary = salary;
    }

    // employ's attribute
    private String name ;
    private int age ;
    private String sex;
    private double salary ;

    //setter and getter
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer() ;
        sb.append("") ;
        sb.append("name:"+name) ;
        sb.append("age:"+age) ;
        sb.append("sex:"+sex) ;
        sb.append("salary:"+salary) ;
        sb.append("") ;
        return sb.toString() ;
    }
   }

完成員工類別後,實際建立一個employee01 的物件,在使用Gson之前需要宣告Gson建構子,範例不管單數還是多數的物件,都是利用同一個toJson()方法來完成序列化轉成JSON字串,所使用起來相當方便。由結果可以發現兩件事情,一、我們發現單數和多數Json字串不同,多數物件顯示會有中括號[物件1、物件2]包括,但是單數沒有。二、Json字串缺少顯示sex屬性值,其將sex屬性列印出來後發現為null值,表示Gson會預先將屬性值為null的預先過濾掉後,才轉成JSON字串。這種方式並不會影響之後反序列處理。後續會有更詳細的Gson字串處理介紹

     /**
 * name : 員工轉json字串測試
 */
public class EmployeeTest {

    public static  void main(String[] arg){
               //單筆
        Employee employee01 = new Employee("bryan",30000);
        Employee employee02 = new Employee("joe",31000);
        employee02.setSex("N");
        Gson gson =  new Gson();
        String jsonStr1 = gson.toJson(employee01);
        String jsonStr2 = gson.toJson(employee01,Employee.class);
        System.out.println("jsonStr1:"+jsonStr1);
        System.out.println("jsonStr2:"+jsonStr2);

        //多筆
        List list = new ArrayList();
        list.add(employee01) ;
        list.add(employee02) ;
        String jsonStr3 = gson.toJson(list);
        System.out.println("jsonStr3:"+jsonStr3);

    }
}

結果:
jsonStr1:{“name”:”bryan”,”age”:0,”salary”:30000.0}
jsonStr2:{“name”:”bryan”,”age”:0,”salary”:30000.0}
jsonStr3:[{“name”:”bryan”,”age”:0,”salary”:30000.0},{“name”:”joe”,”age”:0,”sex”:”N”,”salary”:31000.0}]

物件關係表達

接下來我們將物件關係也加入,看Gson是否可以順利轉換成正確JSON字串,首先我們新增兩個物件:書明細(Book)和借書訂單(Order),之間的關係為員工借書會點選借書訂單系統,去挑選他的書明細,該系統會記錄該員工的姓名以及他點選書單的明細。

物件 屬性
書明細 Book bid 書單號
title書名稱
author 作者
借書訂單 Order orderId 訂單編號
employName 員工名稱
date 日期
bookList 書明細資料
/**
 * name : 書明細
 */
public class Book {
    private String bid;
    private String title;
    private String author;

    public Book(String bid, String title, String author)  {
        this.bid = bid ;
        this.title = title ;
        this.author = author ;
    }
    //setter and getter
    public String getBId() {
        return bid;
    }

    public void setBId(String id) {
        this.bid = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer() ;
        sb.append("") ;
        sb.append("bid:"+bid) ;
        sb.append("title:"+title) ;
        sb.append("author:"+author) ;
        sb.append("") ;
        return sb.toString() ;
    }

}

/**
 * name : 借書訂單
 */
public class Order {
    private String orderId;
    private String employName;
    private Date date;
    private List bookList;
    public Order(String orderId, String employName, Date date,
                 List bookList) {
        super();
        this.orderId = orderId;
        this.employName = employName;
        this.date = date;
        this.bookList = bookList;
    }

    //setter and getter
    public String getOrderId() {
        return orderId;
    }

    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }

    public String getEmployName() {
        return employName;
    }

    public void setEmployName(String employName) {
        this.employName = employName;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    public List getBookList() {
        return bookList;
    }

    public void setBookList(ArrayList bookList) {
        this.bookList = bookList;
    }

    public void show() {
        System.out.println("Order information:");
        System.out.println("orderId = " + orderId + "; employName = " + employName
                + "; date = " + date);
        System.out.println("Details: ");
        System.out.println(bookList.toString());
    }
}

employee01藉兩本書分別編號為B0001和B0002,這兩本書,我們把它加入oreder建構子中,再由Gson轉換成JSON字串出來,由結果顯示可以知道該訂單借了幾本書和是哪位員工借走。

/**
 * name : 借書訂單轉json字串測試
 */
public class OrderTest {

    public static  void main(String[] arg){
        //單筆
        Employee employee01 = new Employee("bryan",30000);
        Employee employee02 = new Employee("joe",31000);
        Book book01 = new Book("B0001","水滸傳","施耐庵 ") ;
        Book book02 = new Book("B0002","西遊記","吳承恩 ") ;
        Book book03 = new Book("B0003","三國演義","羅貫中 ") ;
        Book book04 = new Book("B0004","紅樓夢","曹雪芹 ") ;
        List bList = new ArrayList

JSON字串轉換物件

之前員工建立的Json字串,現在介紹如何用Gson來反序列Employee物件,有範例可以知道,Gson幾行就可以將JSON轉成物件化,用起來相當方便。

範例: JSON字串: {“name”:”bryan”,”age”:0,”salary”:30000.0}

/**
 * name: Json to Object 反序列範例
 */
public class EmployeeSTest {

    public static void main(String[] ags){
        Gson gson = new Gson();
        String employ_json = "{\"name\":\"bryan\",\"age\":0,\"salary\":30000.0}" ;
        Employee employee01 =  gson.fromJson(employ_json, Employee.class) ;
        System.out.println(employee01.toString());

    }
}

再來我們試看看List串列看看,是否可以也是跟上面宣告多數的物件相同,複數物件反序列比較麻煩一點,因為反序列需要告訴預先要轉換哪種類別,利用new TypeToken的方式來取得類別的類型,再使用fromJson方法轉回List<Employee>類型。
問題延伸: 但如果遇到泛型要如何解決?

Json字串: [{“name”:”bryan”,”age”:0,”salary”:30000.0},{“name”:”joe”,”age”:0,”sex”:”N”,”salary”:31000.0}]

/**
 * name: Json to Object 反序列範例
 */
public class EmployeeSTest {

    public static void main(String[] ags){
        Gson gson = new Gson();
        String employ_json = "{\"name\":\"bryan\",\"age\":0,\"salary\":30000.0}" ;
        Employee employee01 =  gson.fromJson(employ_json, Employee.class) ;
        System.out.println(employee01.toString());
        System.out.println("=========================List===========================");
        String employs_json = "[{\"name\":\"bryan\",\"age\":0,\"salary\":30000.0},{\"name\":\"joe\",\"age\":0,\"sex\":\"N\",\"salary\":31000.0}]";
        Type collectionType = new TypeToken

參考資料

  1. https://bng86.gitbooks.io/android-third-party-/content/gson.html
  2. http://j796160836.pixnet.net/blog/post/30530326-%E7%9E%AD%E8%A7%A3json%E6%A0%BC%E5%BC%8F
  3. http://blog.csdn.net/zzp_403184692/article/details/8266575

2017年12月20日

H2資料庫(2)-資料庫操作

H2 操作介紹

在上一章節我們大致上介紹了H2資料庫的特性和安裝方式,本章節會實際使用JAVA來連結資料庫,做新增、刪除、修改和查詢等語法操作,讓大家對H2可以有更進一步的了解。

實際操作

  • 新增員工資料表
    我們今天來新增一個資料表為員工(EMPLOYEE),員工有職工編號、姓名、姓名、性別和是否在職。首先貼上新增資料表的語法(如下),然後按下RUN按鈕,就會出現下面訊息,如果左側欄位有出現EMPLOYY表示新增資料表成功
    新增資料表EMPLOYEE
    CREATE TABLE EMPLOYEE (
        ID BIGINT IDENTITY PRIMARY KEY,
        NAME VARCHAR(100),
        SALARY DOUBLE,
        SEX  VARCHAR(1),
        ISQUIT BOOLEAN
    );
  • 新增一筆員工資料
    新增員工資料
insert into EMPLOYEE (name, salary, sex, isquit) values ('bryan',30000,'N',0) ;
  • 查詢該名員工資料
    查詢該名員工資料
select * from  EMPLOYEE 
  • 修改該名員工資料
    修改該名員工資料
update employee  set name='', sex='M' where id='1'  ; 
  • 刪除該名員工資料
    刪除該名員工資料
delete employee  where id='1'  

2017年12月17日

H2資料庫(1)-基本安裝與介紹

H2資料庫介紹

H2為嵌入式資料庫,使用java開發,跨平台且內含資料庫管理介面,好處開發階段方便開發人員使用。

比較

詳細比較表: http://www.h2database.com/html/features.html#comparison
由圖比較可以知道,H2比其他資料庫更為優勢,以下會介紹開發時常用的模式:
enter image description here

  • 嵌入式模式Embedded Mode
    嵌入式資料庫會與應用程式共用同一JVM底層,在這個模式下persistent或是in-memory資料庫都支援,也沒有連線數的限制。但壞處是只可以給該應用程式使用,其他人無法直接存取資料庫。

  • 伺服器模式 Server Mode
    外部應用程式可以藉由JDBC或是OBC的方式連結該資料庫,它也支援persistent或是in-memory資料庫,也沒有連線數的限制。

  • 混合模式 Mixed Mode
    同時有嵌入式與伺服器的模式去讓外部應用程式連線或是自己應用程式連線。

安裝H2 database

官方網站: http://www.h2database.com/html/main.html

  1. 下載安裝程式
    出現安裝連結,本次範例使用Windows installer 的安裝連結

  2. 下載安裝程式後,點擊兩下,進入安裝畫面,按”下一步”

  3. 一直按”下一步”後,會出現”完成”

  4. 安裝完成後,會出現一個說明網頁,你可以點選Quickstart 它會告訴你,如何進入DBRMS畫面以及開啟資料庫服務

  5. 開啟windows的視窗圖示,執行 H2 Console後,會執行H2資料庫

  6. 進入DBRMS的登入畫面,使用者預設為”SA”,密碼為空值”“,按下”connect”進入,SQL命令中心

  7. 登入後,可以在空白處執行SQL語法

8.大致上安裝H2資料庫滿快速,操作畫面也是很好上手,如果系統在開發階段個人覺的滿推薦給大家使用看看。

參考

  1. H2官方網站

2017年12月9日

JavaBean 和POJO

前言

今天介紹JavaBean和POJO的不同,這兩個名詞在JAVA文章常常被拿來使用以及討論。在JDK1.1時候釋出才有的一個標準架構,很多時候常常被搞混,所以我們特別開闢一章來加以討論。POJO規範在企業級應用已經廣大的被使用的規範。

解釋

  1. POJO : 全名為Plain-old-Java-object,只需要繼承Object就可以,沒有特定規定,只要建立的類別有setter/getter方法都可以稱為POJO
  2. JavaBean: JavaBean通常用來封裝多個物件成為單獨物件使用,規範比較嚴格,規則如下
規則 說明
1 需要實作序列(Serializable/Externalizable)
2 不能有參數的建構子( no-arg constructor)
3 需要有公用setter/getter
4 屬性必須要私人(private)
5 屬於特定POJO規則

比較

  • 所有的JavaBean都為POJO,但是所有的POJO不一定為JavaBean
  • 都可以當作重複元件
  • 都必須序列化
  • 特性都為可用性、易用性和持久化使用
  • -

應用

由圖我們可以知道POJO在應用程式中,主要用來存取資料庫資料達到持久化的目的,並提供給商業邏輯流程處理使用。這種POJO的架構提供程式人員開發時的可以很有規則將資料封裝並加以使用。

POJO的角色

範例1. JavaBean(以員工為實例)
JavaBean建立員工物件,可以發現Employee物件建構子沒有任何參數,屬性為私有化並setter/getter的命名方式。

//實作序列化
public class Employee implements java.io.Serializable{  

private int id;  
private String name;

//無參數建構子
public Employee(){} 

//以下實作setter/getter 
public void setId(int id){this.id=id;}  
public int getId(){return id;}  
public void setName(String name){this.name=name;}  
public String getName(){return name;}  

}

範例2. POJO(以員工為實例)
POJO建構子參數化,有此可以知道name, id, salary為建構子的參數使用。

public class Employee
{

   String name;
   public String id ;
   private double salary;
   public Employee(String name, String id, 
                            double salary)
   {
       this.name = name;
       this.id = id;
       this.salary = salary;
   }

   public String getName()
   {
       return name;
   }

   public String getId()
   {
       return id;
   }

   public Double getSalary()
   {
       return salary;
   }
}

參考

  1. http://www.geeksforgeeks.org/pojo-vs-java-beans/

JavaBean 和POJO

前言

今天介紹JavaBean和POJO的不同,這兩個名詞在JAVA文章常常被拿來使用以及討論。在JDK1.1時候釋出才有的一個標準架構,很多時候常常被搞混,所以我們特別開闢一章來加以討論。POJO規範在企業級應用已經廣大的被使用的規範。

解釋

  1. POJO : 全名為Plain-old-Java-object,只需要繼承Object就可以,沒有特定規定,只要建立的類別有setter/getter方法都可以稱為POJO
  2. JavaBean: JavaBean通常用來封裝多個物件成為單獨物件使用,規範比較嚴格,規則如下
規則 說明
1 需要實作序列(Serializable/Externalizable)
2 不能有參數的建構子( no-arg constructor)
3 需要有公用setter/getter
4 屬性必須要私人(private)
5 屬於特定POJO規則

比較

  • 所有的JavaBean都為POJO,但是所有的POJO不一定為JavaBean
  • 都可以當作重複元件
  • 都必須序列化
  • 特性都為可用性、易用性和持久化使用
  • -

應用

由圖我們可以知道POJO在應用程式中,主要用來存取資料庫資料達到持久化的目的,並提供給商業邏輯流程處理使用。這種POJO的架構提供程式人員開發時的可以很有規則將資料封裝並加以使用。

POJO的角色

範例1. JavaBean(以員工為實例)
JavaBean建立員工物件,可以發現Employee物件建構子沒有任何參數,屬性為私有化並setter/getter的命名方式。

//實作序列化
public class Employee implements java.io.Serializable{  

private int id;  
private String name;

//無參數建構子
public Employee(){} 

//以下實作setter/getter 
public void setId(int id){this.id=id;}  
public int getId(){return id;}  
public void setName(String name){this.name=name;}  
public String getName(){return name;}  

}

範例2. POJO(以員工為實例)
POJO建構子參數化,有此可以知道name, id, salary為建構子的參數使用。

public class Employee
{

   String name;
   public String id ;
   private double salary;
   public Employee(String name, String id, 
                            double salary)
   {
       this.name = name;
       this.id = id;
       this.salary = salary;
   }

   public String getName()
   {
       return name;
   }

   public String getId()
   {
       return id;
   }

   public Double getSalary()
   {
       return salary;
   }
}

參考

  1. http://www.geeksforgeeks.org/pojo-vs-java-beans/

2017年12月6日

Java Reflection(反射機制)之一

Java Reflection -1 (反射機制)

Java Reflection最著名例子就是Spring,它帶來XML和Reflection的結合,可以讓開發者可以動態配置資訊、生成物件或是操過物件,何種情況需要使用Java 反射? 讓我們想一想,有些情況是否需要外部的資訊來決定程式的邏輯或是資料,或是有些情況系統之間希望保持低偶合關係。這些你都可以使用反射機制來完成,例如系統可以提供plugin的功能,來擴充系統的檢查功能,這時候可以利用反射不失為一個辦法。

Java Bean 反射範例

POJO物件 以Employee為例一個反射的對象,我們會介紹利用JAVA的反射設機制,如何將類別解析與載入內容。在一個class類架構會有: Method方法、Field屬性、Constructor建構子,如何分別解析並載入內容,是本章節的重點。

/*
*  name: 員工類別的POJO
*/
public class Employee{

    public  Employee(String id){
        this.id = id ;
    }

    public  Employee(String id, String cname){
        this.id = id ;
        this.cname = cname ;
    }

    private String id ; 
    private String cname ; 
    private String sex ;
    public String getId() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getCname() {
        return cname;
    }
    public void setCname(String cname) {
        this.cname = cname;
    }
    public String getSex() {
        return sex;
    }
    public void setSex(String sex) {
        this.sex = sex;
    } 

    @Override
    public String toString() {
         return "id:"+id+",cname:"+cname+", sex:"+sex ; 
    }
}

類別反射與載入

類別反射是當我需要使用的類別時,我才做載入的動作,也稱為動態載入。如果是程式啟動時就載入,缺點比較耗資源。所以動態載入會讓系統資源更有效的運用。首先 java.lang.Class 物件代表 在運行時所載入的類別或介面實例,使用Class類別可以取得類別的資訊或是生成實例。

  • Class類別API
函式 說明
public static Class forName(String className) 類別的對象
public T newInstance() 返回一個生成實例
public class TestClass01 {
    public static void main(String[] args) {

        Class<?> c1 = Employee.class ; 
        System.out.println("c1-->"+c1);

        try {
            Class<?> c2 = Class.forName("com.tw.refleciton.Employee") ;
            System.out.println("c2-->"+c2);
            System.out.println("c2-->"+c2.getName());
            System.out.println("c2-->"+c2.getSimpleName());
            System.out.println("c2-->"+c2.getEnclosingClass());
        } catch (ClassNotFoundException e) {

            e.printStackTrace();
        }
    }
}
public class TestClass02 {
    public static void main(String[] args) {

        try {
            Class<?> c3 = Class.forName("com.tw.refleciton.Employee") ;
            Object o1 = c3.newInstance() ; 
            System.out.println("c3-->"+c3);
            System.out.println("o1-->"+o1);

        } catch (ClassNotFoundException e) {

            e.printStackTrace();
        } catch (InstantiationException e) {
            // MARK Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // MARK Auto-generated catch block
            e.printStackTrace();
        }
    }
}

建構子載入

  • Constructor类API
函式 說明
Constructor[] getConstructors() 取得該類別的建構子陣列
Constructor getConstructor(Class… parameterTypes) 符合參數類別,取得建構子對象
T newInstance(Object… initargs) 將內容值設定建構子並生成實例
/*
* name: 取得建構子資訊
*/
public class TestClass03 {
    public static void main(String[] args) {

        try {
            Class<?> c3 = Class.forName("com.tw.refleciton.Employee") ;
            Constructor<?>[] cs = c3.getConstructors();
            for(Constructor<?> c : cs ){
                System.out.print(c.getName()+", parameter size:"+c.getParameterTypes().length);
                Class<?>[] parameters = c.getParameterTypes();
                int i=1 ; 
                if(parameters!=null && parameters.length>0){
                    for(Class<?> p : parameters){
                        System.out.print(",parameter"+i+":"+p.getName()+" , ");
                        i++; 
                    }
                }
                System.out.println();

            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
結果
com.tw.refleciton.Employee, parameter size:2,parameter1:java.lang.String , ,parameter2:java.lang.String ,
com.tw.refleciton.Employee, parameter size:1,parameter1:java.lang.String ,
/*
 * name:有參數化建構子
 * 利用建構子參數化宣告,必須有參數初始化建構子
 */
public class TestClass04 {
    public static void main(String[] args) {
        try {
            Class<?> c = Class.forName("com.tw.refleciton.Employee") ;
            Constructor<?> cs = c.getConstructor(String.class, String.class);
            Object o = cs.newInstance("0001", "丁大幅") ; 
            System.out.println(o.toString());

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            // MARK Auto-generated catch block
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            // MARK Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            // MARK Auto-generated catch block
            e.printStackTrace();
        } catch (InstantiationException e) {
            // MARK Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // MARK Auto-generated catch block
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            // MARK Auto-generated catch block
            e.printStackTrace();
        } 
    }
}
結果
id:0001,cname:丁大幅, sex:nul

屬性載入

  • 屬性常用的API
函式 說明
Field[] getDeclaredFields() 取得所有屬性
Field getDeclaredField(String name) 取得指定的屬性
void set(Object obj, Object value) 設定屬性的新內容值
Object get(Object obj) 取得屬性的內容值
void setAccessible(boolean flag) 設定該屬性是否可以訪問true: 可以訪問、false : 不可以訪問
boolean isAccessible() 該屬性是否可以訪問true: 可以訪問、false : 不可以訪問


/**
 * name:解析field的欄位名稱
 */
public class TestClass05 {

    public static void main(String[] args) {

        try {
            Class<?> c5 = Class.forName("com.tw.refleciton.Employee") ;
            Field[] fs = c5.getDeclaredFields() ; 
            if(fs.length>0){
                for(Field f: fs){
                    System.out.println("field:"+f.getName());
                }

            }

        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
結果
name:sex,value –>> 男id:0002,cname:王大明, sex:男

參考

  1. https://github.com/JustinSDK/JavaSE6Tutorial/blob/master/docs/CH16.md
  2. Java反射机制连载(1)– JavaBean
  3. Java反射机制连载(2)– Class类
  4. Java反射机制连载(3)– Constructor类
  5. Java反射机制连载(4)– Field类
  6. Java反射机制连载(5)– Method类
  7. Java反射机制连载(6)– PropertyDescriptor类
  8. Java反射机制连载(7)– 使用反射实现的ORM持久层框架 attachment
  9. Java反射机制连载(7)– 使用反射实现的ORM持久层框架 attachment
  10. Java反射机制连载(9)– 自定义持久层框架之JdbcOperations接口

2017年11月19日

HashMap同步深入探討

前言

HashMap是常用的資料結構物件,主要利用雜湊來存放資料內容,以key-value方式來存取資料,但是HashMap本身不是一個線程安全的Collection类,所謂線性安全就當有多個執行緒在執行時,同時呼叫一個MAP,保證當有其一條執行序存取MAP物件時,其他執行序必須保證存取的值是最新的。在thread搭配hashMap使用時,會出一些問題,我們會針對這些問題做一個解析

執行序安全

在考慮執行序安全下,宣告synchronized情況下,執行時出現下列問題

(1) High CPU LOADING: 發生在hashMap 在put時候會先去檢查容量是否超過臨界點,會新增一個大的hash表,然後把老的表搬去新Map表中,這個搬動的成本很大。
High CPU  LOAD
(2) HashMap infinite loop problem: 死循環發生

HashMap與HashTable比較

(1) HashTable為同步且執行緒安全; HashMap執行緒不安全,非同步機制。
(2) HashMap 允許空建值;HashTable不允許
(3) HashTable是Dictionary的子類,HashMap是Map介面實作
(4) HashTable中hash空間大小是11,增加的方式是 old*2+1。HashMap中hash数组的空間大小是16,增加為2的指数。

ConcurrentHashMap(CHM)

ConcurrentHashMap取代HashMap同步機制,在CHM內部採用 HashEntry 和 Segment,HashEntry 用来封装映射表的键 / 值对;Segment 用来當鎖的角色。一個Segment會包含一組HashEntry健值組。

HashMap與ConcurrentHashMap比較

(1)HashMap 是non-thread safe的結構,不能在多執行緒下使用,如果需要thread safe模式須加上synchronized來使用。但是會造成一次只能有一個執行緒進入,造成其他執行緒都在排隊。
(2) ConcurrentHashMap:為thread safe的結構,讓MAP有多執行安全,可以多執行續來使用。

public class CHMTest implements  Runnable {
    Map

參考

  1. hashMap 死循環問題
  2. 聊聊并发(四)——深入分析ConcurrentHashMap

2017年9月20日

webpack第二堂-使用設定檔

前言

我以官方的文件來介紹webpack設定檔,希望這種方式可以讓剛接觸webpack深入淺出的明白webpack設定檔的意義和目的。

設定檔介紹

經過第一堂的介紹,我們不可能每次都使用指令的方式來打包JS檔,如果當功能複雜時或是特定的需求時,是很難用使用指令完成開發任務的,所以webpack提供設定檔(預設檔名:webpack.config.js)的方式可以讓開發者方便進行編譯

設定概念

  • Entry (進入文件)
    首先webpack在打包時會先建立你應用程式所有使用到其他模組相依關聯的圖形,但是需要先指定圖形的進入點(entry point),這個進入點的作用是告訴webpack從哪裡開始並且告訴他如何打包,你把進入點想成一個contextual root or the first file to kick off your app[官方翻譯].

範例1.
設定檔: webpack.config.js (webpack預設)

  module.exports = {
     entry: './app.js'
  };

語法說明: entry為設定編譯的進入文件

  • Output (目的檔)
    當你打包JS文件時,需要告訴webpack最後產出的檔案,所以這個參數output如何用甚麼方式產出目的檔,output.path和output.filename屬性是告訴如何打包後的路徑和名稱

範例2.
設定檔: webpack.config.js (webpack預設)

const path = require('path');
module.exports = {
    entry: './app.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'app.bundle.js'
      }
  };
  • Loaders(載入器)
    因為webpack只會載入.js檔,所以需要搭配loaders會將專案的檔案(.css, .html, .scss, .jpg 等)視為一個個的模組,所以loaders功能將這些檔案轉換(transform)為模組化後,讓這些檔案可以打包起來

如何在webpack.config.js配置載入器:

  1. 不同的檔案需要使用其對應的載入器,webpack才可以辨別這些檔案的格式
  2. 轉換這些檔案讓他們可以在加入相依模組的圖形

範例3:
設定檔: webpack.config.js (webpack預設)

const path = require('path');
module.exports = {
    entry: './app.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'app.bundle.js'
    },
    module: {
        rules: [
          { test: /\.txt$/, use: 'raw-loader' }
        ]
    }  
  };

文字檔: cars.txt

{"car1":"Alfa Romeo","car2":"Ferrari","car3":"Porsche"}

語法說明:
rules下面會定義兩個屬性,test和use,test解析的檔案為’*.txt’檔,use表示使用的檔入器raw-loader。

但是在執行編譯後卻出現下列的錯誤,Module not found: Error: Can’t resolve ‘raw-loader’表示沒有找到載入器,我們必須安裝載入器,才可以使用

 $ npm install --save-dev raw-loader

安裝raw-loader載入器後,就可以正常執行>node .\dist\app.bundle.js

[ 'dev1', '2222', '333' ]
{"car1":"Alfa Romeo","car2":"Ferrari","car3":"Porsche"}
  • Plugins(插件)
    插件在webpack定義為功能的擴充,表示提供客製化的工具來讓開發更為多元化,webpack plugin system是非常強大且很客製化的特性,

範例4:
設定檔: webpack.config.js (webpack預設)

const HtmlWebpackPlugin = require('html-webpack-plugin'); //installed via npm
const webpack = require('webpack'); //to access built-in plugins
const path = require('path');
module.exports = {
    entry: './app.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'app.bundle.js'
    },
    module: {
        rules: [
          { test: /\.txt$/, use: 'raw-loader' }
        ]
    },
    plugins: [
        new webpack.optimize.UglifyJsPlugin(),
        new HtmlWebpackPlugin({template: './src/index.html'})
    ]    
  };

語法說明: 使用插件需要三個步驟
1. 使用npm安裝插件,但如果webpack內建就不需要
2. 使用require()函式載入
3. 加入plugins陣列中

我們以HtmlWebpackPlugin來教學如何安裝,首先到webpack插件列表找到HtmlWebpackPlugin然後點入,可以找到安裝方法和使用說明這樣就完成的安裝

 $ npm install --save-dev html-webpack-plugin

結論

webpack進入點和輸出文件算是基本的操作,載入器與插件的使用是比較進階的用法,需要花點時間才可以理解,也是webpack與其他JS不同的地方。

參考文件:

  1. 官方網站文件
  2. plungs列表
  3. 如何使用 Webpack 模組整合工具
  4. Vue.js 權威指南

webpack第一堂-初體驗

學習webpack動機

想要學習webpack原因是,當我接觸vue開發SPA時,網路上大家推崇使用webpack開發可以優化JS腳本,webpack的強大功能讓我更專注開發。

webpack 介紹

官網: webpack官網
官方文件: webpack document
Webpack 是一個模組打包器( module bundler) ,簡單來說webpack是可以將你網頁呼叫到相關模組的函示,打包並編譯成一個最小化的檔案,並讓他在任何瀏覽器都可以正常執行。

Webpack 是德國開發者 Tobias Koppers 建置模組化工具,他同時支援AMD、CMD的規則,他有下列特性

  • 代碼分割
    同步:編譯時會直接把打包輸出文件
    異步: 單獨生成一個代碼,只有在運行時才會載入
  • 載入器
    在預設下,只可以打包JS文件,但是通過載入器也可以將其他類型的文件轉成JS文件
  • 插件
    當webpack功能不能滿足我們需求,可以安裝插件擴充功能

實作

  • 安裝webpack
    全局安裝
 $ npm install webpack -g

建立專案

 $ npm init

最後,在專案中安裝webpack並寫入package.json

 $ npm i webpack --save-dev
  • 第一次使用就上手
    我們件ㄌㄧ簡單的範例,使用
    example
    |-app.js
    |-cats.js

範例: app.js 主要編譯入口文件

cats = require("./cats.js")
console.log(cats)

範例: cats.js

var cats = ['dev1','2222','333']
module.exports = cats

我們在node命令中心執行打包的指令

指令 : webpack 入口文件.js 輸出文件.js

     webpack ./app.js   app.bundle.js

node執行app.bundle.js看看結果如何

    webpack app.bundle.js

結果: [ ‘dev1’, ‘2222’, ‘333’ ]

結論

使用webpack把包後的程式碼可以使用任何的環境執行,包含瀏覽器的環境中。

  1. plungs列表
  2. 如何使用 Webpack 模組整合工具
  3. Vue.js 權威指南

Vue(1)-崛起

Vue崛起

Vue在2014年被發佈,在沒還發布之前,不管是angular2和React都會使開發者,花費不少時間學習,然而Vue出現,有著好的設計MVVM模式與最小的儲存容量,讓開發者不需要花太多時間學習就可上手並且不會占用瀏覽器太大的空間。這些特性讓他在2016年擁有驚人的 89%的開發人員滿意度評估。

Vue範例檔案結構

ex1 /
|- dist
    |-app.bundle.js
|- app.js
|- app.html
|- webpack.config.js
|-package.json

  • 首先必須要安裝nodejs環境,並且新增一個專案

    $> npm init

  • 安裝Vue套件,加入package.json

$>npm install vue –save-dev

  • 新增 app.js
import Vue from 'vue'

new Vue({
    el:"#app",
    template:"<div>Hellow world!</div>"
})
  • 新增 app.js
<html>

<body>
    <div id="app"></div>
</body>

<script src="dist/app.bundle.js"></script>

</html>
  • webpack設定檔: webpack.config.js
const path = require('path');
module.exports = {
    entry: './app.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'app.bundle.js'
      },
      resolve: {
        alias: {
            'vue': 'vue/dist/vue.js'
      }
    }
  };

打包

$>webpack

功能顯示

執行app.html
enter image description here

2017年2月26日

PHP與Python搭配

今天介紹如何利用php網頁呼叫目錄下的python程式工作或是資料交換,本人整理的方法有兩種

  • 使用system()、exec()、shell_exec()呼叫程式

(1) string system ( string return_var ] ) 參考網址
官網解釋system()用來執行外部命令,返回為印出的結果,passthru()跟system()類似但是它不會返回結果。

範例1. 利用system執行ls指定並顯示在網頁上,無法使用變數保留ls的結果

檔案名稱: psystem.php

 
        $jsondata= system("ls -al", $result);

結果:


(2) exec() : string exec ( string output [, int &$return_var ]] ) 參考網址

範例2. 利用exec執行python程式並可以回傳json格式給前端網頁做處理並顯示。我們ptopy.php就是可以看到callpy()為執行py的函式,它執行完pyEx01.py會將結果給$jsondata變數,做後面json解析。

檔案名稱: ptopy.php

 
        function callpy() {
            $jsondata= exec("/usr/bin/python  pyEx01.py");
           return $jsondata ;
        }

    $jsondata= callpy();
    echo $jsondata ;
    echo "
" ; $obj = json_decode($jsondata) ; echo "name:".$obj-> { 'name'} .',' ; echo "id:".$obj-> { 'id'} ;

檔案名稱: pyEx01.py

 
    import sys
    print '{ "id": 1, \
        "name": "A green door", \
        "price": 12.50, \
        "tags": ["home", "green"]} '

結果:


(3) shellexec(): string shellexec ( string $cmd )參考網址

範例3. shellexec是用來執行linux/unix的shell指令或是腳本(.sh),我們利用shellexec執行shellEx.sh腳本,shellEx.sh裡面會執行python程 式碼並將結果回傳。

檔案名稱: pshell.php

 
$output = shell_exec('sh shellEx.sh ') ;
echo "
$output
";

檔案名稱:shellEx.sh

 
python pyEx01.py

檔案名稱:pyEx01.py

 
import sys
print '{ "id": 1, \
    "name": "A green door", \
    "price": 12.50, \
    "tags": ["home", "green"]} '

結果:

2017年2月24日

JAVA 8 Stream(1)

Lambda已經是JAVA 8的一個標準,在物件集合的處理方面,它新增一個Stream(java.util.stream.Stream)來操作,

  • 內部與外部迭代

外部迭代:一般我們要存取物件集合我們會實作Iterable來存取一個個物件,例如我們想要讀取員工的姓名

 
     for(Person person: persons){
         System.out.print(person.getName());
     }

問題:
(1) 它限制管理控制流程機會,不能利用並行、懶惰載入來得到更好的效能,這樣就不能充分使用多核心CPU
(2) for-each循環是順序的,並且必須按照集合指定的順序處理元素。 (3) 編譯無法進行指令優化。

內部迭代:可以使用並行、延遲載入等更好的方式來˙改善效能

 
      persons.forEach( person -> person.getName())
  • Stream

Java提供Stream物件,它跟collection的不同在於可以使用循序或是並行運算。Stream 提供 filter、sort、map 的操作功能。

  • Stream 和 Collection差別

(1) 當你Collections.sort(list)會改變原list排序,但是list.stream().sorted()會複製一份,原list保持不變。

(2) stream不會儲存元素,只透過計算管道得傳輸物件

(3) 延遲搜尋(lazying-seeking)透過 filter、map、sort等不會馬上執行,會決定最佳的執行順序並檢查滿足的要求元素就會返回。

(4)平行運算(parallel): 使用多核心的功能

Stream無法儲存,所以需要使用Collectors集合完成資料儲存,下面會介紹stream to list 、stream to map 和stream to set三種方式。

  • stream to List

    將persons處理完的物件結合儲存到List集合的格式

 
    List list2 = list.stream().filter(p -> p.getAge(

2017年2月22日

JAVA 8 Lambda語法結構

Java 在2005 年推出 Annotation 之後,就沒有重大的更新功能,但是在這幾年語言的競爭下,Java為了不落後其他語言,終於Java 8 導入Lambda,讓 Java 平台正式支援 functional programming 。Lambda表達式規範在JSR 335

  • Lambda帶來JAVA有這些好處 :
    1. 簡化程式碼,當需要一个函数,不需要特地去命名一个函数名 稱, 也就是匿名函式
    2. 並行程式開發

  • Lambda 重點學習 :

    1. Lambda 表示可以當作為code
    2. Lambda表示可以轉換成函式介面
    3. 需要學會解決方法與函式介面所造成的衝突
    4. 閉包表示
  • Lambda語法結構 :
    (input) -> {body} input 代表方法的引數,body程式碼主體

(1) ( )表示輸入沒有引數,程式敘述印出AAA,如果只有一行不需要用{}

 
      () -> System.out.print("AAAA")
  

(2) (int a)->{} 用來輸入參數

 
      final IntConsumer intConsumer = (int a) -> System.out.println("input :" + a);
      intConsumer.accept(10);
  

(3) (int a, int b )->{ return a +b } 回傳值使用

 
     IntBinaryOperator intBinaryOperator = (int a, int b) -> {
              return a + b;
          };
          int sum = intBinaryOperator.applyAsInt(1,2) ;
          System.out.print(sum);
  

(4)(int a, int b )->{ ....... } 一行以上需要用{}
 
         (int a, int b) -> {
                System.out.print(a);
                System.out.print(b);
          };

Lambda執行緒的應用 在Java 8以前版本使用執行緒需要寫很多行的程式碼;但是Java 8使用Lambda只需要一行就可以完成工作。

*一般傳統的寫法,需要只少要三行完成

 
              Runnable runnable1 = new Runnable() {
                  @Override
                  public void run() {
                      System.out.print("執行一般寫法執行緒");
                  }
              };
              runnable1.run();
  

*使用Lambda可以利用匿名函式來取代介面,寫少程式碼,在效率上,程式碼執行不會建立new新物件,直接執行主程式的部分。

 
           Runnable runnable2 = ()->System.out.print("執行Lambda 執行緒");
           runnable2.run();
  

2017年1月25日

設計模式-Composition(組合模式)

將物件當作一個元件,將很多元件組合成樹狀結構,有人也稱為部分整理模式(part-whole pattern)。

組合模式定義

單個物件與組合的物件都有一致性

組合模式使用情況

  1. 有物件需要表達複數的狀況

組合模式UML圖

組合模式範例

我們建立員工資料,利用組合模式建立多筆的員工資料

    /**
     * name: Employee.java
     * description :  員工個人資料
     * Created by bryant on 2017/1/25.
     */
    public class Employee {

        public  Employee(String name, String dept, BigDecimal salary){
            this.name = name ;
            this.dept = dept ;
            this.salary = salary;
        }

        private String no;

        //員工姓名
        private String name ;
        //薪水
        private BigDecimal salary ;
        //部門
        private String dept ;

        //取得員工姓名
        public String getName() {
            return name;
        }
        //設定員工姓名
        public void setName(String name) {
            this.name = name;
        }
        //取得員工薪水
        public BigDecimal getSalary() {
            return salary;
        }

        //設定員工薪水
        public void setSalary(BigDecimal salary) {
            this.salary = salary;
        }

        //取得部門
        public String getDept() {
            return dept;
        }
        //設定部門
        public void setDept(String dept) {
            this.dept = dept;
        }

        public String getNo() {
            return no;
        }

        public void setNo(String no) {
            this.no = no;
        }

        @Override
        public int hashCode() {
            return Integer.valueOf(this.no);
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder() ;
            sb.append("") ;
            sb.append(""+no+"") ;
            sb.append(""+name+"") ;
            sb.append(""+dept+"") ;
            sb.append(""+salary+"") ;
            sb.append(""+"\r\n") ;
            return sb.toString();
        }
    }
    /**
     * name: EmployeeComposite.java
     * description :  員工集合
     * Created by bryant on 2017/1/25.
     */
    public class EmployeeComposite {

        private List list = new ArrayList() ;

        public List getEmployees(){
            return list;
        }

        public void addEmployee(Employee employee){
            list.add(employee);
        }

        public void remove(Employee employee){
            list.remove(employee);
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder() ;
            for (Employee e: list){
                sb.append(e.toString());
            }
            return sb.toString() ;
        }
    }
    /**
     * name: EmployeeCompositeDemo.java
     * description :  員工集合DEMO
     * Created by bryant on 2017/1/25.
     */
    public class EmployeeCompositeDemo {

        public  static  void main(String[] args){
            Employee worker01 = new Employee("Jacker", "Worker",new BigDecimal("31000")) ;
            Employee worker02= new Employee("Mary", "Worker",new BigDecimal("31000")) ;
            Employee manager = new Employee("Bob","Manager", new BigDecimal("51000")) ;

            EmployeeComposite employeeComposite = new EmployeeComposite() ;
            employeeComposite.addEmployee(worker01);
            employeeComposite.addEmployee(worker02);
            employeeComposite.addEmployee(manager);
            System.out.println(employeeComposite.toString());

        }

    }

2017年1月24日

設計模式-Adapter(配接器模式)

在日常生活中出國常需要電壓轉接頭,接電壓轉換為另外一種該國家可用的電壓規格。今天要介紹的配置器就是有這樣的作用,可以將兩個不同的系統藉由配置器去符合其他系統的標準。

配接器模式定義

將兩個不同模組介面,利用配接器可以結合在一起。

配接器模式使用狀況

1.當兩個系統需要串接時,需要將資料轉換成另一個系統可以使用的規格 2.

配接器模式UML圖



Target: 目標介面,需要轉換資料的目標
Adapter : 配接器角色,負責轉換為目標介面
Adaptee: 需要轉換的角色

配接器模式種類

  1. Class Adapter(類別配接器): 利用繼承adaptee來完成配接器
  2. Obejct Adapter(物件配接器): 利用組合物件的方式完成配接器
配接器模式範例

我們建立Adaptee為120電壓,利用Adapter變壓器來轉換電壓。本範例類別與物件配接器都有實作。
GitHub: https://github.com/blackbryant/designpattern/tree/master/java/src/design/adapter

    /**
     * name: Volt.java
     * description: 電壓物件
     * Created by bryant on 2017/1/124
     */
    public class Volt {

        private float volt ;

        public  Volt(float volt){
            this.volt = volt ;
        }

        //取得電壓
        public  float getV(){
            return  this.volt ;
        }

    }
    /**
     * name: IAdapter.java
     * description:  變壓器介面
     * Created by bryant on 2017/1/24.
     */
    public interface IAdapter {

       public float convertVolt(float rate) ;
    }
    /**
     *  name: Adaptee.java
     * description:  傳統變壓器
     * Created by bryant on 2017/1/24.
     */
    public class Adaptee {

        public  Volt getVolt(){
            return new Volt(120) ;
        }
    }
    /**
     *  name: Adapter.java
     * description:  變壓器實作(類別配接器)
     * Created by bryant on 2017/1/24.
     */
    public class Adapter extends  Adaptee implements IAdapter {

        @Override
        public float convertVolt( float rate) {
            return getVolt().getV() * rate ;
        }
    }
    /**
     *  name: AdapterComposition.java
     * description:  組合式變壓器
     * Created by bryant on 2017/1/24.
     */
    public class AdapterComposition implements  IAdapter{
        private   Adaptee adaptee  = new Adaptee();

        @Override
        public float convertVolt(float rate) {
            return adaptee.getVolt().getV()*rate;
        }
    }
        /**
     *  name: AdapterDemo.java
     * description:  變壓器Demo
     * Created by bryant on 2017/1/24.
     */
    public class AdapterDemo {

        public  static  void main(String[] args) {
            //Class Adapter
            Adapter adapter = new Adapter();
            System.out.println(adapter.convertVolt(0.5f));

            //Object Adapter
            AdapterComposition adapterCompsition = new AdapterComposition() ;
            System.out.println(adapterCompsition.convertVolt(0.2f));

        }
    }


執行結果 :
60.0
24.0

2017年1月22日

設計模式-簡介

希望利用一些物件導向設計的方式來使設計化繁為簡,讓系統達到低耦合、高內聚等好處。最讓人推崇為GoF四人幫所提出的23 種模式,本以JAVA為範例。這些模式希望以實務面來切入,讓大家更好理解與應用。

Gof 模式

參考資料

  1. 良葛格-設計模式
  2. 搞笑談軟工
  3. [設計模式:Android原始碼解析與應用]
    (http://www.books.com.tw/products/0010709953)
  4. 深入淺出-設計模式

設計模式- Chain of Responsibility(責任鏈)

假設公司請假流程,需要三天需要經理簽核、五天需要副理,七天以上需要副總批准。我們需要使用責任鏈模式,物件會根據每個鏈結去執行,等待可以處理的鏈結才會停止。

責任鏈定義

對同一個事件作多個物件處理,這些物件會串成一個責任鏈,負責處理輸入的事件

責任鏈作用

  1. 當需要動態處理鏈結的處理需求
  2. 一連串流程處理

責任鏈範例

公司出差有規定當出差金額1千元以下經理簽核就可以,但是超過1萬元以上需要副總,超過1萬元為總經理和核准。

 
    /**
     * name: IProcessHandler.java
     * Description :  管理者簽核處理-抽象
     * Created by bryant on 2017/1/22.
     */
    public abstract class IProcessHandler {

        protected  IProcessHandler nextHandler;
        protected  BigDecimal money ;

        public  final  void handler(BigDecimal money){
            this.money = money;
            info();

            if(money.compareTo(getLimit())==1){
                this.nextHandler.handler(money);
            }
        }

        public abstract BigDecimal getLimit();
        public  abstract  void info();

    }
 
    /**
     *  name: ManagerHandler.java
     * Description :  經理簽核
     * Created by bryant on 2017/1/22.
     */
    public class ManagerHandler extends  IProcessHandler{
        @Override
        public BigDecimal getLimit() {
            return new BigDecimal("1000");
        }

        @Override
        public void info() {
            System.out.println("經理核准");
        }

    }
 
    /**
     *  name: ManagerHandler.java
     * Description :   副總
     * Created by bryant on 2017/1/22.
     */
    public class ViceGeneralManger extends  IProcessHandler {
        @Override
        public BigDecimal getLimit() {
            return new BigDecimal("10000");
        }

        @Override
        public void info() {
            System.out.println("副總核准");
        }
    }
 
    /**
     *  name: ManagerHandler.java
     * Description :   總經理
     * Created by bryant on 2017/1/22.
     */
    public class GeneralMangerHandler extends  IProcessHandler {
        @Override
        public BigDecimal getLimit() {
            return new BigDecimal("100000");
        }

        @Override
        public void info() {
            System.out.println("總經理核准");
        }
    }
 
    /**
     *  name: ManagerHandler.java
     * Description :   簽核demo
     * Created by bryant on 2017/1/22.
     */
    public class ManagerDemo {

        public  static  void main(String[] args){
            ManagerHandler managerHandler = new ManagerHandler();
            ViceGeneralManger viceGeneralManager = new ViceGeneralManger() ;
            GeneralMangerHandler generalManger = new GeneralMangerHandler();
            managerHandler.nextHandler = viceGeneralManager;
            viceGeneralManager.nextHandler= generalManger;
            managerHandler.handler(new BigDecimal(100000));

        }
    }

結果:
經理核准
副總核准
總經理核准

2017年1月20日

設計模式-Proxy(代理模式)

代理模式也稱委託模式,日常事務很多代理模式,例如幫別人跑腿買東西,代理售票等等。在設計模式為其他物件服務某件功能。

代理模式定義

為物件提供代理服務

代理模式作用

  1. 想要託管物件行為
  2. 可以在該物件之前或之後,進行額外的動作

代理模式範例

本範例當我有一個網站服務WebService實作IService介面,網站服務利用WebProxy託管,當他託管時,可以在執行服務前後加上其他動作

    /**
     * Name: IService.java
     * Description :  託管介面
     * Created by bryant on 2017/1/20.
     */
    public interface IService {
        //執行動作
        public  void doAction();
    }
    /**
     * Name: WebService.java
     * Description :  網站託管
     * Created by bryant on 2017/1/20.
     */
    public class WebService implements IService {

        @Override
        public void doAction() {
             System.out.println("進行託管");
        }
    }

    /**
     * Name: WebProxy.java
     * Description :  代理模式
     * Created by bryant on 2017/1/20.
     */
    public class WebProxy  {
        private IService service  ;
        public WebProxy(IService service){
            this.service = service ;
        }

        public void action(){
            //執行服務前條件
            System.out.println("託管前設定");

            this.service.doAction();
            //其他條件
            System.out.println("本次託管費用");
        }
    }
    /**
     * Name: IService.java
     * Description :  代理模式測試
     * Created by bryant on 2017/1/20.
     */
    public class WebProxyDemo {

        public  static void main(String[] args){
            IService webService = new WebService();
            WebProxy proxy = new WebProxy(webService);
            proxy.action();
        }
    }

結果: 託管前設定
進行託管
本次託管費用

2017年1月14日

設計模式-Template (樣本)

樣本方法就是將程式固定的步驟抽離出來,讓子類別根據不同狀況去實作適應之方法,例如很多部落格會提供樣板花邊,讓部落客使用不同的圖案。

github:https://github.com/blackbryant/designpattern/tree/master/java/src/design/template

樣本模式定義

將實務邏輯共同行為抽離出來,去定義基本步驟,讓子類別可以個別去實作它,讓模組可以內聚合。

樣本模式用法

1.讓子類別實作共有的方法
2.將核心程式抽離為樣本,周圍細節再由子類別實作

樣本模式範例

我們示範檔案收檔機制,會先定義好一個共用的樣板,再由其他子 類別依照不同情況去實作它。

  • 我們大致可以知道收檔的流程: 下載方法、解析電文、處理和回饋這些步驟。
    /**
     * Name: ContextTemplate.java
     * Description :  內文收檔樣本
     * Created by bryant on 2017/1/14.
     */
    public abstract class ContextTemplate {
        protected String filename ;

        //下載方法
        protected  boolean downloadDoc(String url, String to){
            if(url.equals(""))
                return  false ;
            else
                System.out.println("從"+url+"下載檔案到"+to) ;
            return true;
        }

        //解析文件
        protected  boolean parseDocc(String filename){
            if(filename.equals(""))
                return  false ;
            else
                this.filename = filename;
                System.out.println(filename+"解析檔案") ;
            return true;
        }

        //處理
        protected  void process(){
             System.out.println("處理盪案"+this.filename);
        }

        //回饋
        protected  void feedback(){
            System.out.println("檔案正確");
        }
    }


  • 我們要實作一個FTP收檔類別,首先繼承ContextTemplate接著實作收檔的細節,我們解析檔案利用csv格式解析,在回饋檔案。
  • -
    /**

     * Name: FtpProgress.java
     * Description : Ftp收檔子類別
     * Created by bryant on 2017/1/14.
     */
    public class FtpProgress extends ContextTemplate {

        @Override
        protected boolean downloadDoc(String url, String to) {
            System.out.println("從FTP下載檔案到:"+to);
            return true;
        }

        @Override
        protected boolean parseDocc(String filename) {
            this.filename = filename ;
            System.out.println("利用CVS格式解析檔案"+this.filename);
            return true;
        }

        @Override
        protected void process() {
            System.out.println("做加總處理");
        }

        @Override
        protected void feedback() {
            super.feedback();
        }
    }


  • 樣板模式Demo範例
    /**
     * Name: ContextTemplateDemo.java
     * Description :  內文收檔樣本Demo
     * Created by bryant on 2017/1/14.
     */
    public class ContextTemplateDemo {

        public  static void main(String[] args){
            FtpProgress ftpProgress = new FtpProgress();
            ftpProgress.downloadDoc("ftp:124567","C:file/");
            ftpProgress.parseDocc("aa.txt");
            ftpProgress.process();;
            ftpProgress.feedback();
        }

    }

設計模式-Observer (觀察者)

觀察者模式常常利用通知上面,當物件狀態改變需要通知其他全部的物件一起變更時,這種模式也被稱為發佈-訂閱模式

觀察者定義

定義物件一對多關係,當一個物件改變狀態時,所有相依它的物件都會自動被改變。 主題(Subject) : 定義註冊、非註冊、通知改變和更新等動作 觀察者(observer):設定主題和更新觀察者 案例: Java Message Service (JMS)

觀察者用法

  1. 跨系統訊息交換,如訊息序列、事件觸發等
  2. 關聯行為
  3. GUI前後台的串接,當更新某個元件的狀態,也回影響

觀察者例子

我們利用主從式例子來說明觀察者的例子,當我主伺服器會下達˙訊息給三個從級伺服器,這三台會同步的更新訊息

    /**
     * Name: Observer.java
     * Description :  觀察者
     * Created by bryant on 2017/1/14.
     */
    public interface IObserver {

        //更新觀察者、
        public void update();

    }
    /**
     * Name: ISubject.java
     * Description :  主題
     * Created by bryant on 2017/1/14.
     */
    public interface ISubject {
        //註冊
        public void register(IObserver observer) ;
        //取消註冊
        public void unregister(IObserver observer);
        //更新主題
        public  String update();
        //通知觀察者
        public void notifyObservers();
    }
        /**
     *MasterSubject.java
     * Description :  主伺服器
     * Created by bryant on 2017/1/14.
     */
    public class MasterSubject implements  ISubject {
        private List slave ;
        private boolean change;
        private  String message ;
        public  MasterSubject(){
            this.slave = new ArrayList();
        }

        @Override
        public void register(IObserver observer) {
            if(observer==null){
                throw  new NullPointerException("Null Observer");
            }else{
                slave.add(observer);

            }
        }

        @Override
        public void unregister(IObserver observer) {
            if(observer==null){
                throw  new NullPointerException("Null Observer");
            }else{
                slave.remove(observer);
            }
        }

        @Override
        public String update() {
          return this.message ;
        }

        @Override
        public void notifyObservers() {
            if(this.change){
                for(IObserver observer : this.slave){
                    observer.update();
                }
            }
        }

        //送出訊息
        public void postMessage(String mesage){
            this.message = mesage ;
            this.change = true ;
            notifyObservers();
        }

    }
    /**
     * Name: Observer.java
     * Description :  觀察者
     * Created by bryant on 2017/1/14.
     */
    public class SlaveObserver implements IObserver{

        private String name ;
        private ISubject master ;

        //設定名稱與訂閱端
        public  SlaveObserver(String name, MasterSubject master){
            this.name = name ;
            this.master = master ;
        }

        @Override
        public void update() {
            String message = (String) this.master.update();
            if(message==null){
                System.out.println("no message");
            }else{
                System.out.println(name+":"+message);
            }
        }
    }
        /**
         * Name: MasterSlaveDemo.java
         * Description :  主從式架構,當主伺服器發送訊息給其他從級伺服器
         * Created by bryant on 2017/1/14.
         */
        public class MasterSlaveDemo {

            public  static  void main(String[] args){
                MasterSubject master = new MasterSubject();
                SlaveObserver slave01 = new SlaveObserver("slave-1",master);
                SlaveObserver slave02 = new SlaveObserver("slave-2",master);
                SlaveObserver slave03 = new SlaveObserver("slave-3",master);

                master.register(slave01);
                master.register(slave02);
                master.register(slave03);

                master.postMessage("同步更新資料");

                master.unregister(slave02);
                master.postMessage("slave-2不更新");

            }
        }

輸出
slave-1:同步更新資料
slave-2:同步更新資料
slave-3:同步更新資料
slave-1:slave-2不更新
slave-3:slave-2不更新

觀察者與MVC的關係

MVC架構使用觀察者當作controller和view的溝通機制,在JAVA中提供兩個類別來支援MVC 1. Observer: 2. 待續......

2017年1月13日

設計模式-Stragety (策略)

當你有很多計算方法,通常會將所以計算法則寫在同一個類別,但是當有新的演算法要加入時,你必須修改原來的程式,這種方式容易造成維護成本較高,如果把這些演算法抽離出來,提供統一個介面去實作,不僅擴充性高、可維護性也不錯。我們稱為策略模式

github: https://github.com/blackbryant/designpattern/tree/master/java/src/design/stragety

策略模式定義

策略模式將多個演算法則定義一個介面,並把每個演算法封裝,方便擴展和使用

策略模式用法

  1. 多個處理方法或演算法(例如排序: 選擇排序、泡沫排序、插入排序) 2.業務邏輯中有類似行為但是使用的方法不同

策略模式例子

我們利用進銷存拋帳為例子,當銷售作業、入庫作業、退貨作業都需要拋帳時,我們可以做一個共同介面為TradeStragety讓各作業來實作拋帳規則。因為對各作業拋帳是共同一個行為,但是實作方法不同。

  • 首先定義一個拋帳介面,當入庫、銷售和退貨當完成程序時,會將做最後帳務拋轉。
    /**
     *  Name: TradeStragety.java
     * Description :  拋帳策略模式-介面
     * Created by bryant on 2017/1/14.
     */
    public interface TradeStragety {
        //拋帳
        public boolean doTrade();
    }


  • 銷售拋障實作TradeStragety介面來完成拋帳
    /**
     *  Name: SaleTradeStragety.java
     * Description :  銷售拋帳
     * Created by bryant on 2017/1/14.
     */
    public class SaleTradeStragety implements  TradeStragety{
        @Override
        public boolean doTrade() {
            System.out.println("銷售拋帳成功");
            return true;
        }
    }


  • 入庫拋帳實作TradeStragety介面來完成拋帳
    /**
     *  Name: EnterTradeStragety.java
     * Description :  入庫拋帳
     * Created by bryant on 2017/1/14.
     */
    public class EnterTradeStragety implements  TradeStragety{
        @Override
        public boolean doTrade() {
            System.out.println("入庫拋帳成功");
            return true;
        }
    }


  • 退貨拋帳實作TradeStragety介面來完成拋帳
    /**
     *  Name: ReturnTradeStragety.java
     * Description :  退貨拋帳
     * Created by bryant on 2017/1/14.
     */
    public class ReturnTradeStragety implements  TradeStragety{
        @Override
        public boolean doTrade() {
            System.out.println("退貨拋帳成功");
            return true;
        }
    }


  • 策略模式Demo
    /**
     *  Name: TradeStragetyDemo.java
     * Description : 策略模式-Demo
     * Created by bryant on 2017/1/14.
     */
    public class TradeStragetyDemo {
        private TradeStragety tradeStragety ;
        public TradeStragetyDemo(TradeStragety tradeStragety){
            this.tradeStragety = tradeStragety  ;
        }

        public  void doTrade(){
             this.tradeStragety.doTrade();
        }

        public static void main(String[] args){
            TradeStragetyDemo saleDemo = new  TradeStragetyDemo(new SaleTradeStragety());
            saleDemo.doTrade();
            TradeStragetyDemo returnDemo = new  TradeStragetyDemo(new ReturnTradeStragety());
            returnDemo.doTrade();
        }
    }

設計模式-State (狀態)

狀態模式跟策略模式結構一樣,最大的不同狀態模式會根據狀態的不同,切換不同的動作,所以我們會將狀態物件抽離出來

狀態模式定義

物件行為的改變會根據它內部物件的狀態

狀態模式用法

  1. 物件操作跟他的狀態有相依關係,我們就可以它抽離出來,並在物件運行時,會根據系統狀態還變化它的操作
  2. 如果程式碼有很多if-else跟物件狀態有關,可以利用狀態模式來簡化程式設計

狀態模式例子

下面我們以生產機器為例子,機器的狀態有開始、停止、暫停與工作中四種狀態,會根據工人日常作業,1.上班時,會開啟機器為開機;2.中午休息會將機器切換暫停;3.當下班時,會將基季

        /**
     * name: State
     * description:: 定義系統的狀態為開始]、停止、暫停與工作中
     * Created by bryant on 2017/1/12
     */
    public class State {
        public enum SystemState {STOP, SUSPEND, WORKING, START }
        public  SystemState systemState =SystemState.STOP ;

        public  void setState(SystemState systemState){
            this.systemState =  systemState;
        }
    }
    /**
     * name: StartState
     * description:  打開機器狀態
     * Created by bryant on 2017/1/12
     */
    public class StartState implements  Machine {
        @Override
        public void operate(State state) {
            System.out.println("機器開始運行") ;
            state.setState(State.SystemState.START);
        }
    }
    /**
        * name: StopState
        * description:  關閉機器狀態
        * Created by bryant on 2017/1/12
        */
    public class StopState implements  Machine {
        @Override
        public void operate(State state) {
            System.out.println("關閉機器") ;
            state.setState(State.SystemState.STOP);
        }
    }
    /**
     * name: SuspendState
     * description:: 機器暫停
     * Created by bryant on 2017/1/12
     */
    public class SuspendState implements  Machine {
        @Override
        public void operate(State state) {
            System.out.println("機器暫停") ;
            state.setState(State.SystemState.SUSPEND);
        }
    }
    /**
     * name: SuspendState
     * description:: 機器暫停
     * Created by bryant on 2017/1/12
     */
    public class WorkState implements  Machine {
        @Override
        public void operate(State state) {
            System.out.println("機器工作中") ;
            state.setState(State.SystemState.WORKING);
        }
    }

    /**
     * name: Machine
     * description:  定義狀態介面用來連接動作和狀態
     * Created by bryant on 2017/1/12
     */
    public interface Machine {
        public  void operate(State state );
    }
    /**
     * name: MachineDemo
     * description:  測試狀態模式
     * Created by bryant on 2017/1/12
     */
    public class MachineDemo {
        public  static  void main(String[] args){
            System.out.println("工人七點上班,打開機器!");
            State state = new State();
            Machine startMachine = new StartState();
            startMachine.operate(state);
            System.out.println("中午休息!");
            Machine suspendMachine = new SuspendState();
            suspendMachine.operate(state);
            System.out.println("下班了!");
            Machine stopMachine = new StopState();
            stopMachine.operate(state);

        }
    }

輸出:
工人七點上班,打開機器!
機器開始運行
中午休息!
機器暫停
中午休息結束!
機器工作中
下班了!
關閉機器

2017年1月11日

設計模式-AbstractFactory (抽象工廠模式)

之前介紹工廠模式對一個相同屬性的物件做處理,但是當我有很多物件需要組合?這時候可以使用抽象工廠模式

抽象工廠模式定義

為一組相互關係的物件提供一個介面,來組裝起來

抽象工廠模式用法

以 Abstract Factory 設計模式的 UML 結構圖來看,根據《物件導向設計模式》(Design Patterns) 書中所述,其中定義了參與抽象工廠模式的幾個角色:

  • 抽象工廠角色(AbstractFactory) : 介面宣告出可生成各抽象成品物件的操作
  • 具體工廠角色(ConcreteFactory) : 具體實作出可建構具象成品物件的操作
  • 抽象產品角色(AbstractProduct) : 宣告某成品物件類型之介面
  • 具體產品角色(ConcreteProduct):是 ConcreteFactory 所建構的成品物件
  • 用戶端: 使會使用到AbstractFactory和AbstractProduct

抽象工廠模式實例

 
   /**
     * name: IProductFactory
     * role : AbstractFactory
     * description: 產品抽象類別
     * Created by bryant on 2017/1/11.
     */
    public abstract class IProductFactory {
        //產生播放器
        public abstract IPlayer  createPlayer();
        //產生耳機
        public  abstract IEarphone createEarphone();
    }
    /**
     * name: IPlayer
     * role : AbstractProduct
     * description: 播放器抽象類別
     * Created by bryant on 2017/1/11.
     */
    abstract class IPlayer {
        //播放抽象
        abstract  public void play() ;
    }

    /**
     * name: MP3Player
     * role : ConcreteProduct
     * description:MP3 播放器
     * Created by bryant on 2017/1/11.
     */
    public class MP3Player extends  IPlayer {
        @Override
        public void play() {
            System.out.print("MP3 Player");
        }
    }

        /**
         * name: CDPlayer
         * role : ConcreteProduct
         * description:CD 播放器
         * Created by bryant on 2017/1/11.
         */
        public class CDPlayer extends  IPlayer {
            @Override
            public void play() {
                System.out.print("CD Player");
            }
        }
    /**
     * name: IEarphone
     * role : AbstractProduct
     * description: 耳機抽象類別
     * Created by bryant on 2017/1/11.
     */
    public abstract class IEarphone {
        public abstract void capability();
    }
    /**
     * name: BeatEarphone
     * role : ConcreteProduct
     * description: BeatEarphone
     * Created by bryant on 2017/1/11.
     */
    public class BeatEarphone extends  IEarphone{
        @Override
        public void capability() {
            System.out.println("beat耳機");
        }
    }
    /**
     * name: StereoEarphone
     * role : ConcreteProduct
     * description: StereoEarphone
     * Created by bryant on 2017/1/11.
     */
    public class StereoEarphone extends  IEarphone{
        @Override
        public void capability() {
            System.out.println("立體聲耳機");
        }
    }
    /**
     * name: Walkman
     * role : ConcreteFactory
     * description: 具體工廠角色->產品功能CD、Stereo
     * Created by bryant on 2017/1/11.
     */
    public class Walkman extends IProductFactory {
        @Override
        public IPlayer createPlayer() {
            return new CDPlayer();
        }

        @Override
        public IEarphone createEarphone() {
            return new StereoEarphone();
        }
    }
        /**
     * name: Phone
     * role : ConcreteFactory
     * description: 具體工廠角色->手機功能MP3、beat耳機
     * Created by bryant on 2017/1/11.
     */
    public class Phone extends  IProductFactory {

        @Override
        public IPlayer createPlayer() {
            return new MP3Player();
        }

        @Override
        public IEarphone createEarphone() {
            return new BeatEarphone();
        }
    }

2017年1月10日

設計模式-Factory (工廠模式)


工廠模式是設計模式最為常用的模式之一,此模式可以想像你有一家工廠需要生產那些產品(既物件),

工廠模式定義

利用介面或是抽象物件來定義產品的明細或操作,再利用工廠對產品產生實例

工廠模式用法

  1. 當有很多產品是有共同的特性可以利用介面抽離出來,在使用工廠的new實例

工廠模式案例

我們定義IProduct為車的抽象類別,再利用CarFactory來指定需要產生哪種車款

    /**
     * Name: IProduct.java
     * Description :  產品抽象物件
     * Created by blackbryant on 2017/1/9.
     */
    public abstract class IProduct {

       protected abstract String getName() ;
       protected abstract List getColor() ;
       protected abstract List getOperate();
    }

   /**
     * Name: MPV.java
     * Description :  SUV
     * Created by bryant on 2017/1/9.
     */
    public class MPV extends  IProduct {

        @Override
        protected String getName() {
            return "MPV";
        }

        @Override
        protected List getColor() {
            List colors = new ArrayList();
            colors.add("紅");
            colors.add("藍");
            colors.add("鐵灰");
            return colors;
        }

        @Override
        protected  List  getOperate() {
            List operates = new ArrayList();
            operates.add("煞車");
            operates.add("加速");
            operates.add("導航");
            return operates;
        }
    }
    
    /**
     * Name: SUV.java
     * Description :  SUV
     * Created by bryant on 2017/1/9.
     */
    public class SUV extends  IProduct {
        @Override
        protected String getName() {
            return "SUV";
        }

        @Override
        protected List getColor() {
            List colors = new ArrayList();
            colors.add("紅");
            colors.add("藍");
            colors.add("鐵灰");
            return colors;
        }

        @Override
        protected  List  getOperate() {
            List operates = new ArrayList();
            operates.add("煞車");
            operates.add("加速");
            operates.add("導航");
            operates.add("省油");
            return operates;
        }
    }
    
        /**
     * Name: CarFactory.java
     * Description :  汽車工廠,產生MPV和SUV汽車
     * Created by bryant on 2017/1/9.
     */
    public class CarFactory {

        public   T createCar(Class cls){
            IProduct product = null ;
            try{
                product = (IProduct) Class.forName(cls.getName()).newInstance() ;

            }catch (Exception ex){
                ex.printStackTrace();
            }
            return  (T) product ;
        }


        //主程式測試
        public static void  main(String[] args){
            CarFactory carFactory = new CarFactory();
            IProduct product = carFactory.createCar(MPV.class) ;
            System.out.println("car style:"+product.getName());
            for(String color:product.getColor()){
                System.out.println("color:"+color);
            }

        }
    }

輸出: car style:MPV
color:紅,藍,鐵灰,operate:煞車,加速,導航,
car style:SUV
color:紅,藍,鐵灰,operate:煞車,加速,導航,省油,

2017年1月8日

設計模式-Prototype (原型)

Prototype定義

利用雛型模式可以拷貝這些物件並建立新物件,主要可以分為淺拷貝與深拷貝 1. 淺拷貝: 有稱影子拷貝,拷貝原物件並將原有的物件所以欄位重新建立一次,只針對需要的欄位完整複製,其他部分則是參照的方式完成。

Prototype使用情況

  1. 當有需要複製原物件,還當作副本提供其他系統修改,來保護原物件
  2. 當產生new 物件需要浪費很多時間或是很多資訊時,可以考慮用原型模式完成 3.

Prototype案例 -淺拷貝

我們利用員工薪水的例子來說明原型,在一家公司假設工程師基本薪水有30K,假設每天都需要加班一小時,一個月上班22天要如何算出月薪。不同語言支援的複製功能有不同的規範,使用java需要讓員工物件有複製的功能需要實作 java.lang.Cloneable這個介面。

IEmploy.java : 為抽象員工物件、EngineerShallow.java實作員工物件、Profile.java:為員工基本資料,我們為了可以快速算出員工薪水利用淺拷貝來快速建立其他員工資料。但是淺拷貝有一個缺點就是當執行baseProfile.setName("Mary")時,emp1也會跟著改變,因為淺拷貝當拷貝物件時,有參照外部的物件,也跟著參照並不會完整複製。

 package design.Prototype;  
 /**
     * name: IEmploy.java
     * descirpt: 員工薪水抽象物件,主要用來建立基本資料、薪水、加班費等等
     * Created by bryant on 2017/1/8.
     */
    abstract  class  IEmploy {

        //基本薪水
        abstract  protected void setBaseSalary(BigDecimal baseSalary);
        //津貼
        abstract  protected void setBonus(BigDecimal bouns);
        //加班費
        abstract  protected void setOvertimeSalary(int hour);
        //基本資料
        abstract protected void setProfile(Profile profile);

        //基本薪水
        abstract protected  BigDecimal getBaseSalary() ;
        //津貼
        abstract protected  BigDecimal getBonus() ;
        //加班費
        abstract protected  BigDecimal getOvertimeSalary() ;
        //全部薪水
        abstract protected  BigDecimal getFullSalary() ;
        //基本資料
        abstract protected Profile  getProfile();
    }
    package design.Prototype;

    /**
     * name: Profile.java
     * descirpt:ion: 個人基本資料: 姓名
     * Created by bryant on 2017/1/8.
     */
    public class Profile implements  Cloneable {

        private String name ;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
    }
    package design.Prototype;

    import java.math.BigDecimal;
    import java.math.RoundingMode;

    /**
     * name: EngineerShallow.java
     * descirpt:  淺複製員工資料
     * Created by bryant on 2017/1/8.
     */
    public class EngineerShallow extends  IEmploy implements  Cloneable{
        private  Profile profile ;
        private  BigDecimal baseSalary;
        private  BigDecimal bouns;
        private  BigDecimal overtimeSalary;

        @Override
        protected void setProfile(Profile profile){
            this.profile = profile;
        }

        @Override
        public Profile getProfile() {
            return profile;
        }

        @Override
        protected void setBaseSalary(BigDecimal baseSalary) {
            this.baseSalary = baseSalary ;
        }

        @Override
        protected void setBonus(BigDecimal bouns) {
            this.bouns = bouns ;
        }

        @Override
        protected void setOvertimeSalary(int hour) {
            this.overtimeSalary = (this.baseSalary.divide(new BigDecimal((240)), RoundingMode.HALF_EVEN)).
                    multiply(new BigDecimal("1.33")).
                    multiply(new BigDecimal(hour));

        }

        @Override
        protected BigDecimal getBaseSalary() {
            return this.baseSalary;
        }

        @Override
        protected BigDecimal getBonus() {
            return this.bouns;
        }

        @Override
        protected BigDecimal getOvertimeSalary() {
            return this.overtimeSalary;
        }

        @Override
        protected BigDecimal getFullSalary() {
            return this.baseSalary.add(this.overtimeSalary).add(this.bouns);
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }

        @Override
        public String toString() {
            String s =  "name:"+this.profile.getName()+"," +
                        "base Salary: "+this.getBaseSalary()+"," +
                        "bonus Salary: "+this.getBonus()+"," +
                        "overtime Salary: "+this.getOvertimeSalary()+","+
                        "full Salary: "+this.getFullSalary()+"," ;

            return s;
        }

        //測試Prototype
        public  static void main(String argsp[]){
            EngineerShallow baseEngineer = new EngineerShallow() ;
            Profile baseProfile = new Profile();
            baseProfile.setName("baseProfile");
            baseEngineer.setProfile(baseProfile);
            baseEngineer.setBaseSalary(new BigDecimal(30000));
            baseEngineer.setBonus(new BigDecimal(1000));
            baseEngineer.setOvertimeSalary(40);
            System.out.println(baseEngineer.toString());
            try {
                EngineerShallow emp1 = (EngineerShallow) baseEngineer.clone() ;
                emp1.setBaseSalary(new BigDecimal(35000));
                System.out.println(emp1.toString());
                baseProfile.setName("Mary");
                System.out.println(emp1.toString());

            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }

        }

    }

Prototype案例 -深拷貝

為了解決淺參照的問題,我們利用深拷貝可以達到外部參照也會複製完整的一份物件。由下面程式看到clone函式Profile物件會自我複製,就不會因為EngineerDeep有參照外部的物件而造成問題。

    package design.Prototype;

    import java.math.BigDecimal;
    import java.math.RoundingMode;

    /**
     * name: EngineerDeep.java
     * descirpt: 深拷貝連外部參照也會拷貝一份
     * Created by bryant on 2017/1/8.
     */
    public class EngineerDeep extends  IEmploy implements  Cloneable{
        private  Profile profile ;
        private  BigDecimal baseSalary;
        private  BigDecimal bouns;
        private  BigDecimal overtimeSalary;

        @Override
        protected void setProfile(Profile profile){
            this.profile = profile;
        }

        @Override
        public Profile getProfile() {
            return profile;
        }

        @Override
        protected void setBaseSalary(BigDecimal baseSalary) {
            this.baseSalary = baseSalary ;
        }

        @Override
        protected void setBonus(BigDecimal bouns) {
            this.bouns = bouns ;
        }

        @Override
        protected void setOvertimeSalary(int hour) {
            this.overtimeSalary = (this.baseSalary.divide(new BigDecimal((240)), RoundingMode.HALF_EVEN)).
                    multiply(new BigDecimal("1.33")).
                    multiply(new BigDecimal(hour));

        }

        @Override
        protected BigDecimal getBaseSalary() {
            return this.baseSalary;
        }

        @Override
        protected BigDecimal getBonus() {
            return this.bouns;
        }

        @Override
        protected BigDecimal getOvertimeSalary() {
            return this.overtimeSalary;
        }

        @Override
        protected BigDecimal getFullSalary() {
            return this.baseSalary.add(this.overtimeSalary).add(this.bouns);
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            EngineerDeep engineerDeep = (EngineerDeep) super.clone() ;
            Profile profile = (Profile) engineerDeep.getProfile().clone();
            engineerDeep.setProfile(profile);
            return engineerDeep;
        }

        @Override
        public String toString() {
            String s =  "name:"+this.profile.getName()+"," +
                        "profile :"+this.profile.toString()+","+
                        "base Salary: "+this.getBaseSalary()+"," +
                       "bonus Salary: "+this.getBonus()+"," +
                       "overtime Salary: "+this.getOvertimeSalary()+","+
                       "full Salary: "+this.getFullSalary()+"," ;

            return s;
        }

        //測試Prototype
        public  static void main(String argsp[]){
            EngineerDeep baseEngineer = new EngineerDeep() ;
            Profile baseProfile = new Profile();
            baseProfile.setName("baseProfile");
            baseEngineer.setProfile(baseProfile);
            baseEngineer.setBaseSalary(new BigDecimal(30000));
            baseEngineer.setBonus(new BigDecimal(1000));
            baseEngineer.setOvertimeSalary(40);
            System.out.println(baseEngineer.toString());
            try {
                EngineerDeep emp1 = (EngineerDeep) baseEngineer.clone() ;
                emp1.setBaseSalary(new BigDecimal(35000));
                System.out.println(emp1.toString());
                baseProfile.setName("Mary");
                System.out.println(emp1.toString());

            } catch (CloneNotSupportedException e) {
                e.printStackTrace();
            }

        }
    }

Prototype and Factory 差異

待續...

2017年1月7日

設計模式-Builder(建立者)

Builder 定義

  1. 使用物件有多參數輸入時,可以簡化多參數建立並初始化實例功能
  2. 強調物件屬性與物件建構可以分離,不同建構可以表示不同物件

Builder 使用情況

  1. 初始化參數因參數多,簡化參數建立過程
  2. 2.

Builder 案例

這是利用Buildr模式來簡化日期計算可以看到DateBuilder建立只有基本參數宣告,實際計算方法是建立在Builder物件中,最後載執行由Calendar做日期加減的計算。建立者分離的物件參數與計算方法,但是帶來良好封裝性

    /**
     * Name: builder
     * Description :  日期建立可以自由輸入年 月 日後,建立日期
     * Created by blackbryant on 2017/1/7.
     */
    public class DateBuilder {
        private Locale locale ; 
        private String format ; 
        private int day ; 
        private int month ; 
        private int year ; 
        private int hour ;
        private int minute ; 
        private int second ;
        private String date ; 

        //需要宣告為靜態類別
        public static class Builder{
            private Locale locale ; 
            private String format ; 
            private int day ; 
            private int month ; 
            private int year ; 
            private int hour ;
            private int minute ; 
            private int second ;
            private String date ; 

            public Builder(String calDate, String formate){
                this.date = calDate ;
                this.format = formate ;
            }

            public DateBuilder build(){
                return new DateBuilder(this) ;
            }

            public Builder addDay(int day){
                this.day = day  ; 
                return this;
            }

            public Builder addMonth(int month){
                this.month = month  ; 
                return this;
            }

            public Builder addYear(int year){
                this.year = year  ; 
                return this;
            }

            public Builder addHour(int hour){
                this.hour = hour;
                return this;
            }

            public Builder addMinute(int minute){
                this.minute = minute ; 
                return this ;
            }

            public Builder addSecond(int second){
                this.second = second ;
                return this;
            }

            public Builder setformat(String format){
                this.format = format ; 
                return this ;
            }
        }

        private DateBuilder(Builder builder){
            this.locale = builder.locale ;
            this.year = builder.year ; 
            this.month = builder.month ; 
            this.day = builder.day ; 
            this.hour = builder.hour ; 
            this.minute = builder.minute ; 
            this.second = builder.second ; 
            this.format = builder.format ;
            this.date = builder.date ;
        }


        public Locale getLocale() {
            return locale;
        }

        public String getFormat() {
            return format;
        }

        public int getDay() {
            return day;
        }

        public int getMonth() {
            return month;
        }

        public int getYear() {
            return year;
        }

        public int getHour() {
            return hour;
        }

        public int getMinute() {
            return minute;
        }

        public int getSecond() {
            return second;
        }

        public String getDate(){
            return date ;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder() ; 
            sb.append("locale="+this.locale) ; 
            sb.append("year="+this.year) ; 
            sb.append("month="+this.month) ; 
            sb.append("day="+this.day) ; 
            sb.append("hour="+this.hour) ; 
            sb.append("minute="+this.minute) ; 
            sb.append("second="+this.second) ; 
            sb.append("format="+this.format) ; 
            return sb.toString();
        }

        public  static  void main(String[] args){
            DateBuilder dateBuilder = new DateBuilder.Builder("2001-01-01","yyyy-MM-dd").addDay(2).build() ;

            SimpleDateFormat sdf = new SimpleDateFormat(dateBuilder.getFormat()) ;

            try {
                Date calDate = sdf.parse(dateBuilder.getDate());
                Calendar c = Calendar.getInstance() ;
                c.setTime(calDate);
                c.add(Calendar.YEAR,  dateBuilder.getYear());
                c.add(Calendar.MONTH, dateBuilder.getMonth());
                c.add(Calendar.DAY_OF_MONTH, dateBuilder.getDay());
                c.add(Calendar.HOUR, dateBuilder.getHour());
                c.add(Calendar.MINUTE, dateBuilder.getMinute());
                c.add(Calendar.SECOND, dateBuilder.getSecond());
                Date afterDate = c.getTime() ;
                System.out.print("add 1 day : " +afterDate.toString());
            } catch (ParseException e) {
                e.printStackTrace();
            }
        }
    }

2017年1月6日

設計模式-Singleton(獨體模式)

Singleton 定義

確保每次呼叫都只會有固定自行產生一個實例並提供給整個系統使用

Singleton 使用

  1. 單例模式可以避免產生多個物件消耗多個資源,如資料庫、IO存取
  2. 系統參數設定只需要共同一個設定,例如: andorid 中 Context系統內文

Singleton 單例案例

 
java範例
        /**
         * Name: singleton
         * Description :  To generate  onaly one database configuration  instane when it is called
         * Created by blackbryant on 2017/1/7.
         */
    public class DBConfigSinglton {
        public  static final DBConfigSinglton singlton = new DBConfigSinglton("com.mysql.jdbc.Driver","jdbc:mysql://localhost/EMP","jack","123456");
        private String className ;
        private String db_url ;
        private String user;
        private String password;

        private DBConfigSinglton(String className, String db_url, String user, String password){
            this.className = className ;
            this.db_url= db_url ;
            this.user = user ;
            this.password = password ;
        }

        public static DBConfigSinglton getInstance(){
            return singlton ;
        }
    }
 

Singleton Lazy mode

懶惰模式是當第一次呼叫getInstance()才建立實例,而案例一 是系統開啟就建立實例,所以某程度上懶惰模式節省資源的浪 費。懶惰模式呼叫getInstance()前面加上synchronized同步關鍵 字表示當每次呼叫時都需要做同步的檢查,這對效能上有很大 缺點所以不建議使用

 
    /**
     * Name: singleton
     * Description :  
     * Created by blackbryant on 2017/1/7.
     */
    public class DBConfigSingltonLazy {
        private  static  DBConfigSingltonLazy singlton = null;
        private String className ;
        private String db_url ;
        private String user;
        private String password;

        private DBConfigSingltonLazy(String className, String db_url, String user, String password){
            this.className = className ;
            this.db_url= db_url ;
            this.user = user ;
            this.password = password ;
        }

        public static synchronized DBConfigSingltonLazy getInstance(){
            if(singlton==null){
                singlton= new DBConfigSingltonLazy("com.mysql.jdbc.Driver","jdbc:mysql://localhost/EMP","jack","123456")
            }

            return singlton ;
        }

    }

Singleton- Double CheckLock(DCL)

DCL方法可以在需要時建立實例並保持執行緒安全,在呼叫 getInstance()不需要做同步檢查。DCL看起來很完美但是在高並 行的環境下是有可能出現問題或是低於JDK1.5以下

 
    /**
     * Name: singleton
     * Description : Singleton double checkLock
     * Created by blackbryant on 2017/1/7.
     */
    public class DBConfigSingltonDCL {
        private  static DBConfigSingltonDCL singlton = null;
        private String className ;
        private String db_url ;
        private String user;
        private String password;

        private DBConfigSingltonDCL(String className, String db_url, String user, String password){
            this.className = className ;
            this.db_url= db_url ;
            this.user = user ;
            this.password = password ;
        }

        public static  DBConfigSingltonDCL getInstance(){
            if(singlton==null){
                synchronized (DBConfigSingltonDCL.class) {
                    if(singlton==null)
                        singlton = new DBConfigSingltonDCL("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/EMP", "jack", "123456");
                }
            }

            return singlton ;
        }
    }

Singleton- Static Inner Class

當系統載入DBConfigSingltonSIC 不會建立實例,只有在呼叫 getInstance()才會由SingetonHolder開始建立實例,這種方式不 僅物件唯一性、延遲載入也確保執行緒的安全

 
    /**
     * Name: singleton
     * Description : Static Inner Class mode
     * Created by blackbryant on 2017/1/7.
     */
    public class DBConfigSingltonSIC {
        private  static DBConfigSingltonSIC singlton = null;
        private String className ;
        private String db_url ;
        private String user;
        private String password;

        private DBConfigSingltonSIC(String className, String db_url, String user, String password){
            this.className = className ;
            this.db_url= db_url ;
            this.user = user ;
            this.password = password ;
        }

        public  static DBConfigSingltonSIC getInstance(){
            return SingletonHolder.singleton;
        }


        public static class SingletonHolder{
            private static final DBConfigSingltonSIC singleton =   new DBConfigSingltonSIC("com.mysql.jdbc.Driver", "jdbc:mysql://localhost/EMP", "jack", "123456");
        }
    }

Singleton- Enum

 
    /**
     * Name: singleton
     * Description : Static Inner Class mode
     * Created by blackbryant on 2017/1/7.
     */
    public enum DBConfigSingltonEnum {
        INSTANCE{
            public void DBConfig(){
                this.className =  "com.mysql.jdbc.Driver"  ;
                this.db_url=  "jdbc:mysql://localhost/EMP" ;
                this.user = "jack" ;
                this.password = "123456" ;
            }
        };

        private String className ;
        private String db_url ;
        private String user;
        private String password;



        public static DBConfigSingltonEnum  getInstance(){
           return DBConfigSingltonEnum.INSTANCE;
        }
    }