2010年3月26日 星期五

Design Pattern : 12 Decorator


把裝飾物件與主要內容物件當成相同的物件,也就是給予相同的API
可以在程式內用相同的方式操作,就可以用來組合出有彈性並複雜的結果
這就是Decorator

把裝飾與實際內容物當作相同的方式處裡
範例,要讓輸出字串可以彈性的增加外觀修飾的裝飾
把輸出物拆解成主要內容與裝飾內容,裝飾內容可能也多種的變化


參與者
1.Display (abstract)
提供一個主要內容與裝飾物都要依循的標準操作方法(API)


public abstract class Display {
    public abstract int getColumns();
    public abstract int getRows();
    public abstract String getRowText(int row);
    public final void show(){
        for(...){
            System.out.println(getRowText(i));
        }
    }
}


2.StringDisplay 主內容顯示類別
真正的主要內容顯示class,在實作抽象method時,具體的執行工作內容


public class StringDisplay extends Display {
    private String string;
    public StringDisplay(String string){
        this.string=string;
    }
    public int getColumns() { 
        return string.getBytes().length;
    }
    public int getRows() { 
        return 1;
    }
    public String getRowText(int row) { 
        if(row==0){
            return string;
        }else{
            return null;
        } 
    }
}


3.Border(abstract)
修飾抽象類別,還是繼承了主要Display類別,但是導入了一個display物件
來作為內部method委讓操作使用


public abstract class Border extends Display {
    protected Display display;  
    public Border(Display display){
        this.display=display;
    }
}


4.SlideBorder
在實作抽象method內,利用委讓操作display物件取得主要內容
再添加上裝飾的內容後,將最後結果輸出


public class SlideBorder extends Border {
    private char borderChar;
    public SlideBorder(Display display,char borderChar){
        super(display);
        this.borderChar=borderChar;
    }
    public int getColumns() {
        return 1+display.getColumns()+1;
    }
    public int getRows() {
        return display.getRows();
    }
    public String getRowText(int row) {
        return borderChar+display.getRowText(row)+borderChar;
    }
}


5.Main外部程式
可任意包裝產生所要的結果


Display b1=new StringDisplay("hello ozzy");
b1.show();
Display b2=new SlideBorder(new StringDisplay("hello sun"),'*');
b2.show();



2010年3月25日 星期四

Design Pattern : 11 Composit





當我們遇到有容器(container)與內容(content)需要作遞迴處理
例如要對一個目錄下所有的檔案與目錄都作處理
因為目錄(container)與檔案(content)都可以被裝到目錄下
這時候,可以把兩者都當作是"目錄進入點"(entry)來看
這時候容器與內容就都是屬於同一類的東西

這時候針對entry上定義一個"進入點"所需要的功能
而容器與內容都只是entry的子類別,在遞迴程序的操作上
只要使用屬於entry的method即可,而不用管是容器或是內容

參與者
1.Entry(abstract)
實際執行顯示的是printList(String prefix)
這部份在子類別內實作
而留給外部呼叫的只有printList()



public abstract class Entry {
    public abstract String getName();
    public abstract int getSize();
    public void printList(){
        priinList("");
    }
    protected abstract void printList(String prefix);
}

2.File
把Entry內設定的3個abstract method 實作出來


public class File extends Entry {
    private String name;
    private int size;
    public File(String name,int size){
        this.name=name;
        this.size=size;
    } 
    public String getName() { 
        return this.name;
    }
    public int getSize() { 
        return this.size;
    }
    protected void printList(String prefix) { 
        System.out.println(prefix+"/"+this);
    }
}

3.Directory
在屬於container的class內使用iterator來迴圈執行entry所提供的method


public class Directory extends Entry {
    private String name;
    private Vector directory=new Vector();
    public Directory(String name){
        this.name=name;
    }
    public String getName() {  
        return this.name;
    }
    public int getSize() { 
        int size=0;
        Iterator it=directory.iterator();
        while(it.hasNext()){
            Entry entry=(Entry)it.next();
            size+=entry.getSize();
        }
        return size;
    }
    protected void printList(String prefix) {
        //把自己列印出來,並叫其子node執行entry的列印指令
        System.out.println(prefix+"/"+this);
        Iterator it=directory.iterator();
        while(it.hasNext()){
            Entry entry=(Entry)it.next();
            entry.printList(prefix+"/"+name);
        }
    }
    public Entry add(Entry entry){
        directory.add(entry);
        return this;
    }
}

4.Main 外部程式


Directory root=new Directory("root");
Directory folder1=new Directory("folder1");
File file1=new File("file01",100);
File file2=new File("file02",200);
root.add(folder1);
folder1.add(file1);
folder1.add(file2);
root.printList(); 



2010年3月15日 星期一

Design Pattern : 09 Bridge

通常產生子類別,會有兩種型態
1.extends繼承:
以繼承方式來產生子類別的目的,是在子類別新增功能
屬於功能性的擴增
2.implement實作:
父類別只是一個抽象類別(abstract)或介面(interface)
實作後的子類別,與父類別的功能是相同的,但每個功能可以有不同的展現
屬於實作部分的擴增

如果在做程式設計,希望可以把實作與功能部分完全分離,
如範例中,Display屬於功能性父類別,DisplayImp屬於實作父類別
如果想要新增功能,就擴充在Display的子類別內
如果想要改變輸出型態,如輸出文字變輸出html,
則新增DisplayImp的子類別

Bridge Pattern就是在功能類別(Display)內放一個變數(impl)存放實作類別(DisplayImp)的實體
讓功能類別(Display)內的method都是在操作實作類別(DisplayImp)的功能
把分離的實作擴充與功能擴充部分串起來,就是Bridge Pattern
參與者
1.Display(功能父類別)
在Display功能父類別內,利用一個變數impl儲存實作類別的instance
所有在功能類別內的操作,內部真正都執行的都是impl在執行

public class Display {
    private DisplayImp impl;
    public Display(DisplayImp impl){
        this.impl=impl;
    }
    public void open(){
        impl.rawOpen();
    }
} 

2.abstract DisplayImp(實作的父類別)
實作父類別,定義了所要實作類別所需要的method名稱,再交由子類別去實作出來

public abstract class DisplayImp {
    public abstract void rawOpen();
    public abstract void rawPrint();
    public abstract void rawClose();
}

3.CountDisplay(擴充了功能父類別Display)
要新增加功能,就加在這功能擴充的子類別上

public class CountDisplay extends Display {
    public CountDisplay(DisplayImp impl){
        super(impl);
    }
    public void multiOutPut(int num){
    this.open();
    this.print();
    this.print();
    this.close();
    }
}

4.StringDisplayImp(真正實作可以執行的實作類別)

public class StringDisplayImp extends DisplayImp {
    private String string;
    public StringDisplayImp(String string){
        this.string=string;
    }
    public void rawOpen(){
        System.out.println("---Is Open-------");
    };
    .....
}

5.Main外部程式

Display d1=new Display(new StringDisplayImp("OZ D1"));
CountDisplay d2=new CountDisplay(new StringDisplayImp("OZ D2"));
d1.output();
d2.output();
d2.multiOutPut(3);//執行功能擴充後,新增的method



ps:
當以繼承方式產生子類別,子類別並不會繼承父類別的建構子
如果父類別建構子有接受參數執行項目
那在繼承後,子類別在建構子內,並須執行super(xx)

2010年3月12日 星期五

Design Pattern : 07 Builder


當我們想要建構出一個比較複雜的結果
或是想對相同內容提供不同的輸出樣式,就可以使用Builder Pattern

其流程是
1.先產生一個Builder類別,在Builder內
提供各種基本method
2.再產生一個組合程式用的Director類別
在Director內組合各式的builder 功能,來產生一個比較複雜的結果
外部程式只要操作Director就可以得到想要的結果
3.如果要讓同樣的內容可以產生不同格式的輸出
可以繼承Builder產生各式的Builder類別,來產生不同形式的輸出樣式

參與者
1.Builder
抽象類別,規範了要提供給Director操作的的各項method


class abstract Builder{
    public abstract makeTitle(String _title);
    public abstract makeBody(String _body);
    public abstract makeLine();
    public abstract Object getResult();
}

2.TextBuilder
繼承自Builder,並把相關method都實作出來


class abstract TextBuilder extends Builder{
    private StringBuffer buffer=new StringBuffer()
    public makeTitle(String _title){
        buffer.append(_title)
    };
    public makeBody(String _body){..};
    public makeLine(){..};
    public Object getResult(){
        return buffer.toString();
    };
}

3.Director
實體類別,在內部操作並組合各種Builder的method產生所要的結果
在Director只操作Builder類別的method,不可操作builder的子類別專屬method


class Director{
    private Builder builder;
    public Director(Builder builder){
        this.builder=builder;
    }
    public void setData(String _title,String _body){
        builder.makeLine();
        builder.makeTitle(_title);
        builder.makeBody(_body);
        builder.makeLine();
    }
    public void show(){
        System.out.println(builder.getResult().toString());
    }
}

4.Main外部程式


Director director=new Director(new TextBuilder());
director.setData("title","body");
director.show();

2010年3月10日 星期三

Design Pattern : 06 Prototype


通常我們產生一個instance 通常都會是利用new Class方式
來產生想要的instnace,但如果要產生的物件是一個比較麻煩的東西
譬如說,在畫布上隨手畫出的物件,想要產生多個instance,
這時候,就需要用clone的方式,針對既存的物件原形複製出來

把一個物件class當作原型,丟到manager內當作一個參考
當使用者需要一個instnace時,manager就依照這個原型
複製出一個相同物件出來,傳給需求單位

Prototype Patterng的結構是包含
1.一個Manager Class
2.一個Product 抽象類別:
定義了要在Manager內被呼叫的method
Product內包括了一個createClone()的method
可以對自身複製出一個instance傳出去,至於實作部分就留給其繼承者實作出來即可
3.多個繼承自Product的class(在這範例是StringBox)

其運作流程為
1.利用Manager註冊Product
2.利用Manager產生Product instance
3.操作Product instance
參與者
1.Manager
本身為標準class,非abstract
但裡面method呼叫的是以抽象類別(Product)所訂的method來執行
在這裡面包含了產生product instance的運算邏輯


public class Manager {
    private Hashtable table=new Hashtable();
    public void register(String name,Product p){
        table.put(name,p); 
    }
    public Product create(String protoname){
    Product p=(Product)table.get(protoname);
        return p.createClone();
    }
}


先使用register把Product與設定要產生這product的字串註冊到這manager內
以後就可以利用註冊的字串用create來取得該product 的instance

2.Product(framework)
規範了在Managaer內會被呼叫的method名稱
public abstract class Product implements Cloneable {
public abstract Product createClone();
public abstract void show(String s);
}
在Java內,Product一定要先implements Cloneable


createClone()就是clone自己產生instance的地方,這部分留給繼承者實作
3.StringBox
public class StringBox extends Product {
    public Product createClone(){
        Product p=null;
        try{
            p=(Product)clone();
        }catch(CloneNotSupportedException e){
            e.printStackTrace();
        }
        return p;
    }
}


4.Main(外部應用程式)


Manager manager=new Manager();//產生manager
manager.register("my-",(Product)new StringBox('-'));//註冊product到manager上
Product p=manager.create("my-");//利用manager產生product instance
p.show("hello world");//操作product

2010年3月9日 星期二

Design Pattern : 05 Singleton


當在整個程式裡,我們對於某個clas
只需要存在一個instance時,例如整個程式的設定檔
整個通用的css class,這時可使用Singleton Pattern達到這個目的
參與者
1.Singleton
只能透過 class的 static method getInstance來取得instance
不能在外部使用new 來產生instance,所以將建構子設定為private


public class Singleton{
    private static Singleton _singleton=new Singleton('myid')
    private String myid;//這是instance的var
    private static String myVer="Sample";//這是static var
    private Singleton(String myid){
        this.myid=myid;
        showID();
    }
    private void showID(){
        System.out.println(this.myid)
    }
    public static Singleton getInstance(){
        System.out.println(myVer);
        return _singleton();
    }
}

2.Main 外部主程式


Singleton.getInstance();


PS:在static method內,只能夠呼叫使用staic variable或method
建構子是屬於instance method,可以呼叫使用static或instance method
在使用Vector來儲存物件,記得要宣告內存的類型
Vector myv=new Vector();

2010年3月8日 星期一

Design Pattern : 04 Factory

要建立一個可以用來產生product 的factory class
想要做到,不管什麼product都可以套用相同的產生流程
也就是說一個factory可以用來產生不同的product
這時候就可以用Factory Pattern

定義好抽象的Factory類別與Product類別當框架Framework
在Factory定義實體method產生Product物件
在外部程式要利用Factory產生Product

都是利用上層Framework設定好抽象的factory與product
所提供的public method

但真正內部運作的是分別繼承自factory與product的realFactory
與realProduct

如果要產生不同的product,就可以分別產生不同的realFactory與
realProduct
在不變動外部Main程式狀況下 即可直接使用
參與者
1.Factory (屬framwork)
在Factory class內是用到了Templete Pattern,
提供外部呼叫使用的public method(create),是一個實體的method
而在這public method內呼叫執行的是一些抽象method
這些method就必須要被繼承的子類別實作出來


public abstract classFactory{
    public Product create(){
        Product p =createProduct();
        registProduct(p);
        return p;
    }
    protected abstract Product createProduct();
    protected abstract void registProduct(Product p);
}

2.RealFactory (instance)
繼承自Factory後,把factory內定一的抽象method都實作出來

public class RealFactory extends Factory{
    protected Product createProduct(){
        return RealProduct();  
    };
    protected abstract void registProduct(Product p){
        .....
    };
}

3.Product (屬framework)
只定義public 抽象method,來規範外界要操作使用product的方法

public abstract class Product{
    public abstract void use();
}

4.RealProcuct(instance)
真正的產品class,繼承自Product,並把Product內定義的
抽象method 實作

public class RealProduct{
    public void use(){
        .....
    }
}

5.Main(外部主程式)
產生一個實體的Factory,雖然是new RealFactory()
但類別宣告,還是宣告在其上層的Factory
利用產生的factory來產生product
真正產生的是realProduct物件
但類別還是宣告在其上層的Product class


Factory factory=new RealFactory();
Product product=factory.creat()
product.use();



ps:在Product的子類別,不提供public的建構子,目的是在於
強制規範product只能經由factory去產生,而不能自行使用
new product()產生出來

2010年3月5日 星期五

Design Pattern : 03 Template


在父類別定義好大綱(也就是運算邏輯),
子類別只要實作method 就可以讓多個class共用相同的運算邏輯
參與者
1.Template 父類別
Template必須為abstract class,不能用interface宣告
因為在template內,負責主要邏輯運算的method 必須要是一個
final method



public abstract class Template{
    public abstract void open();
    public final void display(){
        open();
        open();
        open();
    }
}

sample裡的display method包含了運算邏輯,是一個實體的method,但
在裡面呼叫執行的是抽象 method,這部分的method一定要在子類別內被實做
2.MyDisplay 子類別
子類別繼承Template,並把abstract 部分實作出來



public class MyDisplay{
    public void open(){
        .....
    }
}  

3.Main主程式



Template a=new MyDisplay();
a.display();

主程式執行時,會用到的是包含運算邏輯的method

Abstract class與Interface的差別
1.只要class內有method宣告為abstract,則class就必須宣告為abstract
Interface內所有的method都必須是abstract
也就是說在abstract class內,還可包含有非abstract的method
如果是extend abstract method ,只要 實作abstract method即可
對於非abstract method可直接使用,若要改寫則要override


2.只要是abstract class就不能被直接new 出instance


3.Class一次只可以繼承(extends )一個父class
但可以實作(implement)多個class
亦可同時extends 與implement

2010年3月4日 星期四

Design Pattern : 02 Adapter


當在開發Class時,在功能上可以利用舊的Class功能
這時候這個新的Class,就可以用Adapter方式去包裝
也就是說
在Adapter內定義新的method,但method內實際是在執行舊Class的method
使用者只知道他呼叫的是Adapter的method,不需知道其內部是如何運作
Adapter的做法,分別可以用繼承或委讓方式來實做
參與者
1.Old Class
原有已經開發好的Class
譬如 原本已有一個print() method
2.Adapter1
使用繼承方式來包裝Old Clas所產生新的Class


class Adapter1 extend Old{
    function Adapter1(){}
    function show(){print()}
}

3.Adapter2
在Class內包含了一個Old Class的instnace,用委讓的方式
來執行真正要做的事


class Adapter2{
    var old:Old
    function Adapter2(){
        old=new Old();
    }
    function show(){
        old.print()
    }
}

4.Main
主要應用程式


//var adapter:Adapter1=new Adapter1('hello');
var adapter:Adapter2=new Adapter2('hello');
adapter.show();

2010年3月3日 星期三

Design Pattern : 01 Iterator

通常,我們要對一個陣列的所有元素作處理
會以一個for迴圈,直接對陣列做操作
使用Iterator Pattern,則是變為對Iterator物件操作
而非對陣列直接操作
整個Pattern最主要就是在於Iterator Class的設計
參與者

1.Aggregate
可能是一個要被處理的陣列集合,內部包含
1.iterator():Iterator
取得這個Aggregate的iterator

2.Iterator
必定包含三個部分
1.Iterator(aggreate:Array)
  把要處理的集合陣列當作參數傳遞到Iterator內
2.hasNext():Boolean
  判斷是否還有下個元素
3.next():Object
  先取得元素傳出去,並跳到下一個index

3.Main
應用程式

var aggregate:Array=new Array(....)
//var it:Iterator=new Iterator(aggregate);
var it:Iterator=aggregate.iterator();
while(it.hasNext()){
    var obj=it.next();
}


PS:以下系列Pattern文章為"Design Patterns於Java語言上的實習應用"
這本書的心得與Memo筆記