├── singleton └── singleton.go ├── adapter └── adapter.go ├── bridge └── bridge.go ├── flyweight └── flyweight.go ├── strategy └── strategy.go ├── state └── state.go ├── decorator └── decorator.go ├── prototype └── prototype.go ├── private-class-data └── private-class-data.go ├── facade └── facade.go ├── iterator └── iterator.go ├── null-object └── null-object.go ├── factory └── factory.go ├── mediator └── mediator.go ├── proxy └── proxy.go ├── object-pool └── object-pool.go ├── memento └── memento.go ├── template └── template.go ├── command └── command.go ├── interpreter └── interpreter.go ├── composite └── composite.go ├── observer └── observer.go ├── abstract-factory └── abstract-factory.go ├── visitor └── visitor.go ├── chain-of-responsibility └── chain-of-responsibility.go └── README.md /singleton/singleton.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type singleton struct{} 6 | 7 | var instance *singleton 8 | 9 | func GetInstance() *singleton { 10 | if instance == nil { 11 | instance = &singleton{} 12 | } 13 | return instance 14 | } 15 | 16 | func main() { 17 | instance1 := GetInstance() 18 | instance2 := GetInstance() 19 | 20 | fmt.Println(instance1 == instance2) 21 | } 22 | -------------------------------------------------------------------------------- /adapter/adapter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type WhatsappLegacy struct{} 6 | 7 | func (self *WhatsappLegacy) SendMessageOldWay() string { 8 | return "send message whatsapp old way" 9 | } 10 | 11 | type MessageAdapter interface { 12 | SendMessage() string 13 | } 14 | 15 | type Adapter struct { 16 | whatsappLegacy *WhatsappLegacy 17 | } 18 | 19 | func WhatsappAdapter() *Adapter { 20 | return &Adapter{&WhatsappLegacy{}} 21 | } 22 | 23 | func (self *Adapter) SendMessage() string { 24 | fmt.Println("calling from adapter") 25 | return self.whatsappLegacy.SendMessageOldWay() 26 | } 27 | 28 | func main() { 29 | whatsapp := WhatsappAdapter() 30 | fmt.Println(whatsapp.SendMessage()) 31 | } 32 | -------------------------------------------------------------------------------- /bridge/bridge.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type DrawAPI interface { 6 | DrawCircle(int, int, int) 7 | } 8 | 9 | type RedCircle struct{} 10 | 11 | func (self *RedCircle) DrawCircle(radius int, x int, y int) { 12 | fmt.Printf("drawing red circle, radius: %v, x: %v, y: %v\n", radius, x, y) 13 | } 14 | 15 | type GreenCircle struct{} 16 | 17 | func (self *GreenCircle) DrawCircle(radius int, x int, y int) { 18 | fmt.Printf("drawing green circle, radius: %v, x: %v, y: %v\n", radius, x, y) 19 | } 20 | 21 | type Shape interface { 22 | Draw() 23 | } 24 | 25 | type Circle struct { 26 | x, y, radius int 27 | draw DrawAPI 28 | } 29 | 30 | func (self *Circle) Draw() { 31 | self.draw.DrawCircle(self.radius, self.x, self.y) 32 | } 33 | 34 | func main() { 35 | redCircle := &Circle{100, 100, 10, &RedCircle{}} 36 | greenCircle := &Circle{100, 100, 10, &GreenCircle{}} 37 | 38 | redCircle.Draw() 39 | greenCircle.Draw() 40 | } 41 | -------------------------------------------------------------------------------- /flyweight/flyweight.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type Gazillion struct { 9 | row int 10 | } 11 | 12 | func NewGazillion(row int) *Gazillion { 13 | fmt.Println("col to row: " + strconv.Itoa(row)) 14 | return &Gazillion{row} 15 | } 16 | 17 | func (self *Gazillion) print(col int) { 18 | fmt.Print(" " + strconv.Itoa(self.row) + strconv.Itoa(col)) 19 | } 20 | 21 | type Factory struct { 22 | pool map[int]*Gazillion 23 | } 24 | 25 | func NewFactory() *Factory { 26 | return &Factory{make(map[int]*Gazillion)} 27 | } 28 | 29 | func (self *Factory) GetGazillion(row int) *Gazillion { 30 | if self.pool[row] == nil { 31 | self.pool[row] = NewGazillion(row) 32 | } 33 | return self.pool[row] 34 | } 35 | 36 | func main() { 37 | ROWS := 6 38 | COLS := 10 39 | 40 | factory := NewFactory() 41 | for i := 0; i < ROWS; i++ { 42 | for j := 0; j < COLS; j++ { 43 | factory.GetGazillion(i).print(j) 44 | } 45 | fmt.Println() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /strategy/strategy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Strategy interface { 6 | Solve(num1 int, num2 int) int 7 | } 8 | 9 | type Add struct{} 10 | 11 | func (self *Add) Solve(num1 int, num2 int) int { 12 | return num1 + num2 13 | } 14 | 15 | type Subtract struct{} 16 | 17 | func (self *Subtract) Solve(num1 int, num2 int) int { 18 | return num1 - num2 19 | } 20 | 21 | type Multiply struct{} 22 | 23 | func (self *Multiply) Solve(num1 int, num2 int) int { 24 | return num1 * num2 25 | } 26 | 27 | type Context struct { 28 | strategy Strategy 29 | } 30 | 31 | func (self *Context) ExecuteStrategy(num1 int, num2 int) int { 32 | return self.strategy.Solve(num1, num2) 33 | } 34 | 35 | func main() { 36 | context := &Context{&Add{}} 37 | fmt.Println(context.ExecuteStrategy(100, 4)) 38 | 39 | context = &Context{&Subtract{}} 40 | fmt.Println(context.ExecuteStrategy(100, 4)) 41 | 42 | context = &Context{&Multiply{}} 43 | fmt.Println(context.ExecuteStrategy(100, 4)) 44 | } 45 | -------------------------------------------------------------------------------- /state/state.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type State interface { 6 | Action(*Context) 7 | Move() 8 | } 9 | 10 | type Context struct { 11 | state State 12 | } 13 | 14 | type RunningState struct{} 15 | 16 | func (self *RunningState) Action(context *Context) { 17 | fmt.Println("Robot is in running state") 18 | context.state = self 19 | } 20 | 21 | func (self *RunningState) Move() { 22 | fmt.Println("Robot runs 10 m/s") 23 | } 24 | 25 | type WalkingState struct{} 26 | 27 | func (self *WalkingState) Action(context *Context) { 28 | fmt.Println("Robot is in walking state") 29 | context.state = self 30 | } 31 | 32 | func (self *WalkingState) Move() { 33 | fmt.Println("Robot walks 2 m/s") 34 | } 35 | 36 | func main() { 37 | context := &Context{} 38 | 39 | runningState := &RunningState{} 40 | runningState.Action(context) 41 | 42 | context.state.Move() 43 | 44 | walkingState := &WalkingState{} 45 | walkingState.Action(context) 46 | 47 | context.state.Move() 48 | } 49 | -------------------------------------------------------------------------------- /decorator/decorator.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Request func(string) string 6 | 7 | func RequireAuth(req Request) Request { 8 | return func(data string) string { 9 | fmt.Println("ensure header contains auth token") 10 | res := req(data) 11 | return res 12 | } 13 | } 14 | 15 | func EnsureParam(param string, req Request) Request { 16 | return func(data string) string { 17 | fmt.Println("ensure " + param + " is passed in body") 18 | res := req(data) 19 | return res 20 | } 21 | } 22 | 23 | func LogJson(req Request) Request { 24 | return func(data string) string { 25 | fmt.Println("enable log request response in json format") 26 | res := req(data) 27 | return res 28 | } 29 | } 30 | 31 | func FacebookAPI(data string) string { 32 | return "calling facebook api with -- " + data 33 | } 34 | 35 | func main() { 36 | client := LogJson(RequireAuth(EnsureParam("profile", FacebookAPI))) 37 | res := client("auth: key, profile: trump") 38 | fmt.Println(res) 39 | } 40 | -------------------------------------------------------------------------------- /prototype/prototype.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type botPrototype interface { 6 | clone() botPrototype 7 | SendMessage() string 8 | } 9 | 10 | type BotCache struct { 11 | bot botPrototype 12 | } 13 | 14 | func (self *BotCache) Store(bot botPrototype) { 15 | self.bot = bot 16 | } 17 | 18 | func (self *BotCache) Load() botPrototype { 19 | bot := self.bot 20 | return bot.clone() 21 | } 22 | 23 | type Bot struct { 24 | message string 25 | } 26 | 27 | func (self *Bot) Init() { 28 | // init bot takes resources and time 29 | } 30 | 31 | func (self *Bot) SendMessage() string { 32 | return self.message 33 | } 34 | 35 | func (self *Bot) clone() botPrototype { 36 | return &Bot{self.message} 37 | } 38 | 39 | func main() { 40 | bot := Bot{"Hello"} 41 | bot.Init() 42 | 43 | botCache := BotCache{} 44 | botCache.Store(&bot) 45 | 46 | cloned := botCache.Load() 47 | 48 | fmt.Printf("%p %s\n", &bot, bot.SendMessage()) 49 | fmt.Printf("%p %s\n", &cloned, cloned.SendMessage()) 50 | } 51 | -------------------------------------------------------------------------------- /private-class-data/private-class-data.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "strconv" 7 | ) 8 | 9 | type CircleData struct { 10 | x, y, radius int 11 | color string 12 | } 13 | 14 | type Circle struct { 15 | circleData *CircleData 16 | } 17 | 18 | func NewCircle(x int, y int, radius int, color string) *Circle { 19 | circle := &Circle{ 20 | &CircleData{x, y, radius, color}, 21 | } 22 | return circle 23 | } 24 | 25 | func (self *Circle) Circumference() float64 { 26 | return float64(self.circleData.radius) * math.Pi 27 | } 28 | 29 | func (self *Circle) Diameter() int { 30 | return self.circleData.radius * 2 31 | } 32 | 33 | func (self *Circle) Draw() { 34 | fmt.Println("Draw circle with x: " + strconv.Itoa(self.circleData.x) + ", y: " + strconv.Itoa(self.circleData.y) + ", color: " + self.circleData.color) 35 | } 36 | 37 | func main() { 38 | circle := NewCircle(1, 1, 3, "red") 39 | fmt.Println(circle.Circumference()) 40 | fmt.Println(circle.Diameter()) 41 | circle.Draw() 42 | } 43 | -------------------------------------------------------------------------------- /facade/facade.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Server struct{} 6 | 7 | func (self *Server) Listen(port int) { 8 | fmt.Printf("listening to port %v\n", port) 9 | } 10 | 11 | type Logging struct{} 12 | 13 | func (self *Logging) Aggregate() { 14 | fmt.Println("aggregate distributed logs") 15 | } 16 | 17 | type Monitoring struct{} 18 | 19 | func (self *Monitoring) Subscribe(event string) { 20 | fmt.Printf("continously watch %s\n", event) 21 | } 22 | 23 | type CloudSystemFacade struct { 24 | webServer *Server 25 | log *Logging 26 | monitor *Monitoring 27 | } 28 | 29 | func NewCloudSystemFacade() *CloudSystemFacade { 30 | return &CloudSystemFacade{&Server{}, &Logging{}, &Monitoring{}} 31 | } 32 | 33 | func (self *CloudSystemFacade) start() { 34 | self.webServer.Listen(80) 35 | self.log.Aggregate() 36 | self.monitor.Subscribe("network traffic") 37 | self.monitor.Subscribe("cpu resource") 38 | } 39 | 40 | func main() { 41 | cloudSystem := NewCloudSystemFacade() 42 | cloudSystem.start() 43 | } 44 | -------------------------------------------------------------------------------- /iterator/iterator.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Iterator interface { 6 | HasNext() bool 7 | Next() interface{} 8 | } 9 | 10 | type Container interface { 11 | GetIterator() Iterator 12 | } 13 | 14 | type SchoolIterator struct { 15 | schoolRepo *SchoolRepo 16 | index int 17 | } 18 | 19 | func (self *SchoolIterator) HasNext() bool { 20 | if self.index < len(self.schoolRepo.schools) { 21 | return true 22 | } 23 | return false 24 | } 25 | 26 | func (self *SchoolIterator) Next() interface{} { 27 | defer func() { self.index++ }() 28 | if self.HasNext() { 29 | return self.schoolRepo.schools[self.index] 30 | } 31 | return nil 32 | } 33 | 34 | type SchoolRepo struct { 35 | schools []string 36 | } 37 | 38 | func (self *SchoolRepo) GetIterator() Iterator { 39 | return &SchoolIterator{schoolRepo: self} 40 | } 41 | 42 | func main() { 43 | schoolsRepo := &SchoolRepo{[]string{"Cornell", "Stanford", "MIT", "Harvard"}} 44 | for i := schoolsRepo.GetIterator(); i.HasNext(); { 45 | school := i.Next() 46 | fmt.Println(school) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /null-object/null-object.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type User interface { 6 | IsNil() bool 7 | GetName() string 8 | } 9 | 10 | type RealUser struct { 11 | name string 12 | } 13 | 14 | func (self *RealUser) IsNil() bool { 15 | return false 16 | } 17 | 18 | func (self *RealUser) GetName() string { 19 | return self.name 20 | } 21 | 22 | type NilUser struct{} 23 | 24 | func (self *NilUser) IsNil() bool { 25 | return true 26 | } 27 | 28 | func (self *NilUser) GetName() string { 29 | return "User not found in database" 30 | } 31 | 32 | type UserFactory struct { 33 | nameList []string 34 | } 35 | 36 | func (self *UserFactory) GetUser(username string) User { 37 | for _, name := range self.nameList { 38 | if name == username { 39 | return &RealUser{name: username} 40 | } 41 | } 42 | return &NilUser{} 43 | } 44 | 45 | func main() { 46 | userFactory := &UserFactory{[]string{"Allie", "Bob", "David"}} 47 | users := []string{"Allie", "Bob", "Charlie", "David", "Edward"} 48 | 49 | for _, user := range users { 50 | fmt.Println(userFactory.GetUser(user).GetName()) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /factory/factory.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Coffee interface { 6 | Brew() string 7 | } 8 | 9 | type EspressoCoffee struct{} 10 | 11 | func (ec *EspressoCoffee) Brew() string { 12 | return "Pour espresso coffee" 13 | } 14 | 15 | type MacchiatoCoffee struct{} 16 | 17 | func (mc *MacchiatoCoffee) Brew() string { 18 | return "Pour macchiato coffee" 19 | } 20 | 21 | type CapuccinoCoffee struct{} 22 | 23 | func (cc *CapuccinoCoffee) Brew() string { 24 | return "Pour capuccino coffee" 25 | } 26 | 27 | type CoffeeType int 28 | 29 | const ( 30 | Espresso CoffeeType = 1 << iota 31 | Macchiato 32 | Capuccino 33 | ) 34 | 35 | func CoffeeFactory(t CoffeeType) Coffee { 36 | switch t { 37 | case Espresso: 38 | return &EspressoCoffee{} 39 | case Macchiato: 40 | return &MacchiatoCoffee{} 41 | case Capuccino: 42 | return &CapuccinoCoffee{} 43 | default: 44 | return nil 45 | } 46 | } 47 | 48 | func main() { 49 | coffees := []Coffee{ 50 | CoffeeFactory(Espresso), 51 | CoffeeFactory(Macchiato), 52 | CoffeeFactory(Capuccino), 53 | } 54 | 55 | for _, coffee := range coffees { 56 | fmt.Println(coffee.Brew()) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /mediator/mediator.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Mediator interface { 6 | AddUser(User) 7 | SendMessage(User, string) 8 | } 9 | 10 | type Chatroom struct { 11 | users []*User 12 | } 13 | 14 | func (self *Chatroom) AddUser(user *User) { 15 | self.users = append(self.users, user) 16 | } 17 | 18 | func (self *Chatroom) SendMessage(sender *User, message string) { 19 | for _, user := range self.users { 20 | if user != sender { 21 | fmt.Println(sender.name + " sent message to " + user.name + ": " + message) 22 | } 23 | } 24 | } 25 | 26 | type User struct { 27 | name string 28 | mediator *Chatroom 29 | } 30 | 31 | func (self *User) Send(message string) { 32 | self.mediator.SendMessage(self, message) 33 | } 34 | 35 | func NewUser(name string, mediator *Chatroom) *User { 36 | user := &User{name, mediator} 37 | mediator.AddUser(user) 38 | return user 39 | } 40 | 41 | func main() { 42 | chatroom := &Chatroom{} 43 | larry := NewUser("Larry", chatroom) 44 | mark := NewUser("Mark", chatroom) 45 | bill := NewUser("Bill", chatroom) 46 | 47 | larry.Send("Hello from Larry!") 48 | mark.Send("Aloha from Mark!!") 49 | bill.Send("Hi from Bill!!!") 50 | } 51 | -------------------------------------------------------------------------------- /proxy/proxy.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Image interface { 8 | Display() 9 | } 10 | 11 | type RealImage struct { 12 | fileName string 13 | } 14 | 15 | func (self *RealImage) Display() { 16 | fmt.Println("displaying " + self.fileName) 17 | } 18 | 19 | func (self *RealImage) loadFromDisk(fileName string) { 20 | fmt.Println("loading " + self.fileName + " from disk") 21 | } 22 | 23 | func NewRealImage(fileName string) *RealImage { 24 | realImage := &RealImage{fileName} 25 | realImage.loadFromDisk(fileName) 26 | return realImage 27 | } 28 | 29 | type ProxyImage struct { 30 | realImage *RealImage 31 | fileName string 32 | } 33 | 34 | func (self *ProxyImage) Display() { 35 | if self.realImage == nil { 36 | self.realImage = NewRealImage(self.fileName) 37 | } 38 | self.realImage.Display() 39 | } 40 | 41 | func NewProxyImage(fileName string) *ProxyImage { 42 | proxyImage := &ProxyImage{fileName: fileName} 43 | proxyImage.fileName = fileName 44 | return proxyImage 45 | } 46 | 47 | func main() { 48 | image := NewProxyImage("yosemite.jpg") 49 | 50 | // image loaded from disk 51 | image.Display() 52 | 53 | // image not loaded from disk 54 | image.Display() 55 | } 56 | -------------------------------------------------------------------------------- /object-pool/object-pool.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | var uniqueID int 6 | 7 | type Object struct { 8 | id int 9 | } 10 | 11 | type ObjectPool struct { 12 | unlocked []*Object 13 | locked []*Object 14 | } 15 | 16 | func (self *ObjectPool) AcquireObject() *Object { 17 | var object *Object 18 | if len(self.unlocked) <= 0 { 19 | object = &Object{uniqueID} 20 | uniqueID++ 21 | } else { 22 | object, self.unlocked = self.unlocked[len(self.unlocked)-1], self.unlocked[:len(self.unlocked)-1] 23 | } 24 | self.locked = append(self.locked, object) 25 | return object 26 | } 27 | 28 | func (self *ObjectPool) ReleaseObject(object *Object) { 29 | for i, lockedObject := range self.locked { 30 | if lockedObject.id == object.id { 31 | self.locked = append(self.locked[:i], self.locked[i+1:]...) 32 | self.unlocked = append(self.unlocked, object) 33 | return 34 | } 35 | } 36 | } 37 | 38 | func main() { 39 | objectPool := &ObjectPool{} 40 | object1 := objectPool.AcquireObject() 41 | fmt.Println(object1.id) 42 | 43 | object2 := objectPool.AcquireObject() 44 | fmt.Println(object2.id) 45 | objectPool.ReleaseObject(object2) 46 | 47 | object3 := objectPool.AcquireObject() 48 | fmt.Println(object3.id) 49 | } 50 | -------------------------------------------------------------------------------- /memento/memento.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Memento struct { 6 | state string 7 | } 8 | 9 | type Originator struct { 10 | state string 11 | } 12 | 13 | func (self *Originator) SaveState() *Memento { 14 | return &Memento{self.state} 15 | } 16 | 17 | func (self *Originator) GetState(memento *Memento) { 18 | self.state = memento.state 19 | } 20 | 21 | type CareTaker struct { 22 | mementoList []*Memento 23 | } 24 | 25 | func (self *CareTaker) Add(state *Memento) { 26 | self.mementoList = append(self.mementoList, state) 27 | } 28 | 29 | func (self *CareTaker) Get(index int) *Memento { 30 | return self.mementoList[index] 31 | } 32 | 33 | func main() { 34 | originator := &Originator{} 35 | careTaker := &CareTaker{} 36 | 37 | originator.state = "State 1" 38 | originator.state = "State 2" 39 | careTaker.Add(originator.SaveState()) 40 | 41 | originator.state = "State 3" 42 | careTaker.Add(originator.SaveState()) 43 | 44 | originator.state = "State 4" 45 | fmt.Println("Current state: ", originator.state) 46 | 47 | originator.GetState(careTaker.Get(0)) 48 | fmt.Println("First saved state: ", originator.state) 49 | 50 | originator.GetState(careTaker.Get(1)) 51 | fmt.Println("Second saved state: ", originator.state) 52 | } 53 | -------------------------------------------------------------------------------- /template/template.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Crawler interface { 6 | scrapeWebpage() 7 | parseHtml() 8 | store() 9 | } 10 | 11 | type AbstractCrawler struct { 12 | crawler Crawler 13 | } 14 | 15 | func (self *AbstractCrawler) Start() { 16 | self.crawler.scrapeWebpage() 17 | self.crawler.parseHtml() 18 | self.crawler.store() 19 | } 20 | 21 | type NewyorkTimesCrawler struct { 22 | *AbstractCrawler 23 | } 24 | 25 | func (self *NewyorkTimesCrawler) scrapeWebpage() { 26 | fmt.Println("Crawling newyork times webpage") 27 | } 28 | 29 | func (self *NewyorkTimesCrawler) parseHtml() { 30 | fmt.Println("Parse the scraped newyork times html page") 31 | } 32 | 33 | func (self *NewyorkTimesCrawler) store() { 34 | fmt.Println("Store processed newyork times page to DB") 35 | } 36 | 37 | type AljazeeraCrawler struct { 38 | *AbstractCrawler 39 | } 40 | 41 | func (self *AljazeeraCrawler) scrapeWebpage() { 42 | fmt.Println("Crawling aljazeera webpage") 43 | } 44 | 45 | func (self *AljazeeraCrawler) parseHtml() { 46 | fmt.Println("Parse the scraped aljazeera html page") 47 | } 48 | 49 | func (self *AljazeeraCrawler) store() { 50 | fmt.Println("Store processed aljazeera page to DB") 51 | } 52 | 53 | func main() { 54 | crawler := &AbstractCrawler{&NewyorkTimesCrawler{}} 55 | crawler.Start() 56 | 57 | crawler = &AbstractCrawler{&AljazeeraCrawler{}} 58 | crawler.Start() 59 | } 60 | -------------------------------------------------------------------------------- /command/command.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type Order interface { 9 | Execute() 10 | } 11 | 12 | type Stock struct { 13 | name string 14 | amount int 15 | } 16 | 17 | func (self *Stock) Buy() { 18 | fmt.Println("buy " + self.name + " stock " + strconv.Itoa(self.amount) + " shares") 19 | } 20 | 21 | func (self *Stock) Sell() { 22 | fmt.Println("sell " + self.name + " stock " + strconv.Itoa(self.amount) + " shares") 23 | } 24 | 25 | type BuyStock struct { 26 | stock *Stock 27 | } 28 | 29 | func (self *BuyStock) Execute() { 30 | self.stock.Buy() 31 | } 32 | 33 | type SellStock struct { 34 | stock *Stock 35 | } 36 | 37 | func (self *SellStock) Execute() { 38 | self.stock.Sell() 39 | } 40 | 41 | // command invoker which is going to 42 | // take and execute request 43 | type Broker struct { 44 | orderList []Order 45 | } 46 | 47 | func (self *Broker) TakeOrder(order Order) { 48 | self.orderList = append(self.orderList, order) 49 | } 50 | 51 | func (self *Broker) PlaceOrders() { 52 | for _, order := range self.orderList { 53 | order.Execute() 54 | } 55 | self.orderList = nil 56 | } 57 | 58 | func main() { 59 | stock := &Stock{name: "APPL", amount: 10} 60 | buyStock := &BuyStock{stock} 61 | sellStock := &SellStock{stock} 62 | 63 | broker := &Broker{} 64 | broker.TakeOrder(buyStock) 65 | broker.TakeOrder(sellStock) 66 | 67 | broker.PlaceOrders() 68 | } 69 | -------------------------------------------------------------------------------- /interpreter/interpreter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type Expression interface { 9 | Interpret(context string) bool 10 | } 11 | 12 | type TerminalExpression struct { 13 | data string 14 | } 15 | 16 | func (self *TerminalExpression) Interpret(context string) bool { 17 | if strings.Contains(context, self.data) { 18 | return true 19 | } 20 | return false 21 | } 22 | 23 | type OrExpression struct { 24 | expr1 Expression 25 | expr2 Expression 26 | } 27 | 28 | func (self *OrExpression) Interpret(context string) bool { 29 | return self.expr1.Interpret(context) || self.expr2.Interpret(context) 30 | } 31 | 32 | type AndExpression struct { 33 | expr1 Expression 34 | expr2 Expression 35 | } 36 | 37 | func (self *AndExpression) Interpret(context string) bool { 38 | return self.expr1.Interpret(context) && self.expr2.Interpret(context) 39 | } 40 | 41 | func main() { 42 | // Example: Donald and Trump are male 43 | donald := &TerminalExpression{"Donald"} 44 | trump := &TerminalExpression{"Trump"} 45 | isMale := &OrExpression{donald, trump} 46 | 47 | // Example: Natalie is a single lady 48 | natalie := &TerminalExpression{"Natalie"} 49 | single := &TerminalExpression{"Single"} 50 | isSingleLady := &AndExpression{natalie, single} 51 | 52 | fmt.Println("Donald is male?", isMale.Interpret("Donald")) 53 | fmt.Println("Natalie is single lady?", isSingleLady.Interpret("Single Natalie")) 54 | 55 | } 56 | -------------------------------------------------------------------------------- /composite/composite.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type AbstractFile interface { 6 | GetName() string 7 | PrintList() 8 | } 9 | 10 | type File struct { 11 | name string 12 | } 13 | 14 | func (self *File) GetName() string { 15 | return self.name 16 | } 17 | 18 | func (self *File) PrintList() { 19 | fmt.Println(self.name) 20 | } 21 | 22 | type Directory struct { 23 | includedFiles []AbstractFile 24 | name string 25 | } 26 | 27 | func (self *Directory) GetName() string { 28 | return self.name 29 | } 30 | 31 | func (self *Directory) Add(entry AbstractFile) { 32 | self.includedFiles = append(self.includedFiles, entry) 33 | } 34 | 35 | func (self *Directory) PrintList() { 36 | fmt.Println() 37 | for _, file := range self.includedFiles { 38 | fmt.Print(self.GetName() + "/") 39 | file.PrintList() 40 | } 41 | } 42 | 43 | func main() { 44 | music := &Directory{name: "MUSIC"} 45 | johnMayer := &Directory{name: "JOHN-MAYER"} 46 | incubus := &Directory{name: "INCUBUS"} 47 | track1 := &File{name: "free falling.mp3"} 48 | track2 := &File{name: "anna molly.mp3"} 49 | track3 := &File{name: "californication.mp3"} 50 | track4 := &File{name: "drive.mp3"} 51 | track5 := &File{name: "dig.mp3"} 52 | track6 := &File{name: "xoxo.mp3"} 53 | music.Add(track3) 54 | music.Add(johnMayer) 55 | music.Add(incubus) 56 | johnMayer.Add(track1) 57 | johnMayer.Add(track4) 58 | johnMayer.Add(track6) 59 | incubus.Add(track2) 60 | incubus.Add(track5) 61 | 62 | music.PrintList() 63 | } 64 | -------------------------------------------------------------------------------- /observer/observer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | type Notifier struct { 9 | observers []Observer 10 | state int 11 | } 12 | 13 | func (self *Notifier) SetState(state int) { 14 | fmt.Println("set state to: " + strconv.Itoa(state)) 15 | self.state = state 16 | self.notify() 17 | } 18 | 19 | func (self *Notifier) Register(observer Observer) { 20 | self.observers = append(self.observers, observer) 21 | } 22 | 23 | func (self *Notifier) notify() { 24 | for _, observer := range self.observers { 25 | observer.update() 26 | } 27 | } 28 | 29 | type Observer interface { 30 | update() 31 | } 32 | 33 | type BinaryObserver struct { 34 | notifier *Notifier 35 | } 36 | 37 | func (self *BinaryObserver) update() { 38 | fmt.Println("binary string: " + strconv.FormatInt(int64(self.notifier.state), 2)) 39 | } 40 | 41 | func NewBinaryObserver(notifier *Notifier) { 42 | binaryObserver := &BinaryObserver{} 43 | binaryObserver.notifier = notifier 44 | binaryObserver.notifier.Register(binaryObserver) 45 | } 46 | 47 | type HexaObserver struct { 48 | notifier *Notifier 49 | } 50 | 51 | func (self *HexaObserver) update() { 52 | fmt.Printf("hexadecimal string: %x\n", self.notifier.state) 53 | } 54 | 55 | func NewHexaObserver(notifier *Notifier) { 56 | hexaObserver := &HexaObserver{} 57 | hexaObserver.notifier = notifier 58 | hexaObserver.notifier.Register(hexaObserver) 59 | } 60 | 61 | func main() { 62 | notifier := &Notifier{} 63 | NewBinaryObserver(notifier) 64 | NewHexaObserver(notifier) 65 | 66 | notifier.SetState(9) 67 | notifier.SetState(4) 68 | notifier.SetState(123) 69 | } 70 | -------------------------------------------------------------------------------- /abstract-factory/abstract-factory.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type PickupTruck interface { 6 | Load() string 7 | } 8 | type SportCar interface { 9 | Race() string 10 | } 11 | 12 | type VehicleFactory interface { 13 | CreatePickupTruck() PickupTruck 14 | CreateSportCar() SportCar 15 | } 16 | 17 | type MazdaPickup struct{} 18 | 19 | func (mp *MazdaPickup) Load() string { 20 | return "mazda loading 200 pounds" 21 | } 22 | 23 | type MazdaSport struct{} 24 | 25 | func (ms *MazdaSport) Race() string { 26 | return "mazda racing 300 km/h" 27 | } 28 | 29 | type ToyotaPickup struct{} 30 | 31 | func (tp *ToyotaPickup) Load() string { 32 | return "toyota loading 150 pounds" 33 | } 34 | 35 | type ToyotaSport struct{} 36 | 37 | func (ts *ToyotaSport) Race() string { 38 | return "toyota racing 260 km/h" 39 | } 40 | 41 | type MazdaFactory struct{} 42 | 43 | func (f *MazdaFactory) CreatePickupTruck() PickupTruck { 44 | return &MazdaPickup{} 45 | } 46 | 47 | func (f *MazdaFactory) CreateSportCar() SportCar { 48 | return &MazdaSport{} 49 | } 50 | 51 | type ToyotaFactory struct{} 52 | 53 | func (f *ToyotaFactory) CreatePickupTruck() PickupTruck { 54 | return &ToyotaPickup{} 55 | } 56 | 57 | func (f *ToyotaFactory) CreateSportCar() SportCar { 58 | return &ToyotaSport{} 59 | } 60 | 61 | func main() { 62 | mazdaFactory := &MazdaFactory{} 63 | toyotaFactory := &ToyotaFactory{} 64 | 65 | mazdaPickup := mazdaFactory.CreatePickupTruck() 66 | mazdaSport := mazdaFactory.CreateSportCar() 67 | toyotaPickup := toyotaFactory.CreatePickupTruck() 68 | toyotaSport := toyotaFactory.CreateSportCar() 69 | 70 | fmt.Println(mazdaPickup.Load()) 71 | fmt.Println(mazdaSport.Race()) 72 | fmt.Println(toyotaPickup.Load()) 73 | fmt.Println(toyotaSport.Race()) 74 | } 75 | -------------------------------------------------------------------------------- /visitor/visitor.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type TaxiElement interface { 6 | Accept(VisitorPassenger) 7 | } 8 | 9 | type Uber struct { 10 | name string 11 | } 12 | 13 | func (self *Uber) Accept(visitor VisitorPassenger) { 14 | self.name = "Uber" 15 | visitor.CallUber(self) 16 | } 17 | 18 | type Lyft struct { 19 | name string 20 | } 21 | 22 | func (self *Lyft) Accept(visitor VisitorPassenger) { 23 | self.name = "Lyft" 24 | visitor.CallLyft(self) 25 | } 26 | 27 | type Gojek struct { 28 | name string 29 | } 30 | 31 | func (self *Gojek) Accept(visitor VisitorPassenger) { 32 | self.name = "Gojek" 33 | visitor.CallGojek(self) 34 | } 35 | 36 | type VisitorPassenger interface { 37 | CallUber(*Uber) 38 | CallLyft(*Lyft) 39 | CallGojek(*Gojek) 40 | } 41 | 42 | type BusinessmanPassenger struct{} 43 | 44 | func (self *BusinessmanPassenger) CallUber(uber *Uber) { 45 | fmt.Println("Businessman is riding " + uber.name) 46 | } 47 | 48 | func (self *BusinessmanPassenger) CallLyft(lyft *Lyft) { 49 | fmt.Println("Businessman is riding " + lyft.name) 50 | } 51 | 52 | func (self *BusinessmanPassenger) CallGojek(gojek *Gojek) { 53 | fmt.Println("Businessman is riding " + gojek.name) 54 | } 55 | 56 | type StudentPassenger struct{} 57 | 58 | func (self *StudentPassenger) CallUber(uber *Uber) { 59 | fmt.Println("Student is riding " + uber.name) 60 | } 61 | 62 | func (self *StudentPassenger) CallLyft(lyft *Lyft) { 63 | fmt.Println("Student is riding " + lyft.name) 64 | } 65 | 66 | func (self *StudentPassenger) CallGojek(gojek *Gojek) { 67 | fmt.Println("Student is riding " + gojek.name) 68 | } 69 | 70 | func main() { 71 | taxis := []TaxiElement{&Uber{}, &Lyft{}, &Gojek{}} 72 | businessman := &BusinessmanPassenger{} 73 | student := &StudentPassenger{} 74 | 75 | for _, taxi := range taxis { 76 | taxi.Accept(businessman) 77 | } 78 | for _, taxi := range taxis { 79 | taxi.Accept(student) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /chain-of-responsibility/chain-of-responsibility.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type Logger interface { 6 | write(message string) string 7 | LogMessage(logger Logger, message string) 8 | } 9 | 10 | type AbstractLogger struct { 11 | Logger 12 | level int 13 | nextLogger Logger 14 | } 15 | 16 | func (self *AbstractLogger) SetNextLogger(nextLogger Logger) { 17 | self.nextLogger = nextLogger 18 | } 19 | 20 | func (self *AbstractLogger) LogMessage(logger Logger, message string) { 21 | fmt.Println(logger.write(message)) 22 | if self.nextLogger != nil { 23 | self.nextLogger.LogMessage(self.nextLogger, message) 24 | } 25 | } 26 | 27 | type ConsoleLogger struct { 28 | *AbstractLogger 29 | } 30 | 31 | func (self *ConsoleLogger) write(message string) string { 32 | return "Standard Console::Logger: " + message 33 | } 34 | 35 | func NewConsoleLogger() *ConsoleLogger { 36 | return &ConsoleLogger{&AbstractLogger{}} 37 | } 38 | 39 | type ErrorLogger struct { 40 | *AbstractLogger 41 | } 42 | 43 | func (self *ErrorLogger) write(message string) string { 44 | return "Error Console::Logger: " + message 45 | } 46 | 47 | func NewErrorLogger() *ErrorLogger { 48 | return &ErrorLogger{&AbstractLogger{}} 49 | } 50 | 51 | type FileLogger struct { 52 | *AbstractLogger 53 | } 54 | 55 | func (self *FileLogger) write(message string) string { 56 | return "File::Logger: " + message 57 | } 58 | 59 | func NewFileLogger() *FileLogger { 60 | return &FileLogger{&AbstractLogger{}} 61 | } 62 | 63 | func main() { 64 | errorLogger := NewErrorLogger() 65 | fileLogger := NewFileLogger() 66 | consoleLogger := NewConsoleLogger() 67 | errorLogger.SetNextLogger(fileLogger) 68 | fileLogger.SetNextLogger(consoleLogger) 69 | 70 | consoleLogger.LogMessage(consoleLogger, "This is an information") 71 | fileLogger.LogMessage(fileLogger, "This is a debug level information") 72 | errorLogger.LogMessage(errorLogger, "This is an error information") 73 | } 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go design patterns 2 | Collection of design pattern implementation in Go 3 | 4 | ## Creational patterns 5 | 6 | | Pattern | Description | 7 | |------------------|-------------| 8 | | [Abstract Factory](https://github.com/girikuncoro/go-design-pattern/blob/master/abstract-factory/abstract-factory.go) | Provide an interface for creating families of related or dependent objects without specifying their concrete classes. | 9 | | [Builder](https://github.com/girikuncoro/go-design-pattern/blob/master/builder/builder.go) | Separate the construction of a complex object from its representation so that the same construction process can create different representations. | 10 | | [Factory Method](https://github.com/girikuncoro/go-design-pattern/blob/master/factory/factory.go) | Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory method lets a class defer instantiation to subclasses. | 11 | | [Object Pool](https://github.com/girikuncoro/go-design-pattern/blob/master/object-pool/object-pool.go) | Instantiates and maintains a group of objects instances of the same type. | 12 | | [Prototype](https://github.com/girikuncoro/go-design-pattern/blob/master/prototype/prototype.go) | Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype. | 13 | | [Singleton](https://github.com/girikuncoro/go-design-pattern/blob/master/singleton/singleton.go) | Ensure a class only has one instance, and provide a global point of access to it. | 14 | 15 | ## Structural patterns 16 | | Pattern | Description | 17 | |------------------|-------------| 18 | | [Adapter](https://github.com/girikuncoro/go-design-pattern/blob/master/adapter/adapter.go) | Convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. | 19 | | [Bridge](https://github.com/girikuncoro/go-design-pattern/blob/master/bridge/bridge.go) | Decouple an abstraction from its implementation so that the two can cary independently. | 20 | | [Composite](https://github.com/girikuncoro/go-design-pattern/blob/master/composite/composite.go) | Compose objects into tree structures to represent part-whole hierarchies. Composite lets clients treat individual objects and compositions of objects uniformly. | 21 | | [Decorator](https://github.com/girikuncoro/go-design-pattern/blob/master/decorator/decorator.go) | Attach additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality. | 22 | | [Facade](https://github.com/girikuncoro/go-design-pattern/blob/master/facade/facade.go) | Provide a unified interface to a set of interfaces in a subsystem. Faade defines a higher-level interface that makes the subsystem easier to use. | 23 | | [Flyweight](https://github.com/girikuncoro/go-design-pattern/blob/master/flyweight/flyweight.go) | Use sharing to support large numbers of fine-grained objects efficiently. | 24 | | [Private Class Data](https://github.com/girikuncoro/go-design-pattern/blob/master/private-class-data/private-class-data.go) | Protect class state by minimizing the visibility of its attributes (data). | 25 | | [Proxy](https://github.com/girikuncoro/go-design-pattern/blob/master/proxy/proxy.go) | Provide a surrogate or placeholder for another object to control access to it. | 26 | 27 | ## Behavioral patterns 28 | | Pattern | Description | 29 | |------------------|-------------| 30 | | [Chain of Responsibility](https://github.com/girikuncoro/go-design-pattern/blob/master/chain-of-responsibility/chain-of-responsibility.go) | Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it. | 31 | | [Command](https://github.com/girikuncoro/go-design-pattern/blob/master/command/command.go) | Encapsulate a request as an object, thereby letting you parameterize clients with different requests, queue or log requests, and support undoable operations. | 32 | | [Interpreter](https://github.com/girikuncoro/go-design-pattern/blob/master/interpreter/interpreter.go) | Given a language, define a representation for its grammar along with an interpreter that uses the representation to interpret sentences in the language. | 33 | | [Iterator](https://github.com/girikuncoro/go-design-pattern/blob/master/iterator/iterator.go) | Provide a way to access the elements of an aggregate object sequentially without exposing its underlying its representation. | 34 | | [Mediator](https://github.com/girikuncoro/go-design-pattern/blob/master/mediator/mediator.go) | Define an object that encapsulates how a set of objevts interact. Mediator promotes loose coupling by keeping objevts from referring to each other explicitly, and it lets you vary their interaction independently. | 35 | | [Memento](https://github.com/girikuncoro/go-design-pattern/blob/master/memento/memento.go) | Without violating encapsulation, capture and externalize an object's internal state so that the object can be restored to this state later. | 36 | | [Null Object](https://github.com/girikuncoro/go-design-pattern/blob/master/null-object/null-object.go) | Encapsulate the absence of an object by providing a substitutable alternative that offers suitable default do nothing behavior. | 37 | | [Observer](https://github.com/girikuncoro/go-design-pattern/blob/master/observer/observer.go) | Define a one-to-many dependency between objects so that when one objet changes state, all its dependents are notified and updated automatically. | 38 | | [State](https://github.com/girikuncoro/go-design-pattern/blob/master/state/state.go) | Allow an object to alter its behavior when its internal state changes. The object will appear to change its class. | 39 | | [Strategy](https://github.com/girikuncoro/go-design-pattern/blob/master/strategy/strategy.go) | Define a family of algorithms, encapsulate each one, and make them interchangeable. Strategy lets the algorithm vary independently from clients that use it. | 40 | | [Template](https://github.com/girikuncoro/go-design-pattern/blob/master/template/template.go) | Define the skeleton of an algorithm in an operation, deferring some steps to subclasses. Template method lets subclasses redefine certain steps of an algorithm without changing the algorithm's structure. | 41 | | [Visitor](https://github.com/girikuncoro/go-design-pattern/blob/master/visitor/visitor.go) | Represent an operation to be performed on the elements of an object structure. Visitor lets you define a new operation without changing the classes of the elements on which it operates. | 42 | 43 | ## TODO 44 | ### Synchronization Patterns 45 | - [ ] Condition Variable 46 | - [ ] Lock/Mutex 47 | - [ ] Monitor 48 | - [ ] Read-Write Lock 49 | - [ ] Semaphore 50 | 51 | ### Concurrency Patterns 52 | - [ ] N-Barrier 53 | - [ ] Bounded Parallelism 54 | - [ ] Broadcast 55 | - [ ] Coroutines 56 | - [ ] Generators 57 | - [ ] Reactor 58 | - [ ] Parallelism 59 | - [ ] Producer Consumer 60 | 61 | ### Messaging Patterns 62 | - [ ] Fan-In 63 | - [ ] Fan-Out 64 | - [ ] Futures & Promises 65 | - [ ] Publish/Subscribe 66 | - [ ] Push & Pull 67 | 68 | ### Stability Patterns 69 | - [ ] Bulkheads 70 | - [ ] Circuit-Breaker 71 | - [ ] Deadline 72 | - [ ] Fall-Fast 73 | - [ ] Handshaking 74 | - [ ] Steady-State 75 | 76 | ### Profiling Patterns 77 | - [ ] Timing Functions 78 | 79 | ### Idioms 80 | - [ ] Functional Options 81 | 82 | ### Anti-Patterns 83 | - [ ] Cascading Failures 84 | 85 | ## References 86 | * [Design Patterns: Elements Reusable Object Book](https://www.amazon.com/Design-Patterns-Elements-Reusable-Object-Oriented/dp/0201633612) 87 | * [Sourcemaking](https://sourcemaking.com/design_patterns) 88 | * [Tmrts Go Patterns](http://tmrts.com/go-patterns) 89 | --------------------------------------------------------------------------------