1. 程式人生 > >設計模式精髓篇之建立型

設計模式精髓篇之建立型

說明

借鑑的JDK中的優秀原始碼來幫助自己理解設計模式,另外一方面還能幫助自己深入的瞭解JDK。型別中包含的模式可能不齊全,我只詳細的描述了一些相對重要的設計模式。

類之間的關係

設計模式主要是基於類之間的關係來設計的。先了解類之間的關係和表示形式是很有必要的。
繼承關係
繼承
實現關係
實現
依賴關係
類A使用到了另一個類B,或者物件方法中返回B類物件
依賴
關聯關係
類A的成員變數是類B,體現的是兩個類之間語義級別的一種強依賴關係
關聯
聚合關係
聚合是關聯關係的一種特例,它體現的是整體與部分的關係,即has-a的關係。整體與部分可分。如公司與員工。
聚合
組合關係


組合也是關聯關係的一種特例,它體現的是一種contains-a的關係,這種關係比聚合更強。整體與部分是不可分的關係。如人體與心臟。
組合

物件的四種生成方式

1.使用new來建立。

Object object=new Object();//此種方法將會呼叫類的建構函式。

2.java的反射機制

///////假設你已經建立了一個類Test

//方式一,使用Class類,會呼叫類的建構函式,方式一內部的實現還是呼叫方式二的getConstructor方法來實現的
Class class=Class.forName("包名.Test");
Test test1=(Test)class.newInstance();//建立了test物件,由object物件轉換為test物件
//方式二使用Constructor,會呼叫類的建構函式 Constructor<Test>cons=Test.class.getConstructor(); Test test2=cons.newInstance();

3.clone方法

///////假設你已經建立了一個類Test,其中Test類需要實現Cloneable介面,複寫clone方法。具體怎麼複寫,需要看你是深複製還是淺複製.
Test test=new Test();//之前已經建立了一個物件.
Test test1=test.clone();//建立了一個新的物件,此時不在呼叫建構函式,並且test1指向的新堆的記憶體空間.

4.反序列化方法

///當我們序列化和反序列化一個物件,jvm會給我們建立一個單獨的物件。類需要實現序列化介面
ObjectInputStream in = new ObjectInputStream(new FileInputStream("test.obj"));
Test test= (Test) in.readObject();

建立型模式

提供了建立什麼(what),由誰來建立(who),以及何時(when)來建立

單例模式

目標: 向整個系統提供一個唯一的物件例項。
why-思考: 如何確保要生成的類的物件只有一個?當需要使用使用該物件時是使用時先生成還是建立該類的時候生成?如何確保多處同時需要訪問唯一物件時,怎麼確保原子性?
How-思考: 將構造方法私有化,並在內中方法生成物件,外部將不能生成物件了;需要檢視具體的情況,看資源使用率和執行緒安全性,資源使用率高的是懶載入,執行緒安全性好的建立類時便生成物件;使用多執行緒間鎖的機制。
JDK中的示例:

//java.lang.Runtime類
public class Runtime {
    private Runtime() {}//私有的構造方法
    //Runtime類的私有靜態例項
    private static Runtime currentRuntime = new Runtime();
    //獲得靜態例項的唯一方法
    public static Runtime getRuntime() {
        return currentRuntime;
    }
}
工廠三大類模式

包括靜態工廠、工廠模式、抽象工廠。工廠相關的模式主要包含要建立的物件類(產品類)和主導有建立功能的類(工廠類)。
目標:

*靜態工廠*,因為要建立的型別較少,可根據型別進行建立物件,並且要將物件的例項化過程進行封裝,外部建立物件呼叫的方式更清晰。
*工廠模式*,因為要建立的型別的物件較多,並且可能需要擴充套件物件的型別,讓子類去決定例項化那個物件。重點在如何抽取要建立的物件類的共同點上。
**抽象工廠**,因為要建立的物件的包含的組別很多,不同的組包括不同的型別,為建立一組相關或相互依賴的物件。重點在如何抽取要建立的物件類的共同點上和抽象化工廠類上。

why—思考:

*靜態工廠*,工廠類如何做到根據不同的引數型別,輸出不同的物件?
*工廠模式*,如果物件的引數型別過多,並且或者需要不定期擴充套件不同的物件時,工廠類該怎樣設計?
**抽象工廠**,要建立的物件是一組相關的或相互依賴,並且組內不同的物件型別可以不同,工廠模式又該怎麼設計?

How-思考:

*靜態工廠*,通過新增靜態生成物件的函式,根據不同的引數,使用不同的例項化物件的方法。
*工廠模式*,提取出要建立的不同型別的物件的公共方法並封裝成介面,在具體的物件中實現該介面。
**抽象工廠**,工廠模式的擴充套件,因為組包含一組包含多個不同型別的物件。

JDK中的示例:

//////////////////////////靜態工廠java.lang.Class
//根據不同的類名來建立物件
public static Class<?> forName(String className)
                throws ClassNotFoundException {
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
    }

//////////////////////////工廠, Iterator類產品類
//說明,因為Iterator包含很多種類,有LinkList中的ListItr、ArrayList中的ListItr、Vector中的ListItr(都是內部類),以後可能還需要擴充套件其他集合類的Iterator。
//提取出要建立的不同型別的物件的公共部分並封裝成介面。
interface Iterator<E> 
{ 
 boolean hasNext();
 E next();
}
//LinkList中的ListItr、ArrayList中的ListItr、Vector中的ListItr根據自身特徵分別實現介面。
//工廠類Collection(LinkList、ArrayList、Vector)中建立產品。
Iterator<Object> it=list.iterator();

//////////////////////////抽象工廠 java.sql.Connection
//說明,抽象工廠重點在產品類的抽象和工廠類的抽象。
 interface Connection{
 //工廠要建立的產品組:Clob ,Blob ,NClob ,Statement(每個都是抽象的產品)
 Clob createClob() throws SQLException;
 Blob createBlob() throws SQLException;
 NClob createNClob() throws SQLException;
 Statement createStatement() throws SQLException;
 ......
 }
 //抽象產品Statement由很多產品物件的公共部分組成介面
 interface Statement{
  boolean execute(String sql) throws SQLException;
  ResultSet getResultSet() throws SQLException;
  .....
 }
 //具體的建立物件可以實現Statement功能形成不同的物件。
 //Clob ,Blob ,NClob同是抽象產品可以具體化到很多產品
 //大工廠實現Connection介面將物件組合。
 /////////工廠模式可以看出抽象工廠的組內只有一種型別。
建造者模式

目標: 將一個複雜物件的構建和它的表示分離。重點在抽象出類的Build類。
why-思考: 怎麼將一個複雜的類構建尼?構建後怎麼達到分離尼?
How-思考: 類的構建主要是對類的成員變數進行重新設定,因為每個成員變數可以取不同的值,但是最後都是需要設定的,所以對每個成員變數可以抽取出各自的的設定部分,從而將類的構建分離出。
正常示例:

//JDK中沒有找到好的示例,列舉兩種方式。
//////////////////////////////////////////////方式一
public class Test
//複雜類
{
    private String part1;
    private String part2;
    private String part3;
    ......
}
//分離Build,抽象化,重點
interface Build{
public interface ICarBuilder
{
    public void buildpart1();
    public void buildpart2();
    public void buildpart3();
    Test buildTest();
}
//ConBuild實現Build介面
conBuild.buildpart1();
......
Test test=conBuild.buildTest();
}
//////////////////////////////////////////////方式二
//將Build封裝在要建立的物件內部。
Class Test{
    private String part1;
    private String part2;
    private String part3;
    ......
    private ClassTest(Build build){
     this.part1=build.part1;
     ......
    }
    public static Class Build{
     private String part1;
     private String part2;
     private String part3;
     public Builder part1(string val){
            this.part1= val;
            return this;
        }
     public Builder part2(string val){
            this.part2= val;
            return this;
        }
        ......
     public Test build(){
       return new Build(this);
     }
    }
}
//通過分離者Build來建立物件例項
Test test=new Test.Build().part1("1").part2("2").part3("3").build();
//方式二相對方式一擴充套件性更弱,好處是易於程式碼的管理
原型模式

目標: 用原型例項大量的複製建立新的物件。重點是需要大量建立新物件。
JDK中的示例:

介面Cloneable的用法

感想

對建立型模式的感想(到模式中去)
1.明確專案中要建立的物件是否只需要一個。或者是否需要大批覆制建立。
2.要建立的物件是否很複雜,複雜體現在該物件對應的類的成員變數是否包含多個,並且成員變數多變,賦值過程分離出來是否更好。
3.要建立的物件的型別是否很多,學會抽象出共同部分,當型別多,或者多變的情況,一定首先想到要面向介面程式設計,抽取共同部分,然後**分角色**考慮問題是工廠者,物件產品或者建造者。

----分角色思考問題很重要,抽取共同部分也很重要!!!-------