From 82d8c7cf0228472b76b47bbdcde4af274d18a6f1 Mon Sep 17 00:00:00 2001 From: Edward Date: Fri, 1 May 2020 19:46:07 +0800 Subject: [PATCH] finish flyweight pattern --- .vscode/settings.json | 75 +++++++++--------- structure/18_flyweight/README.md | 17 +++- structure/18_flyweight/flyweight.go | 98 ++++++++++++++---------- structure/18_flyweight/flyweight_test.go | 57 +++++++++++--- 4 files changed, 158 insertions(+), 89 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index dee0840..fc581af 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,36 +1,41 @@ { - "editor.tabSize": 2, - "files.trimTrailingWhitespace": true, - "files.insertFinalNewline": true, - "extensions.ignoreRecommendations": true, - "workbench.colorCustomizations": { - "activityBar.background": "#759570", - "activityBar.activeBorder": "#cfd3ef", - "activityBar.foreground": "#e7e7e7", - "activityBar.hoverBackground": "#352cea", - "activityBar.inactiveForeground": "#e7e7e799", - "activityBarBadge.background": "#cfd3ef", - "activityBarBadge.foreground": "#15202b", - "titleBar.activeBackground": "#5e7959", - "titleBar.inactiveBackground": "#5e795999", - "titleBar.activeForeground": "#e7e7e7", - "titleBar.inactiveForeground": "#e7e7e799", - "statusBarItem.hoverBackground": "#352cea", - "statusBar.foreground": "#e7e7e7", - "panel.border": "#759570", - "sideBar.border": "#759570", - "editorGroup.border": "#759570" - }, - "go.languageServerFlags": [], - "go.lintOnSave": "file", - "go.vetOnSave": "package", - "go.useCodeSnippetsOnFunctionSuggest": true, - "go.testFlags": ["-v"], - "go.testTimeout": "10s", - "go.formatTool": "goimports", - "cSpell.allowCompoundWords": true, - "editor.codeActionsOnSave": { - "source.organizeImports": true - }, - } - //https://vscode.readthedocs.io/en/latest/getstarted/settings/ + "editor.tabSize": 2, + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "extensions.ignoreRecommendations": true, + "workbench.colorCustomizations": { + "activityBar.background": "#759570", + "activityBar.activeBorder": "#cfd3ef", + "activityBar.foreground": "#e7e7e7", + "activityBar.hoverBackground": "#352cea", + "activityBar.inactiveForeground": "#e7e7e799", + "activityBarBadge.background": "#cfd3ef", + "activityBarBadge.foreground": "#15202b", + "titleBar.activeBackground": "#5e7959", + "titleBar.inactiveBackground": "#5e795999", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveForeground": "#e7e7e799", + "statusBarItem.hoverBackground": "#352cea", + "statusBar.foreground": "#e7e7e7", + "panel.border": "#759570", + "sideBar.border": "#759570", + "editorGroup.border": "#759570" + }, + "go.languageServerFlags": [], + "go.lintOnSave": "file", + "go.vetOnSave": "package", + "go.useCodeSnippetsOnFunctionSuggest": true, + "go.testFlags": [ + "-v" + ], + "go.testTimeout": "10s", + "go.formatTool": "goimports", + "cSpell.allowCompoundWords": true, + "editor.codeActionsOnSave": { + "source.organizeImports": true + }, +"fileheader.Author":"Edward", +"fileheader.tpl": "/* \r\n * @Description: https://github.com/crazybber \r\n * @Author: {author} \r\n * @Date: {createTime} \r\n * @Last Modified by: {lastModifiedBy} \r\n * @Last Modified time: {updateTime} \r\n */\r\n", +"fileheader.LastModifiedBy": "Edward" +} +//https://vscode.readthedocs.io/en/latest/getstarted/settings/ diff --git a/structure/18_flyweight/README.md b/structure/18_flyweight/README.md index 8b636b1..0a393eb 100644 --- a/structure/18_flyweight/README.md +++ b/structure/18_flyweight/README.md @@ -1,4 +1,19 @@ # 享元模式 -享元模式从对象中剥离出不发生改变且多个实例需要的重复数据,独立出一个享元,使多个对象共享,从而节省内存以及减少对象数量。 +享元模式是细粒度控制主体构成的一种方式,英文叫 flyweight,单词分开看反倒更容易理解,fly-weight,看起来是要消减对象的大小/重量(size/weight),对!就是这个意思!抽取对象中的不变部分,使其能够容易的被复用或者共用,进而能够减小内存占用,优化性能,提高运行时效率,比如,会减少对象在内存分配/克隆时的空间大小,减小对象内存占用损耗,缩短响应时间等。 + +注意:实际上完整的享元模式,包含两部分可以被共享的部分,不可以被共享部分,两者组合起来才构成一个完整的整体,设计享元模式,严谨的讲需要针对可共享部分与不可共享两部分设计接口。 + + +单纯享元模式:在单纯享元模式中,所有的具体享元类都是可以共享的,不存在非共享具体享元类。 +复合享元模式:将一些单纯享元对象使用组合模式加以组合,还可以形成复合享元对象,这样的复合享元对象本身不能共享,但是它们可以分解成单纯享元对象,而后者则可以共享。 + +享元的的可共享部分在程序是设计,一般是集中管理的,一般设计为一个存储map的,享元的不可共享部分,一般是即用即创建,完全的专有模式. + +现实生活中的快递送货任务就一个很好的享元模式,快递公司的快递员是共享的,并且可以重复使用,快递公司不可能送一个快递雇一个快递员,否则,这个代价太大巨大了,但是快递员送的包裹是不一样,这是每个人的私有物品,所以不变的快递员+一直在变化的快递,才能构成一次完整的送货任务。 + + + + + diff --git a/structure/18_flyweight/flyweight.go b/structure/18_flyweight/flyweight.go index 6bd1203..0b331b0 100644 --- a/structure/18_flyweight/flyweight.go +++ b/structure/18_flyweight/flyweight.go @@ -1,59 +1,73 @@ package flyweight -import "fmt" +/* + * @Description: github.com/crazybber + * @Author: Edward + * @Date: 2020-05-01 18:10:57 + * @Last Modified by: Edward + * @Last Modified time: 2020-05-01 19:38:21 + */ +import ( + "fmt" +) -type ImageFlyweightFactory struct { - maps map[string]*ImageFlyweight +//IDeliverCompany 公司的能力 +type IDeliverCompany interface { + //雇人 + Hire(name string) + //送货任务 + DeliverTask(name string, packets []string) + + GetDeliver(name string) IDeliver } -var imageFactory *ImageFlyweightFactory +//DeliverCompany 快递公司 +type DeliverCompany struct { + Employees map[string]IDeliver +} -func GetImageFlyweightFactory() *ImageFlyweightFactory { - if imageFactory == nil { - imageFactory = &ImageFlyweightFactory{ - maps: make(map[string]*ImageFlyweight), - } +//IDeliver 快递员能做的事情 +type IDeliver interface { + //送货 + DeliverPackets(packets []string) +} + +//Deliver 快递员工,员工是一个享元对象 +type Deliver struct { + Name string //快递员的名字 + Packets []string //快递员的携带的快递,这一部分是变化的 +} + +//Hire 雇佣新员工,员工是全公司共享 +func (d *DeliverCompany) Hire(name string) { + if d.Employees == nil || len(d.Employees) == 0 { + d.Employees = make(map[string]IDeliver) } - return imageFactory -} - -func (f *ImageFlyweightFactory) Get(filename string) *ImageFlyweight { - image := f.maps[filename] - if image == nil { - image = NewImageFlyweight(filename) - f.maps[filename] = image + if _, ok := d.Employees[name]; ok { + fmt.Println("already hired") + return } + d.Employees[name] = &Deliver{Name: name} - return image + fmt.Println("hired") } -type ImageFlyweight struct { - data string +//GetDeliver return Deliver +func (d *DeliverCompany) GetDeliver(name string) IDeliver { + + return d.Employees[name] + } -func NewImageFlyweight(filename string) *ImageFlyweight { - // Load image file - data := fmt.Sprintf("image data %s", filename) - return &ImageFlyweight{ - data: data, - } +//DeliverTask 派员工送货 +func (d *DeliverCompany) DeliverTask(name string, packets []string) { + + d.Employees[name].DeliverPackets(packets) + } -func (i *ImageFlyweight) Data() string { - return i.data -} +//DeliverPackets 送货了 +func (d *Deliver) DeliverPackets(packets []string) { -type ImageViewer struct { - *ImageFlyweight -} - -func NewImageViewer(filename string) *ImageViewer { - image := GetImageFlyweightFactory().Get(filename) - return &ImageViewer{ - ImageFlyweight: image, - } -} - -func (i *ImageViewer) Display() { - fmt.Printf("Display: %s\n", i.Data()) + fmt.Println(d.Name, ": Delivered:", packets) } diff --git a/structure/18_flyweight/flyweight_test.go b/structure/18_flyweight/flyweight_test.go index 330ccc8..f2bd3ab 100644 --- a/structure/18_flyweight/flyweight_test.go +++ b/structure/18_flyweight/flyweight_test.go @@ -1,19 +1,54 @@ +/* + * @Description: github.com/crazybber + * @Author: Edward + * @Date: 2020-05-01 17:24:28 + * @Last Modified by: Edward + * @Last Modified time: 2020-05-01 19:45:32 + */ package flyweight -import "testing" +import ( + "fmt" + "testing" +) + +func TestDeliveryPackets(t *testing.T) { + + dc := &DeliverCompany{Employees: make(map[string]IDeliver)} + + dc.Hire("bob") + dc.Hire("lily") + dc.Hire("bob") + + deliver1 := dc.GetDeliver("lily") + + deliver2 := dc.GetDeliver("lily") + + dc.DeliverTask("lily", []string{"box1", "box2"}) + + dc.DeliverTask("lily", []string{"box6", "box7", "box8"}) + + deliver2.DeliverPackets([]string{"box9", "box10", "box11"}) + + deliver1.DeliverPackets([]string{"box12"}) -func ExampleFlyweight() { - viewer := NewImageViewer("image1.png") - viewer.Display() - // Output: - // Display: image data image1.png } -func TestFlyweight(t *testing.T) { - viewer1 := NewImageViewer("image1.png") - viewer2 := NewImageViewer("image1.png") +func TestDeliverEmployee(t *testing.T) { + dc := &DeliverCompany{Employees: make(map[string]IDeliver)} - if viewer1.ImageFlyweight != viewer2.ImageFlyweight { - t.Fail() + dc.Hire("bob") + dc.Hire("lily") + + deliver1 := dc.GetDeliver("lily") + + deliver2 := dc.GetDeliver("lily") + + dp1 := fmt.Sprintf("%p", deliver1) + + dp2 := fmt.Sprintf("%p", deliver2) + + if dp1 != dp2 { + t.Error(dp1, "not the same with", dp2) } }