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;
        }
    }