<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
如果一個類的有非常多的屬性,層級還很深。每次構造起來,不管是直接構造還是用建造者模式,都要對太多屬性進行復制,那麼有沒有一種好的方式讓我們建立太的時候使用體驗更好一點呢? 今天的文章裡就給大家介紹一種設計模式,來解決這個問題。
這篇內容要說的是創造型設計模式裡的原型模式,如果寫過點 JS 程式碼的話,大家可能聽說過原型鏈這麼個東西,原型模式在 JavaScript 實現裡確實廣泛應用,它那個物件導向跟 Java、C++ 這些語言的物件導向的實現方式還不太一樣,繼承其實是通過原型克隆出來,在拷貝出來的原型的基礎上再繼續新增或者修改來實現的。
通過複製、拷貝或者叫克隆已有物件的方式來建立新物件的設計模式叫做原型模式,被拷貝的物件也被稱作原型物件。
原型物件按照慣例,會暴露出一個 Clone 方法,給外部呼叫者一個機會來從自己這裡“零成本”的克隆出一個新物件。
這裡的“零成本”說的是,呼叫者啥都不用幹,乾等著,原型物件在 Clone 方法裡自己克隆出自己,給到呼叫者,所以按照這個約定所有原型物件都要實現一個 Clone 方法。
type Prototype interface { Clone() SpecificType }
這裡我們用UML類圖描述一下原型模式中各角色擁有的行為以及它們之間的關係
至於原型物件克隆自己的時候用的是深拷貝還是淺拷貝?可以先理解成是都用深拷貝,等完全掌握這種思想後,可以再根據實際情況,比如為了節省空間、以及減少編寫克隆方法的複雜度時可以兩者綜合使用。
原型模式更多的是闡述一種程式設計模式,並沒有限制我們用什麼方式實現。比如下面這個深拷貝和淺拷貝結合使用的例子。
// 範例程式碼來自:https://lailin.xyz/post/prototype.html package prototype import ( "encoding/json" "time" ) // Keyword 搜尋鍵碼 type Keyword struct { word string visit int UpdatedAt *time.Time } // Clone 這裡使用序列化與反序列化的方式深拷貝 func (k *Keyword) Clone() *Keyword { var newKeyword Keyword b, _ := json.Marshal(k) json.Unmarshal(b, &newKeyword) return &newKeyword } // Keywords 關鍵字 map type Keywords map[string]*Keyword // Clone 複製一個新的 keywords // updatedWords: 需要更新的關鍵詞列表,由於從資料庫中獲取資料常常是陣列的方式 func (words Keywords) Clone(updatedWords []*Keyword) Keywords { newKeywords := Keywords{} for k, v := range words { // 這裡是淺拷貝,直接拷貝了地址 newKeywords[k] = v } // 替換掉需要更新的欄位,這裡用的是深拷貝 for _, word := range updatedWords { newKeywords[word.word] = word.Clone() } return newKeywords }
使用原型模式的目的主要是為了節省建立物件所花費的時間和資源消耗,提升效能。
還有一點就是,比如全域性設定物件這種也可以當成原型物件,如果不想讓程式在執行時修改初始化好的原型物件,導致影響其他執行緒的程式執行的時候,也可以用原型模式快速拷貝出一份,再在副本上做執行時自定義修改。
當物件的建立成本比較大,並且同一個類的不同物件間差別不大時(大部分屬性值相同),如果物件的屬性值需要經過複雜的計算、排序,或者需要從網路、DB等這些慢IO中獲取、亦或者或者屬性值擁有很深的層級,這時就是原型模式發揮作用的地方了。
因為物件在記憶體中複製自己遠比每次建立物件時重走一遍上面說的操作要來高效的多。
下面再來一個例子,讓我們更好的理解原型模式的優點。
下面是一個類似 DOM 樹物件的例子,因為 DOM 物件往往層級會很深,那麼要建立類似的DOM樹的時候能讓我們更好的理解原型模式的優勢。
這個範例程式碼來自:blog.ralch.com/articles/de…
package dom import ( "bytes" "fmt" ) // Node a document object model node type Node interface { // Strings returns nodes text representation String() string // Parent returns the node parent Parent() Node // SetParent sets the node parent SetParent(node Node) // Children returns the node children nodes Children() []Node // AddChild adds a child node AddChild(child Node) // Clone clones a node Clone() Node } // Element represents an element in document object model type Element struct { text string parent Node children []Node } // NewElement makes a new element func NewElement(text string) *Element { return &Element{ text: text, parent: nil, children: make([]Node, 0), } } // Parent returns the element parent func (e *Element) Parent() Node { return e.parent } // SetParent sets the element parent func (e *Element) SetParent(node Node) { e.parent = node } // Children returns the element children elements func (e *Element) Children() []Node { return e.children } // AddChild adds a child element func (e *Element) AddChild(child Node) { copy := child.Clone() copy.SetParent(e) e.children = append(e.children, copy) } // Clone makes a copy of particular element. Note that the element becomes a // root of new orphan tree func (e *Element) Clone() Node { copy := &Element{ text: e.text, parent: nil, children: make([]Node, 0), } for _, child := range e.children { copy.AddChild(child) } return copy } // String returns string representation of element func (e *Element) String() string { buffer := bytes.NewBufferString(e.text) for _, c := range e.Children() { text := c.String() fmt.Fprintf(buffer, "n %s", text) } return buffer.String() }
上面的DOM物件-- Node、Element 這些都支援原型模式要求的 Clone 方法,那麼有了這個原型克隆的能力後,假如我們想根據建立好的 DOM 樹上克隆出一個子分支作為一顆獨立的 DOM 樹物件的時候,就可以像下面這樣簡單地執行 Node.Clone() 把節點和其下面的子節點全部拷貝出去。比我們使用構造方法再重新構造樹形結構要方便許多。
下面的例子是用DOM樹結構建立一下公司裡的職級關係,然後還可以從任意層級克隆出一顆新的樹。
func main() { // 職級節點--總監 directorNode := dom.NewElement("Director of Engineering") // 職級節點--研發經理 engManagerNode := dom.NewElement("Engineering Manager") engManagerNode.AddChild(dom.NewElement("Lead Software Engineer")) // 研發經理是總監的下級 directorNode.AddChild(engManagerNode) directorNode.AddChild(engManagerNode) // 辦公室經理也是總監的下級 officeManagerNode := dom.NewElement("Office Manager") directorNode.AddChild(officeManagerNode) fmt.Println("") fmt.Println("# Company Hierarchy") fmt.Print(directorNode) fmt.Println("") // 從研發經理節點克隆出一顆新的樹 fmt.Println("# Team Hiearachy") fmt.Print(engManagerNode.Clone()) }
關於原型模式的總結,我們先來說一下原型模式的優缺點。
原型模式的優點
原型模式的缺點
在專案中使用原型模式時,可能需要在專案初始化時就把提供克隆能力的原型物件建立好,在多執行緒環境下,每個執行緒處理任務的時候,用到了相關物件,可以去原型物件那裡拷貝。不過適合當作原型物件的資料並不多,所以原型模式在開發中的使用頻率並不高,如果有機會做專案架構,可以適當考慮,確實需要再在專案中引入這種設計模式。
以上就是Go設計模式原型模式考查點及使用詳解的詳細內容,更多關於Go原型模式考查點的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45