经典设计模式(Golang)

访问者模式

Posted by Dingding on August 1, 2019

概述

  • 意图:主要将数据结构与数据操作分离。
  • 主要解决:稳定的数据结构和易变的操作耦合问题。
  • 何时使用:需要对一个对象结构中的对象进行很多不同的并且不相关的操作,而需要避免让这些操作”污染”这些对象的类,使用访问者模式将这些封装到类中。
  • 如何解决:在被访问的类里面加一个对外提供接待访问者的接口。
  • 关键代码:在数据基础类里面有一个方法接受访问者,将自身引用传入访问者。

实现

package main

import (
	"fmt"
)

// Bill 账单
type Bill interface {
	Accept(viewer AccountBookViewer) //接受一个账单的访问者
}

// AccountBookViewer 账单访问者
type AccountBookViewer interface {
	ViewConsumeBill(bill *ConsumeBill) //访问消费的账单
	ViewIncomeBill(bill *IncomeBill)   //访问收入的账单
}

// ConsumeBill 消费账单,实现 Bill接口
type ConsumeBill struct {
	Amount float64
	Item   string
}

// NewConsumeBill 消费账单
func NewConsumeBill(amount float64, item string) *ConsumeBill {
	return &ConsumeBill{
		Amount: amount,
		Item:   item,
	}
}

// Accept 实现账单 Bill 接口
func (c *ConsumeBill) Accept(viewer AccountBookViewer) {
	// 直接让访问者访问自己,这相当于一次静态分派
	viewer.ViewConsumeBill(c)
}

// IncomeBill 收入账单,实现 Bill接口
type IncomeBill struct {
	Amount float64
	Item   string
}

// NewIncomeBill 收入账单
func NewIncomeBill(amount float64, item string) *IncomeBill {
	return &IncomeBill{
		Amount: amount,
		Item:   item,
	}
}

// Accept 实现账单 Bill 接口
func (c *IncomeBill) Accept(viewer AccountBookViewer) {
	viewer.ViewIncomeBill(c)
}

// Boss 老板
type Boss struct {
	totalIncome  float64
	totalConsume float64
}

// ViewConsumeBill 账单访问
func (b *Boss) ViewConsumeBill(bill *ConsumeBill) {
	b.totalConsume += bill.Amount
}

// ViewIncomeBill 账单访问
func (b *Boss) ViewIncomeBill(bill *IncomeBill) {
	b.totalIncome += bill.Amount
}

// GetTotalIncome .
func (b *Boss) GetTotalIncome() {
	fmt.Println("老板查看收入总计:", b.totalIncome)
}

// GetTotalConsume .
func (b *Boss) GetTotalConsume() {
	fmt.Println("老板查看支出总计:", b.totalConsume)
}

// AccountBook 账本,提供给访问者访问
type AccountBook struct {
	billList []Bill
}

// AddBill 账本添加账单方法
func (a *AccountBook) AddBill(bill Bill) {
	a.billList = append(a.billList, bill)
}

// Show 账本访问方法
func (a *AccountBook) Show(viewer AccountBookViewer) {
	for _, bill := range a.billList {
		bill.Accept(viewer)
	}
}

//客户端
func main() {
	//
	accountBook := &AccountBook{}
	accountBook.AddBill(NewIncomeBill(100, "卖商品"))
	accountBook.AddBill(NewIncomeBill(200, "卖广告位"))
	accountBook.AddBill(NewConsumeBill(50, "工资"))
	accountBook.AddBill(NewConsumeBill(100, "材料"))

	boss := &Boss{}
	accountBook.Show(boss)

	boss.GetTotalIncome()
	boss.GetTotalConsume()
}