?
? ? 原型模式是一種設計模式,屬于創(chuàng)建型模式的一種,它用于創(chuàng)建重復的對象,同時又能保持性能。在原型模式中,通過復制現(xiàn)有對象的原型來創(chuàng)建新對象,而不是通過實例化類來創(chuàng)建對象。這樣做可以避免耗費過多的資源開銷,特別是在對象的創(chuàng)建過程比較復雜或耗時的情況下。
? ? 在原型模式中,原型對象實現(xiàn)一個克隆方法(Clone)用于復制自身,當需要創(chuàng)建新對象時,就可以通過克隆原型對象來得到一個新的對象副本。原型模式通常包括淺拷貝和深拷貝兩種形式,淺拷貝只復制對象本身,而深拷貝則會連同對象引用的其他對象一起復制,因此能夠得到完全獨立的新對象。
? ? 原型模式可以在需要大量創(chuàng)建相似對象的場景中發(fā)揮作用,它能夠提高對象的創(chuàng)建效率,同時也能夠減少對類的直接依賴,使系統(tǒng)結構更靈活。
??關鍵角色有兩個:
- 原型(Prototype):定義用于復制現(xiàn)有對象的接口,通常包含一個克隆方法,用于返回一個克隆對象。
- 具體原型(Concrete Prototype):實現(xiàn)原型接口,實現(xiàn)克隆方法來復制自身。
??優(yōu)點:
- 減少對象創(chuàng)建時間:原型模式通過復制現(xiàn)有對象來創(chuàng)建新對象,避免了昂貴的對象創(chuàng)建過程,特別是在需要頻繁創(chuàng)建相似對象時,可以大大減少對象創(chuàng)建的時間和開銷。
- 簡化對象創(chuàng)建過程:原型模式封裝了對象的創(chuàng)建過程,客戶端無需關心具體的創(chuàng)建細節(jié),使得對象創(chuàng)建變得更加簡單,提高了系統(tǒng)的可維護性和擴展性。
- 動態(tài)增加或減少原型:原型模式允許動態(tài)地添加或刪除原型,使得系統(tǒng)更加靈活,能夠根據(jù)需求動態(tài)創(chuàng)建新的原型對象。
??缺點:
- 需要理解原型對象:在使用原型模式時,需要確保原型對象的克隆方法能夠正確地復制對象的狀態(tài),有時需要深度復制而不是淺復制,這需要額外的處理和理解。
- 難以保持一致性:原型模式可能會造成系統(tǒng)中的一些對象克隆出來之后狀態(tài)難以保持一致,特別是涉及到對象之間的引用關系時,需要特別小心處理。
? 本質體現(xiàn)了兩個關鍵點:
- 原型:具備復制能力的對象,它是創(chuàng)建新對象的模板。原型可以是一個接口、抽象類或具體類,關鍵是它需要提供克隆自身的方法。
- 克?。焊鶕?jù)原型對象復制出來的新對象??寺∵^程可以是淺復制(只復制對象本身)或者深復制(復制對象和其引用的對象)。
? ??原型模式的本質是通過復制現(xiàn)有對象來創(chuàng)建新對象,從而封裝了對象的創(chuàng)建過程,提供了一種靈活、高效的對象創(chuàng)建方式。
目錄
一、案例
1.1?不用模式來實現(xiàn)
1.2 有何問題
1.3 原型模式重構代碼
1.4?完美實現(xiàn)?
三、模式講解
3.1?功能
3.2?原型模式的結構和說明
?3.3?幾種工廠模式總結
一、案例
場景:前段時間有一個考試,發(fā)現(xiàn)在一個教室里考試的試卷有ABCDEFG卷,座位上前后左右人的卷子跟我的都不一樣??纪旰舐犂蠋熣f,ABCDEFG卷的題目是一樣的,意思就是題目是一樣的,題目的順序是打亂的甚至同一個題的選項的順序也是打亂的。
? ? 下面我們從無到有來感受原型模式的設計藝術。
1.1?不用模式來實現(xiàn)
? 選擇題類:
@Data
public class ChoiceQuestion {
/**
* 選擇題題目
*/
private String title;
/**
* 選項
*/
private Map<String, String> options;
/**
* 答案
*/
private String key;
public ChoiceQuestion(){}
public ChoiceQuestion(String title, Map<String, String> options, String key) {
this.title = title;
this.options = options;
this.key = key;
}
}
? 填空題類:
@Data
public class Completion {
/**
* 填空題題目
*/
private String title;
/**
* 答案
*/
private List<String> key;
public Completion() {}
public Completion(String title, List<String> key) {
this.title = title;
this.key = key;
}
}
? 試卷類:
public class TestPaper {
/**
* 創(chuàng)建試卷
* @param name 考生姓名
* @param code 考生編號
* @return
*/
public String createPaper(String name, String code) {
// 這里舉例有兩道選擇題和兩道填空題
List<ChoiceQuestion> choiceQuestionList = new ArrayList<>(2);
List<Completion> completionList = new ArrayList<>(2);
// 初始化選擇題
Map<String, String> choiceMap1 = new HashMap<>();
choiceMap1.put("A", "1");
choiceMap1.put("B", "2");
choiceMap1.put("C", "3");
choiceMap1.put("D", "4");
Map<String, String> choiceMap2 = new HashMap<>();
choiceMap2.put("A", "1");
choiceMap2.put("B", "2");
choiceMap2.put("C", "3");
choiceMap2.put("D", "4");
choiceQuestionList.add(new ChoiceQuestion("1 + 1 = ", choiceMap1, "B"));
choiceQuestionList.add(new ChoiceQuestion("3 + 1 = ", choiceMap2, "D"));
// 初始化填空題
List<String> comList1 = new ArrayList<>();
comList1.add("富強");
comList1.add("民主");
List<String> comList2 = new ArrayList<>();
comList2.add("51");
comList2.add("21");
completionList.add(new Completion("任意列舉出兩個社會主義核心價值觀", comList1));
completionList.add(new Completion("2008年中國獲得金牌數(shù)___枚,銀牌___枚", comList2));
String newLine = "\n";
// 組裝試卷
StringBuffer strBuff = new StringBuffer();
strBuff.append("考生:").append(name).append(newLine)
.append("考號:").append(code).append(newLine)
.append("一、選擇題").append(newLine);
for (ChoiceQuestion choiceQuestion : choiceQuestionList) {
strBuff.append(choiceQuestion.getTitle()).append(newLine);
for (Map.Entry<String, String> entry : choiceQuestion.getOptions().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
strBuff.append(key).append(":").append(value).append(newLine);
}
strBuff.append("答案:").append(choiceQuestion.getKey()).append(newLine);
}
for (Completion completion : completionList) {
strBuff.append(completion.getTitle()).append(newLine);
strBuff.append("答案:");
for (String anster : completion.getKey()) {
strBuff.append(" ").append(anster);
}
}
return strBuff.toString();
}
}
? 測試客戶端:
public class Client {
public static void main(String[] args) {
TestPaper testPaper = new TestPaper();
System.out.println(testPaper.createPaper("張三", "ACCDF0001"));
System.out.println(testPaper.createPaper("李四", "ACCDF0002"));
System.out.println(testPaper.createPaper("王五", "ACCDF0003"));
}
}
? 運行結果如下:
考生:張三
考號:ACCDF0001
一、選擇題
1 + 1 =
A:1
B:2
C:3
D:4
答案:B
3 + 1 =
A:1
B:2
C:3
D:4
答案:D
任意列舉出兩個社會主義核心價值觀
答案: 富強 民主
2008年中國獲得金牌數(shù)___枚,銀牌___枚
答案: 51 21
考生:李四
考號:ACCDF0002
一、選擇題
1 + 1 =
A:1
B:2
C:3
D:4
答案:B
3 + 1 =
A:1
B:2
C:3
D:4
答案:D
任意列舉出兩個社會主義核心價值觀
答案: 富強 民主
2008年中國獲得金牌數(shù)___枚,銀牌___枚
答案: 51 21
考生:王五
考號:ACCDF0003
一、選擇題
1 + 1 =
A:1
B:2
C:3
D:4
答案:B
3 + 1 =
A:1
B:2
C:3
D:4
答案:D
任意列舉出兩個社會主義核心價值觀
答案: 富強 民主
2008年中國獲得金牌數(shù)___枚,銀牌___枚
答案: 51 21
? 一坨坨代碼很快就實現(xiàn)了,看上去很簡單。?
????????
1.2 有何問題
- 以上呢就是三位考試的試卷; 線三 、李四和王五 ,每個?人的試卷內(nèi)容是?一樣的這沒問題,但是三個人的題?以及選項順序和答案都是一樣的,但是沒有達到我們的場景中“ABCDEFG卷題目順序和選項順序都不一樣”的目的。
- 以上代碼?非常難擴展,隨著題?目的不不斷的增加以及亂序功能的補充,都會讓這段代碼變 得越來越混亂。?
?????????
1.3 原型模式重構代碼
原型模式解決的痛點:創(chuàng)建?量重復的類,?我們的場景就需要給不不同的用戶都創(chuàng)建相同的試卷,但這些試卷的題目不便于每次都從庫中獲取,甚至有時候需要從遠程的RPC中獲取。這樣都是?常耗時的,?且隨著創(chuàng)建對象的增多將嚴重影響效率。
????????
?用原型模式注意要點:
原型對象的接口:在原型模式中,原型對象需要實現(xiàn)一個克隆方法(Clone),該方法用于復制自身并返回一個新的對象副本。這需要確保原型對象具有適當?shù)慕涌诤涂寺∧芰Α?/p>
淺拷貝和深拷貝:在實現(xiàn)克隆方法時,需要考慮對象中是否存在引用類型的成員變量。淺拷貝只會復制對象本身,而不會復制對象包含的引用類型成員變量,這意味著原型對象和克隆對象會引用同一個引用類型的成員變量。深拷貝則會復制對象本身以及其引用類型的成員變量,使得原型對象和克隆對象完全獨立。選擇淺拷貝還是深拷貝取決于實際需求和設計考慮。
創(chuàng)建新對象的方式:在使用原型模式創(chuàng)建新對象時,可以通過調用原型對象的克隆方法,或者使用原型管理器(Prototype Manager)來獲取新對象。原型管理器可以維護一組原型對象,并根據(jù)需要返回相應的克隆對象。
修改克隆對象:當獲得克隆對象后,可以根據(jù)需要對克隆對象做進一步的修改。這使得每個克隆對象都可以根據(jù)需求進行個性化的定制。
?????????
? 代碼改造
? 添加選項答案類:
@Data
public class Option {
/**
* 選項
*/
private Map<String, String> options;
/**
* 答案
*/
private String key;
public Option(Map<String, String> options, String key) {
this.options = options;
this.key = key;
}
}
? 添加選項亂序工具類:
public class RandomUtil {
/**
* 亂序Map元素,記錄對應答案key
* @param option 題?選項
* @param key 答案
* @return Option 亂序后 {A=c., B=d., C=a., D=b.} */
static public Option random(Map<String, String> option, String key) {
Set<String> keySet = option.keySet();
ArrayList<String> keyList = new ArrayList<>(keySet);
Collections.shuffle(keyList);
HashMap<String, String> optionNew = new HashMap<>();
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 Option(optionNew, keyNew);
}
}
? 添加題庫類:
@Data
public class QuestionBank implements Cloneable {
/**
* 試卷序號
*/
private String seq;
/**
* 考生姓名
*/
private String name;
/**
* 考生考號
*/
private String code;
private ArrayList<ChoiceQuestion> choiceQuestionList = new ArrayList<>(2);
private ArrayList<Completion> completionList = new ArrayList<>(2);
/**
* 實現(xiàn)中題目可能來自數(shù)據(jù)庫中,這里示例便直接寫死
*/
public QuestionBank() {
// 初始化選擇題
Map<String, String> choiceMap1 = new HashMap<>();
choiceMap1.put("A", "1");
choiceMap1.put("B", "2");
choiceMap1.put("C", "3");
choiceMap1.put("D", "4");
Map<String, String> choiceMap2 = new HashMap<>();
choiceMap2.put("A", "1");
choiceMap2.put("B", "2");
choiceMap2.put("C", "3");
choiceMap2.put("D", "4");
this.choiceQuestionList.add(new ChoiceQuestion("1 + 1 = ", choiceMap1, "B"));
this.choiceQuestionList.add(new ChoiceQuestion("3 + 1 = ", choiceMap2, "D"));
// 初始化填空題
List<String> comList1 = new ArrayList<>();
comList1.add("富強");
comList1.add("民主");
List<String> comList2 = new ArrayList<>();
comList2.add("51");
comList2.add("21");
this.completionList.add(new Completion("任意列舉出兩個社會主義核心價值觀", comList1));
this.completionList.add(new Completion("2008年中國獲得金牌數(shù)___枚,銀牌___枚", comList2));
}
@Override
public QuestionBank clone() {
try {
QuestionBank questionBank = (QuestionBank) super.clone();
questionBank.setChoiceQuestionList((ArrayList<ChoiceQuestion>) this.getChoiceQuestionList().clone());
questionBank.setCompletionList((ArrayList<Completion>) this.getCompletionList().clone());
// 打亂選擇題
Collections.shuffle(questionBank.getChoiceQuestionList());
// 選擇題的選項打亂
for (ChoiceQuestion choiceQuestion : questionBank.getChoiceQuestionList()) {
Option option = RandomUtil.random(choiceQuestion.getOptions(), choiceQuestion.getKey());
choiceQuestion.setOptions(option.getOptions());
choiceQuestion.setKey(option.getKey());
}
// 打亂填空題
Collections.shuffle(questionBank.getCompletionList());
return questionBank;
} catch (Exception e) {
throw new AssertionError();
}
}
@Override
public String toString() {
String newLine = "\r\n";
StringBuffer strBuff = new StringBuffer();
strBuff.append("考卷:").append(getSeq()).append(newLine)
.append("考生:").append(getName()).append(newLine)
.append("考號:").append(getCode()).append(newLine);
strBuff.append("一、選擇題").append(newLine);
for (ChoiceQuestion choiceQuestion : getChoiceQuestionList()) {
strBuff.append(choiceQuestion.getTitle()).append(newLine);
for (Map.Entry<String, String> entry : choiceQuestion.getOptions().entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
strBuff.append(key).append(":").append(value).append(newLine);
}
strBuff.append("答案:").append(choiceQuestion.getKey()).append(newLine);
}
strBuff.append("一、填空題").append(newLine);
for (Completion completion : getCompletionList()) {
strBuff.append(completion.getTitle()).append(newLine);
strBuff.append("答案:");
for (String anster : completion.getKey()) {
strBuff.append(" ").append(anster);
}
}
return strBuff.toString();
}
}
? 試卷類改造:
@Data
public class TestPaper {
/**
* 題庫
*/
QuestionBank questionBank = new QuestionBank();
/**
* 創(chuàng)建試卷
* @param req 試卷序號
* @param name 考生姓名
* @param code 考生編號
* @return
*/
public String createPaper(String req, String name, String code) {
QuestionBank questionBankClone = this.getQuestionBank().clone();
questionBankClone.setSeq(req);
questionBankClone.setName(name);
questionBankClone.setCode(code);
return questionBankClone.toString();
}
}
? 改造測試客戶端:
public class Client {
public static void main(String[] args) {
// 創(chuàng)建試卷工具類并初始化題庫
TestPaper testPaper = new TestPaper();
// 創(chuàng)建張三
System.out.println(testPaper.createPaper("A", "張三", "A0001"));
System.out.println(testPaper.createPaper("B", "李四", "A0002"));
System.out.println(testPaper.createPaper("C", "王五", "A0003"));
}
}
? 運行結果如下:
? (考卷A)
考卷:A
考生:張三
考號:A0001
一、選擇題
1 + 1 =
A:3
B:1
C:2
D:4
答案:C
3 + 1 =
A:3
B:1
C:4
D:2
答案:C
一、填空題
2008年中國獲得金牌數(shù)___枚,銀牌___枚
答案: 51 21
任意列舉出兩個社會主義核心價值觀
答案: 富強 民主
? (考卷B)
考卷:B
考生:李四
考號:A0002
一、選擇題
3 + 1 =
A:4
B:1
C:2
D:3
答案:A
1 + 1 =
A:2
B:4
C:3
D:1
答案:A
一、填空題
2008年中國獲得金牌數(shù)___枚,銀牌___枚
答案: 51 21
任意列舉出兩個社會主義核心價值觀
答案: 富強 民主
? (考卷C)
考卷:C
考生:王五
考號:A0003
一、選擇題
1 + 1 =
A:1
B:4
C:3
D:2
答案:D
3 + 1 =
A:4
B:3
C:1
D:2
答案:A
一、填空題
任意列舉出兩個社會主義核心價值觀
答案: 富強 民主
2008年中國獲得金牌數(shù)___枚,銀牌___枚
答案: 51 21
?????????
1.4?完美實現(xiàn)?
? ? 從上面結果看出,A卷、B卷和C卷的題目和選項都被打亂了。再添加DEF等卷,就完美實現(xiàn)了案例場景下我座位中前后左右人的卷子與我不同的情況。
????????
三、模式講解
本質:通過復制現(xiàn)有對象來創(chuàng)建新對象,從而封裝了對象的創(chuàng)建過程,提供了一種靈活、高效的對象創(chuàng)建方式。
3.1?功能
? ? 原型模式?的功能?實際上包含兩個方面:
- 一個是通過克隆來創(chuàng)建新的對象實例;
- 另一個是為克隆出來的對象實例復制原型實例屬性的值。
3.2?原型模式的結構和說明
- Prototype:聲明一個克隆自身的接口,用來約束想要克隆自己的類,要求它們都要實現(xiàn)這里定義的克隆方法。
- ConcretePrototype:實現(xiàn)Prototype接口的類,這些類真正實現(xiàn)了克隆自身的功能。
- Client:使用原型的客戶端,首先獲取到原型對象,然后通過原型實例克隆自身來創(chuàng)建新的對象實例。
????????
?3.3?幾種工廠模式總結
? ??工廠方法、抽象工廠、建造者模式和原型模式是常見的軟件設計模式,它們各自解決了不同類型的設計問題。
工廠方法模式:
工廠方法模式是一種創(chuàng)建型設計模式,它提供了一種將對象的創(chuàng)建和使用分離的方式。核心在于定義一個創(chuàng)建對象的接口,但將具體的創(chuàng)建過程延遲到子類中進行實現(xiàn)。抽象工廠模式:
抽象工廠模式也是一種創(chuàng)建型設計模式,它提供了一種創(chuàng)建一系列相關或相互依賴對象的接口,而無需指定具體的類。核心在于提供一個能夠創(chuàng)建一系列產(chǎn)品的工廠接口,而具體的工廠類負責實現(xiàn)這個接口并創(chuàng)建具體的產(chǎn)品。建造者模式:
建造者模式是一種創(chuàng)建型設計模式,它將一個復雜對象的構建過程與其表示相分離,并允許使用相同的構建過程來創(chuàng)建不同的表示。核心在于將對象的構建過程分解成多個步驟,并提供一個統(tǒng)一的接口來組合這些步驟,從而創(chuàng)建復雜對象。原型模式:
原型模式是一種創(chuàng)建型設計模式,它允許通過復制現(xiàn)有對象來創(chuàng)建新對象,而不是通過實現(xiàn)一個特定的工廠方法來創(chuàng)建對象。核心在于通過克隆已有的對象來創(chuàng)建新的對象,從而避免了通過構造函數(shù)創(chuàng)建對象時的復雜性。文章來源:http://www.zghlxwxcb.cn/news/detail-817355.html
??區(qū)別:文章來源地址http://www.zghlxwxcb.cn/news/detail-817355.html
- 工廠方法模式和抽象工廠模式都是用于對象創(chuàng)建,但工廠方法模式關注于單個對象的創(chuàng)建,而抽象工廠模式關注于一系列相關對象的創(chuàng)建。
- 建造者模式主要用于創(chuàng)建復雜對象,并且可以根據(jù)需要定制對象的不同表示,而工廠方法和抽象工廠模式更多地關注于對象的創(chuàng)建過程和對象的組合。
- 原型模式主要用于通過復制來創(chuàng)建新對象,避免了直接通過構造函數(shù)創(chuàng)建對象時的復雜性,與工廠方法、抽象工廠和建造者模式的區(qū)別在于其實現(xiàn)方式和目的不同。
到了這里,關于探索設計模式的魅力:一次設計,多次利用,深入理解原型模式的設計藝術的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!