Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

设计模式 - 装饰器 - 用go来实现 #34

Open
Petelin opened this issue Nov 3, 2019 · 0 comments
Open

设计模式 - 装饰器 - 用go来实现 #34

Petelin opened this issue Nov 3, 2019 · 0 comments
Assignees
Labels

Comments

@Petelin
Copy link
Owner

Petelin commented Nov 3, 2019

设计模式 - 装饰器 - 用go来实现

抄一下书

liu-jianhao/Cpp-Design-Patternsgithub.com图标

动机(Motivation)

  • 在某些情况下我们可能会“过度地使用继承来扩展对象的功能”,由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性; 并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。
  • 如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?从而使得任何“功能扩展变化”所导致的影响将为最低?

模式定义

动态(组合)地给一个对象增加一些额外的职责。就增加功能而言,Decorator模式比生成子类(继承)更为灵活(消除重复代码 & 减少子类个数)。 ——《设计模式》GoF

要点总结

  • 通过采用组合而非继承的手法, Decorator模式实现了在运行时动态扩展对象功能的能力,而且可以根据需要扩展多个功能。 避免了使用继承带来的“灵活性差”和“多子类衍生问题”。
  • Decorator类在接口上表现为is-a Component的继承关系,即Decorator类继承了Component类所具有的接口。 但在实现上又表现为has-a Component的组合关系,即Decorator类又使用了另外一个Component类。
  • Decorator模式的目的并非解决“多子类衍生的多继承”问题,Decorator模式应用的要点在于解决“主体类在多个方向上的扩展功能”——是为“装饰”的含义。

Show Code

https://gist.github.com/Petelin/a7bf8ccfd657536d46aaa355c6dc421agist.github.com

// with interface. which we need create a `point` first. and use it later 

package main

import "fmt"

type FibI interface {
	Fib(n int) int
	Wrap(fib FibI) FibI
}

type Fib struct {
	Wrapper FibI
}

func (this *Fib) Fib(n int) int {
	wrapper := this.Wrapper
	if this.Wrapper == nil {
		wrapper = this
	}
	if n == 0 {
		return 0
	}
	if n == 1 {
		return 1
	}
	// call son
	return wrapper.Fib(n-1) + wrapper.Fib(n-2)
}

func (this *Fib) Wrap(fib FibI) FibI {
	this.Wrapper = fib
	return this
}

type CacheFib struct {
	Wrapper FibI
	cache   map[int]int
}

func (this *CacheFib) Wrap(fib FibI) FibI {
	this.Wrapper = fib
	return this
}

func (this *CacheFib) Fib(n int) int {
	if this.cache == nil {
		this.cache = make(map[int]int)
	}
	if ans, ok := this.cache[n]; ok {
		return ans
	}
	ans := this.Wrapper.Fib(n)
	this.cache[n] = ans
	return ans
}

type CounterFib struct {
	Wrapper FibI
	Counter int
}

func (this *CounterFib) Wrap(fib FibI) FibI {
	this.Wrapper = fib
	return this
}

func (this *CounterFib) Fib(n int) int {
	this.Counter++
	return this.Wrapper.Fib(n)
}

func main() {
	fib := new(Fib)
	fmt.Println("result fib", fib.Fib(10))

	cacheFib := new(CacheFib)
	counterFib := new(CounterFib)
	counterCacheFib := cacheFib.Wrap(counterFib.Wrap(fib.Wrap(cacheFib)))
	fmt.Println("result cache:counter:fib", counterCacheFib.Fib(10))
	fmt.Printf("count: %d, cache: %v", counterFib.Counter, cacheFib.cache)
}

function 版本

package main

import "fmt"

type FibI func(int) int

var Fib FibI

func BaseFib() FibI {
	return func(n int) int {
		if n == 0 {
			return 0
		}
		if n == 1 {
			return 1
		}
		return Fib(n-1) + Fib(n-2)
	}
}

func CounterFib(counter *int, f FibI) FibI {
	return func(n int) int {
		*counter++
		return f(n)
	}
}

func CacheFib(cache map[int]int, f FibI) FibI {
	return func(n int) int {
		if ans, ok := cache[n]; ok {
			return ans
		}
		ans := f(n)
		cache[n] = ans
		return ans
	}
}

func main() {
	var counter int
	var cache = make(map[int]int)
	Fib = CacheFib(cache, CounterFib(&counter, BaseFib()))
	fmt.Println(Fib(10), counter, cache)
}

我们原始需求是写一个fib函数.

现在为了统计调用了多少次需要加一个 counter

为了快一点我们需要加一个cache

...

实现这个有这么几种方式

  • 所有的逻辑都写在一个类里, 通过参数或者来实现动态组合, 这种方式无疑最差
  • 通过继承来实现, 那么需要 fib cacheFib counterFib cacheCounterFib ... 因为没办法复用, 问题是导致子类太多, 代码重复
  • 通过组合的方式可以让用户自己组合要使用的东西.

装饰器模式就是这里的最后一种.

go语言和别的不一样的地方在于, 他没有提供面向对象编程, 在这个例子里就是缺少super , this 机制. 在Java里很容易实现的原因是

functionWrapper1 -> 做自己的事情, 然后通过super 向上传递 -> functionWrapper2 -> 最后类通过this call自己

this这里会变成functionWrape会动态的指向当前对象, 也就是functionWraper1. 而golang没有这个机制, 所以就需要我们自己把整个流程wrap起来.

fib := new(Fib)	
cacheFib := new(CacheFib)
counterFib := new(CounterFib)
counterCacheFib := cacheFib.Wrap(counterFib.Wrap(fib.Wrap(cacheFib)))

// 通过pipe line把多个装饰器组合在了一起
@Petelin Petelin added the fromweb label Nov 3, 2019
@Petelin Petelin self-assigned this Nov 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant