重學 Java 設計模式:實戰原型模式

小傅哥發表於2020-05-29


作者:小傅哥
部落格:https://bugstack.cn

沉澱、分享、成長,讓自己和他人都能有所收穫!?

一、前言

老闆你加錢我的程式碼能飛

程式設計師這份工作裡有兩種人;一類是熱愛喜歡的、一類是僅當成工作的。而喜歡程式碼程式設計的這部分人會極其主動學習去豐富自己的羽翼,也非常喜歡對技術探索力求將學到的知識賦能到平時的業務需求開發中。對於這部分小夥伴來說上班寫程式碼還能賺錢真的是幸福!

怎麼成為喜歡編碼都那部分人

無論做哪行那業你都喜歡,往往來自從中持續不斷都獲取成就感。就開發程式設計而言因為你的一行程式碼影響到了千千萬萬的人、因為你的一行程式碼整個系統更加穩定、因為你的一行程式碼扛過了所有秒殺等等,這樣一行行的程式碼都是你日積月累學習到的經驗。那如果你也想成為這樣有成就感的程式設計師就需要不斷的學習,不斷的用更多的技能知識把自己編寫的程式碼運用到更核心的系統。

方向不對努力白費

平常你也付出了很多的時間,但就是沒有得到多少收益。就像有時候很多小夥伴問我,我是該怎麼學一個我沒接觸過的內容。我的個人經驗非常建議,先不要學太多理論性的內容,而是嘗試實際操作下,把要學的內容做一些Demo案例出來。這有點像你買了個自行車是先拆了學學怎麼個原理,還是先騎幾圈呢?哪怕摔了跟頭,但那都是必須經歷後留下的經驗。

同樣我也知道很多人看了設計模式收穫不大,這主要新人對沒有案例或者案例不貼近實際場景沒有學習方向導致。太空、太虛、太玄,讓人沒有抓手!

所以我開始編寫以實際案例為著手的方式,講解設計模式的文章,幫助大家成長的同時也讓我自己有所沉澱!

二、開發環境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程三個,可以通過關注公眾號bugstack蟲洞棧,回覆原始碼下載獲取(開啟獲取的連結,找到序號18)
工程 描述
itstack-demo-design-4-00 場景模擬工程,模擬線上考試題庫抽提打亂順序
itstack-demo-design-4-01 使用一坨程式碼實現業務需求,也是對ifelse的使用
itstack-demo-design-4-02 通過設計模式優化改造程式碼,產生對比性從而學習

三、原型模式介紹

原型模式,圖片來自 refactoringguru.cn

原型模式主要解決的問題就是建立重複物件,而這部分物件內容本身比較複雜,生成過程可能從庫或者RPC介面中獲取資料的耗時較長,因此採用克隆的方式節省時間。

其實這種場景經常出現在我們的身邊,只不過很少用到自己的開發中,就像;

  1. 你經常Ctrl+CCtrl+V,複製貼上程式碼。
  2. Java多數類中提供的API方法;Object clone()
  3. 細胞的有絲分裂。

類似以上的場景並不少,但如果讓你去思考平時的程式碼開發中,有用到這樣的設計模式嗎?確實不那麼容易找到,甚至有時候是忽略了這個設計模式的方式。在沒有閱讀下文之前,也可以思考下哪些場景可以用到。

四、案例場景模擬

場景模擬;考試試卷

每個人都經歷過考試,從紙製版到上機答題,大大小小也有幾百場。而以前坐在教室裡答題身邊的人都是一套試卷,考試的時候還能偷摸或者別人給發資訊抄一抄答案。

但從一部分可以上機考試的內容開始,在保證大家的公平性一樣的題目下,開始出現試題混排更有做的好的答案選項也混排。這樣大大的增加了抄的成本,也更好的做到了考試的公平性。

但如果這個公平性的考試需求交給你來完成,你會怎麼做?

因為需要實現一個上機考試抽題的服務,因此在這裡建造一個題庫題目的場景類資訊,用於建立;選擇題問答題

1. 場景模擬工程

itstack-demo-design-4-00
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── AnswerQuestion.java
                └── ChoiceQuestion.java
  • 在這裡模擬了兩個試卷題目的類;ChoiceQuestion(選擇題)、AnswerQuestion(問答題)。如果是實際的業務場景開發中,會有更多的題目型別,可以回憶一下你的高考試卷。

2. 場景簡述

2.1 選擇題

public class ChoiceQuestion {

    private String name;                 // 題目
    private Map<String, String> option;  // 選項;A、B、C、D
    private String key;                  // 答案;B

    public ChoiceQuestion() {
    }

    public ChoiceQuestion(String name, Map<String, String> option, String key) {
        this.name = name;
        this.option = option;
        this.key = key;
    }

    // ...get/set
}

2.2 問答題

public class AnswerQuestion {

    private String name;  // 問題
    private String key;   // 答案

    public AnswerQuestion() {
    }

    public AnswerQuestion(String name, String key) {
        this.name = name;
        this.key = key;
    }

    // ...get/set
}
  • 以上兩個類就是我們場景中需要的物料內容,相對來說比較簡單。如果你在測試的時候想擴充學習,可以繼續新增一些其他物料(題目型別)。

五、用一坨坨程式碼實現

今天的實現方式沒有ifelse了,但是沒有一個類解決不了的業務,只要你膽大!

在以下的例子中我們會按照每一個使用者建立試卷的題目,並返回給呼叫方。

1. 工程結構

itstack-demo-design-4-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── QuestionBankController.java
  • 一個類幾千行的程式碼你是否見過,嚯?那今天就再讓你見識一下有這樣潛質的類!

2. 一把梭實現需求

public class QuestionBankController {

    public String createPaper(String candidate, String number) {

        List<ChoiceQuestion> choiceQuestionList = new ArrayList<ChoiceQuestion>();
        List<AnswerQuestion> answerQuestionList = new ArrayList<AnswerQuestion>();

        Map<String, String> map01 = new HashMap<String, String>();
        map01.put("A", "JAVA2 EE");
        map01.put("B", "JAVA2 Card");
        map01.put("C", "JAVA2 ME");
        map01.put("D", "JAVA2 HE");
        map01.put("E", "JAVA2 SE");

        Map<String, String> map02 = new HashMap<String, String>();
        map02.put("A", "JAVA程式的main方法必須寫在類裡面");
        map02.put("B", "JAVA程式中可以有多個main方法");
        map02.put("C", "JAVA程式中類名必須與檔名一樣");
        map02.put("D", "JAVA程式的main方法中如果只有一條語句,可以不用{}(大括號)括起來");

        Map<String, String> map03 = new HashMap<String, String>();
        map03.put("A", "變數由字母、下劃線、數字、$符號隨意組成;");
        map03.put("B", "變數不能以數字作為開頭;");
        map03.put("C", "A和a在java中是同一個變數;");
        map03.put("D", "不同型別的變數,可以起相同的名字;");

        Map<String, String> map04 = new HashMap<String, String>();
        map04.put("A", "STRING");
        map04.put("B", "x3x;");
        map04.put("C", "void");
        map04.put("D", "de$f");

        Map<String, String> map05 = new HashMap<String, String>();
        map05.put("A", "31");
        map05.put("B", "0");
        map05.put("C", "1");
        map05.put("D", "2");

        choiceQuestionList.add(new ChoiceQuestion("JAVA所定義的版本中不包括", map01, "D"));
        choiceQuestionList.add(new ChoiceQuestion("下列說法正確的是", map02, "A"));
        choiceQuestionList.add(new ChoiceQuestion("變數命名規範說法正確的是", map03, "B"));
        choiceQuestionList.add(new ChoiceQuestion("以下()不是合法的識別符號", map04, "C"));
        choiceQuestionList.add(new ChoiceQuestion("表示式(11+3*8)/4%3的值是", map05, "D"));
        answerQuestionList.add(new AnswerQuestion("小紅馬和小黑馬生的小馬幾條腿", "4條腿"));
        answerQuestionList.add(new AnswerQuestion("鐵棒打頭疼還是木棒打頭疼", "頭最疼"));
        answerQuestionList.add(new AnswerQuestion("什麼床不能睡覺", "牙床"));
        answerQuestionList.add(new AnswerQuestion("為什麼好馬不吃回頭草", "後面的草沒了"));

        // 輸出結果
        StringBuilder detail = new StringBuilder("考生:" + candidate + "\r\n" +
                "考號:" + number + "\r\n" +
                "--------------------------------------------\r\n" +
                "一、選擇題" + "\r\n\n");

        for (int idx = 0; idx < choiceQuestionList.size(); idx++) {
            detail.append("第").append(idx + 1).append("題:").append(choiceQuestionList.get(idx).getName()).append("\r\n");
            Map<String, String> option = choiceQuestionList.get(idx).getOption();
            for (String key : option.keySet()) {
                detail.append(key).append(":").append(option.get(key)).append("\r\n");
                ;
            }
            detail.append("答案:").append(choiceQuestionList.get(idx).getKey()).append("\r\n\n");
        }

        detail.append("二、問答題" + "\r\n\n");

        for (int idx = 0; idx < answerQuestionList.size(); idx++) {
            detail.append("第").append(idx + 1).append("題:").append(answerQuestionList.get(idx).getName()).append("\r\n");
            detail.append("答案:").append(answerQuestionList.get(idx).getKey()).append("\r\n\n");
        }

        return detail.toString();
    }

}
  • 這樣的程式碼往往都非常易於理解,要什麼程式就給什麼程式碼,不物件導向,只程式導向。不考慮擴充套件性,能用就行。
  • 以上的程式碼主要就三部分內容;首先建立選擇題和問答題到集合中、定義詳情字串包裝結果、返回結果內容。
  • 但以上的程式碼有一個沒有實現的地方就是不能亂序,所有人的試卷順序都是一樣的。如果需要加亂序也是可以的,但複雜度又會增加。這裡不展示具體過多實現,只為後文對比重構

3. 測試驗證

接下來我們通過junit單元測試的方式驗證介面服務,強調日常編寫好單測可以更好的提高系統的健壯度。

編寫測試類:

@Test
public void test_QuestionBankController() {
    QuestionBankController questionBankController = new QuestionBankController();
    System.out.println(questionBankController.createPaper("花花", "1000001921032"));
    System.out.println(questionBankController.createPaper("豆豆", "1000001921051"));
    System.out.println(questionBankController.createPaper("大寶", "1000001921987"));
}

結果:

考生:花花
考號:1000001921032
--------------------------------------------
一、選擇題

第1題:JAVA所定義的版本中不包括
A:JAVA2 EE
B:JAVA2 Card
C:JAVA2 ME
D:JAVA2 HE
E:JAVA2 SE
答案:D

第2題:下列說法正確的是
A:JAVA程式的main方法必須寫在類裡面
B:JAVA程式中可以有多個main方法
C:JAVA程式中類名必須與檔名一樣
D:JAVA程式的main方法中如果只有一條語句,可以不用{}(大括號)括起來
答案:A

第3題:變數命名規範說法正確的是
A:變數由字母、下劃線、數字、$符號隨意組成;
B:變數不能以數字作為開頭;
C:A和a在java中是同一個變數;
D:不同型別的變數,可以起相同的名字;
答案:B

第4題:以下()不是合法的識別符號
A:STRING
B:x3x;
C:void
D:de$f
答案:C

第5題:表示式(11+3*8)/4%3的值是
A:31
B:0
C:1
D:2
答案:D

二、問答題

第1題:小紅馬和小黑馬生的小馬幾條腿
答案:4條腿

第2題:鐵棒打頭疼還是木棒打頭疼
答案:頭最疼

第3題:什麼床不能睡覺
答案:牙床

第4題:為什麼好馬不吃回頭草
答案:後面的草沒了


考生:豆豆
考號:1000001921051
--------------------------------------------
一、選擇題

第1題:JAVA所定義的版本中不包括
A:JAVA2 EE
B:JAVA2 Card
C:JAVA2 ME
D:JAVA2 HE
E:JAVA2 SE
答案:D

第2題:下列說法正確的是
A:JAVA程式的main方法必須寫在類裡面
B:JAVA程式中可以有多個main方法
C:JAVA程式中類名必須與檔名一樣
D:JAVA程式的main方法中如果只有一條語句,可以不用{}(大括號)括起來
答案:A

第3題:變數命名規範說法正確的是
A:變數由字母、下劃線、數字、$符號隨意組成;
B:變數不能以數字作為開頭;
C:A和a在java中是同一個變數;
D:不同型別的變數,可以起相同的名字;
答案:B

第4題:以下()不是合法的識別符號
A:STRING
B:x3x;
C:void
D:de$f
答案:C

第5題:表示式(11+3*8)/4%3的值是
A:31
B:0
C:1
D:2
答案:D

二、問答題

第1題:小紅馬和小黑馬生的小馬幾條腿
答案:4條腿

第2題:鐵棒打頭疼還是木棒打頭疼
答案:頭最疼

第3題:什麼床不能睡覺
答案:牙床

第4題:為什麼好馬不吃回頭草
答案:後面的草沒了


考生:大寶
考號:1000001921987
--------------------------------------------
一、選擇題

第1題:JAVA所定義的版本中不包括
A:JAVA2 EE
B:JAVA2 Card
C:JAVA2 ME
D:JAVA2 HE
E:JAVA2 SE
答案:D

第2題:下列說法正確的是
A:JAVA程式的main方法必須寫在類裡面
B:JAVA程式中可以有多個main方法
C:JAVA程式中類名必須與檔名一樣
D:JAVA程式的main方法中如果只有一條語句,可以不用{}(大括號)括起來
答案:A

第3題:變數命名規範說法正確的是
A:變數由字母、下劃線、數字、$符號隨意組成;
B:變數不能以數字作為開頭;
C:A和a在java中是同一個變數;
D:不同型別的變數,可以起相同的名字;
答案:B

第4題:以下()不是合法的識別符號
A:STRING
B:x3x;
C:void
D:de$f
答案:C

第5題:表示式(11+3*8)/4%3的值是
A:31
B:0
C:1
D:2
答案:D

二、問答題

第1題:小紅馬和小黑馬生的小馬幾條腿
答案:4條腿

第2題:鐵棒打頭疼還是木棒打頭疼
答案:頭最疼

第3題:什麼床不能睡覺
答案:牙床

第4題:為什麼好馬不吃回頭草
答案:後面的草沒了

Process finished with exit code 0
  • 以上呢就是三位考試的試卷;花花豆豆大寶,每個人的試卷內容是一樣的這沒問題,但是三個人的題目以及選項順序都是一樣,就沒有達到我們說希望的亂序要求。
  • 而且以上這樣的程式碼非常難擴充套件,隨著題目的不斷的增加以及亂序功能的補充,都會讓這段程式碼變得越來越混亂。

六、原型模式重構程式碼

接下來使用原型模式來進行程式碼優化,也算是一次很小的重構。

原型模式主要解決的問題就是建立大量重複的類,而我們模擬的場景就需要給不同的使用者都建立相同的試卷,但這些試卷的題目不便於每次都從庫中獲取,甚至有時候需要從遠端的RPC中獲取。這樣都是非常耗時的,而且隨著建立物件的增多將嚴重影響效率。

在原型模式中所需要的非常重要的手段就是克隆,在需要用到克隆的類中都需要實現 implements Cloneable 介面。

1. 工程結構

itstack-demo-design-4-02
└── src
    ├── main
    │   └── java
    │       └── org.itstack.demo.design
    │           ├── util
    │           │   ├── Topic.java
    │           │   └── TopicRandomUtil.java
    │           ├── QuestionBank.java
    │           └── QuestionBankController.java 
    └── test
         └── java
             └── org.itstack.demo.design.test
                 └── ApiTest.java

原型模式模型結構

原型模式模型結構

  • 工程中包括了核心的題庫類QuestionBank,題庫中主要負責將各個的題目進行組裝最終輸出試卷。
  • 針對每一個試卷都會使用克隆的方式進行復制,複製完成後將試卷中題目以及每個題目的答案進行亂序處理。這裡提供了工具包;TopicRandomUtil

2. 程式碼實現

2.1 題目選項亂序操作工具包

/**
 * 亂序Map元素,記錄對應答案key
 * @param option 題目
 * @param key    答案
 * @return Topic 亂序後 {A=c., B=d., C=a., D=b.}
 */
static public Topic random(Map<String, String> option, String key) {
    Set<String> keySet = option.keySet();
    ArrayList<String> keyList = new ArrayList<String>(keySet);
    Collections.shuffle(keyList);
    HashMap<String, String> optionNew = new HashMap<String, String>();
    int idx = 0;
    String keyNew = "";
    for (String next : keySet) {
        String randomKey = keyList.get(idx++);
        if (key.equals(next)) {
            keyNew = randomKey;
        }
        optionNew.put(randomKey, option.get(next));
    }
    return new Topic(optionNew, keyNew);
}
  • 可能你還記得上文裡我們提供了Map儲存題目選項,同時key的屬性存放答案。如果忘記可以往上翻翻
  • 這個這個工具類的操作就是將原有Map中的選型亂序操作,也就是A的選項內容給BB的可能給C,同時記錄正確答案在處理後的位置資訊。

2.2 克隆物件處理類

public class QuestionBank implements Cloneable {

    private String candidate; // 考生
    private String number;    // 考號

    private ArrayList<ChoiceQuestion> choiceQuestionList = new ArrayList<ChoiceQuestion>();
    private ArrayList<AnswerQuestion> answerQuestionList = new ArrayList<AnswerQuestion>();

    public QuestionBank append(ChoiceQuestion choiceQuestion) {
        choiceQuestionList.add(choiceQuestion);
        return this;
    }

    public QuestionBank append(AnswerQuestion answerQuestion) {
        answerQuestionList.add(answerQuestion);
        return this;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        QuestionBank questionBank = (QuestionBank) super.clone();
        questionBank.choiceQuestionList = (ArrayList<ChoiceQuestion>) choiceQuestionList.clone();
        questionBank.answerQuestionList = (ArrayList<AnswerQuestion>) answerQuestionList.clone();

        // 題目亂序
        Collections.shuffle(questionBank.choiceQuestionList);
        Collections.shuffle(questionBank.answerQuestionList);
        // 答案亂序
        ArrayList<ChoiceQuestion> choiceQuestionList = questionBank.choiceQuestionList;
        for (ChoiceQuestion question : choiceQuestionList) {
            Topic random = TopicRandomUtil.random(question.getOption(), question.getKey());
            question.setOption(random.getOption());
            question.setKey(random.getKey());
        }
        return questionBank;
    }

    public void setCandidate(String candidate) {
        this.candidate = candidate;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String toString() {

        StringBuilder detail = new StringBuilder("考生:" + candidate + "\r\n" +
                "考號:" + number + "\r\n" +
                "--------------------------------------------\r\n" +
                "一、選擇題" + "\r\n\n");

        for (int idx = 0; idx < choiceQuestionList.size(); idx++) {
            detail.append("第").append(idx + 1).append("題:").append(choiceQuestionList.get(idx).getName()).append("\r\n");
            Map<String, String> option = choiceQuestionList.get(idx).getOption();
            for (String key : option.keySet()) {
                detail.append(key).append(":").append(option.get(key)).append("\r\n");;
            }
            detail.append("答案:").append(choiceQuestionList.get(idx).getKey()).append("\r\n\n");
        }

        detail.append("二、問答題" + "\r\n\n");

        for (int idx = 0; idx < answerQuestionList.size(); idx++) {
            detail.append("第").append(idx + 1).append("題:").append(answerQuestionList.get(idx).getName()).append("\r\n");
            detail.append("答案:").append(answerQuestionList.get(idx).getKey()).append("\r\n\n");
        }

        return detail.toString();
    }

}

這裡的主要操作內容有三個,分別是;

  • 兩個append(),對各項題目的新增,有點像我們在建造者模式中使用的方式,新增裝修物料。
  • clone() ,這裡的核心操作就是對物件的複製,這裡的複製不只是包括了本身,同時對兩個集合也做了複製。只有這樣的拷貝才能確保在操作克隆物件的時候不影響原物件。
  • 亂序操作,在list集合中有一個方法,Collections.shuffle,可以將原有集合的順序打亂,輸出一個新的順序。在這裡我們使用此方法對題目進行亂序操作。

2.4 初始化試卷資料

public class QuestionBankController {

    private QuestionBank questionBank = new QuestionBank();

    public QuestionBankController() {

        Map<String, String> map01 = new HashMap<String, String>();
        map01.put("A", "JAVA2 EE");
        map01.put("B", "JAVA2 Card");
        map01.put("C", "JAVA2 ME");
        map01.put("D", "JAVA2 HE");
        map01.put("E", "JAVA2 SE");

        Map<String, String> map02 = new HashMap<String, String>();
        map02.put("A", "JAVA程式的main方法必須寫在類裡面");
        map02.put("B", "JAVA程式中可以有多個main方法");
        map02.put("C", "JAVA程式中類名必須與檔名一樣");
        map02.put("D", "JAVA程式的main方法中如果只有一條語句,可以不用{}(大括號)括起來");

        Map<String, String> map03 = new HashMap<String, String>();
        map03.put("A", "變數由字母、下劃線、數字、$符號隨意組成;");
        map03.put("B", "變數不能以數字作為開頭;");
        map03.put("C", "A和a在java中是同一個變數;");
        map03.put("D", "不同型別的變數,可以起相同的名字;");

        Map<String, String> map04 = new HashMap<String, String>();
        map04.put("A", "STRING");
        map04.put("B", "x3x;");
        map04.put("C", "void");
        map04.put("D", "de$f");

        Map<String, String> map05 = new HashMap<String, String>();
        map05.put("A", "31");
        map05.put("B", "0");
        map05.put("C", "1");
        map05.put("D", "2");
        
        questionBank.append(new ChoiceQuestion("JAVA所定義的版本中不包括", map01, "D"))
                .append(new ChoiceQuestion("下列說法正確的是", map02, "A"))
                .append(new ChoiceQuestion("變數命名規範說法正確的是", map03, "B"))
                .append(new ChoiceQuestion("以下()不是合法的識別符號",map04, "C"))
                .append(new ChoiceQuestion("表示式(11+3*8)/4%3的值是", map05, "D"))
                .append(new AnswerQuestion("小紅馬和小黑馬生的小馬幾條腿", "4條腿"))
                .append(new AnswerQuestion("鐵棒打頭疼還是木棒打頭疼", "頭最疼"))
                .append(new AnswerQuestion("什麼床不能睡覺", "牙床"))
                .append(new AnswerQuestion("為什麼好馬不吃回頭草", "後面的草沒了"));
    }

    public String createPaper(String candidate, String number) throws CloneNotSupportedException {
        QuestionBank questionBankClone = (QuestionBank) questionBank.clone();
        questionBankClone.setCandidate(candidate);
        questionBankClone.setNumber(number);
        return questionBankClone.toString();
    }

}
  • 這個類的內容就比較簡單了,主要提供對試卷內容的模式初始化操作(所有考生試卷一樣,題目順序不一致)。
  • 以及對外部提供建立試卷的方法,在建立的過程中使用的是克隆的方式;(QuestionBank) questionBank.clone();,並最終返回試卷資訊。

3. 測試驗證

編寫測試類:

@Test
public void test_QuestionBank() throws CloneNotSupportedException {
    QuestionBankController questionBankController = new QuestionBankController();
    System.out.println(questionBankController.createPaper("花花", "1000001921032"));
    System.out.println(questionBankController.createPaper("豆豆", "1000001921051"));
    System.out.println(questionBankController.createPaper("大寶", "1000001921987"));
}

結果:

考生:花花
考號:1000001921032
--------------------------------------------
一、選擇題

第1題:JAVA所定義的版本中不包括
A:JAVA2 Card
B:JAVA2 HE
C:JAVA2 EE
D:JAVA2 ME
E:JAVA2 SE
答案:B

第2題:表示式(11+3*8)/4%3的值是
A:1
B:0
C:31
D:2
答案:D

第3題:以下()不是合法的識別符號
A:void
B:de$f
C:STRING
D:x3x;
答案:A

第4題:下列說法正確的是
A:JAVA程式的main方法中如果只有一條語句,可以不用{}(大括號)括起來
B:JAVA程式中可以有多個main方法
C:JAVA程式的main方法必須寫在類裡面
D:JAVA程式中類名必須與檔名一樣
答案:C

第5題:變數命名規範說法正確的是
A:變數由字母、下劃線、數字、$符號隨意組成;
B:A和a在java中是同一個變數;
C:不同型別的變數,可以起相同的名字;
D:變數不能以數字作為開頭;
答案:D

二、問答題

第1題:小紅馬和小黑馬生的小馬幾條腿
答案:4條腿

第2題:什麼床不能睡覺
答案:牙床

第3題:鐵棒打頭疼還是木棒打頭疼
答案:頭最疼

第4題:為什麼好馬不吃回頭草
答案:後面的草沒了


考生:豆豆
考號:1000001921051
--------------------------------------------
一、選擇題

第1題:下列說法正確的是
A:JAVA程式中可以有多個main方法
B:JAVA程式的main方法必須寫在類裡面
C:JAVA程式的main方法中如果只有一條語句,可以不用{}(大括號)括起來
D:JAVA程式中類名必須與檔名一樣
答案:B

第2題:表示式(11+3*8)/4%3的值是
A:2
B:1
C:31
D:0
答案:A

第3題:以下()不是合法的識別符號
A:void
B:de$f
C:x3x;
D:STRING
答案:A

第4題:JAVA所定義的版本中不包括
A:JAVA2 Card
B:JAVA2 HE
C:JAVA2 ME
D:JAVA2 EE
E:JAVA2 SE
答案:B

第5題:變數命名規範說法正確的是
A:變數不能以數字作為開頭;
B:A和a在java中是同一個變數;
C:不同型別的變數,可以起相同的名字;
D:變數由字母、下劃線、數字、$符號隨意組成;
答案:A

二、問答題

第1題:什麼床不能睡覺
答案:牙床

第2題:鐵棒打頭疼還是木棒打頭疼
答案:頭最疼

第3題:為什麼好馬不吃回頭草
答案:後面的草沒了

第4題:小紅馬和小黑馬生的小馬幾條腿
答案:4條腿


考生:大寶
考號:1000001921987
--------------------------------------------
一、選擇題

第1題:以下()不是合法的識別符號
A:x3x;
B:de$f
C:void
D:STRING
答案:C

第2題:表示式(11+3*8)/4%3的值是
A:31
B:0
C:2
D:1
答案:C

第3題:變數命名規範說法正確的是
A:不同型別的變數,可以起相同的名字;
B:變數由字母、下劃線、數字、$符號隨意組成;
C:變數不能以數字作為開頭;
D:A和a在java中是同一個變數;
答案:C

第4題:下列說法正確的是
A:JAVA程式的main方法中如果只有一條語句,可以不用{}(大括號)括起來
B:JAVA程式的main方法必須寫在類裡面
C:JAVA程式中類名必須與檔名一樣
D:JAVA程式中可以有多個main方法
答案:B

第5題:JAVA所定義的版本中不包括
A:JAVA2 EE
B:JAVA2 Card
C:JAVA2 HE
D:JAVA2 SE
E:JAVA2 ME
答案:C

二、問答題

第1題:為什麼好馬不吃回頭草
答案:後面的草沒了

第2題:小紅馬和小黑馬生的小馬幾條腿
答案:4條腿

第3題:什麼床不能睡覺
答案:牙床

第4題:鐵棒打頭疼還是木棒打頭疼
答案:頭最疼

Process finished with exit code 0

從以上的輸出結果可以看到,每個人的題目和答案都是差異化的亂序的,如下圖比對結果; - 花花、豆豆、大寶,每個人的試卷都存在著題目和選項的混亂排序

原型模式,亂序題目比對結果

七、總結

  • 以上的實際場景模擬了原型模式在開發中重構的作用,但是原型模式的使用頻率確實不是很高。如果有一些特殊場景需要使用到,也可以按照此設計模式進行優化。
  • 另外原型設計模式的優點包括;便於通過克隆方式建立複雜物件、也可以避免重複做初始化操作、不需要與類中所屬的其他類耦合等。但也有一些缺點如果物件中包括了迴圈引用的克隆,以及類中深度使用物件的克隆,都會使此模式變得異常麻煩。
  • 終究設計模式是一整套的思想,在不同的場景合理的運用可以提升整體的架構的質量。永遠不要想著去硬湊設計模式,否則將會引起過渡設計,以及在承接業務反覆變化的需求時造成浪費的開發和維護成本。
  • 初期是程式碼的優化,中期是設計模式的使用,後期是把控全域性服務的搭建。不斷的加強自己對全域性能力的把控,也加深自己對細節的處理。可上可下才是一個程式設計師最佳處理方式,選取做合適的才是最好的選擇。

八、推薦閱讀

相關文章