Go语言面向对象之旅

目的:

通过一道题目体验下Go语言下的OO编程范式。

题目:

模拟人和机器人制造产品。

这里人和机器人在工作时都是一名worker(扮演的角色),工作的流程是一样的:如果能量没有消耗完,那么可以继续制造产品,否则停工。

区别在于依赖的能量消耗和获取方式不同:

  1. 人制造产品会消耗吃饭得到的能量,缺乏能量后需要再吃饭补充。人吃完饭后能量值为10,每生产一件产品,消耗能量值为1,吃完饭后能量恢复到10。
  2. 机器人制造产品会消耗电能,缺乏能量后需要再充电,充完电可以继续工作。充完电后能量值为100,每生产一件产品,消耗能量值为1,充电后能量值恢复到100。

基于组合式设计思想,得到一个领域模型:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HykebIFT-1645663259864)(https://upload-images.jianshu.io/upload_images/2463211-33dd36fe2be15f24.png?imageMogr2/auto-orient/strip|imageView2/2/format/webp)]

Energy:

接口在Golang中是一个interface。它包含两个方法:一个是消耗能量Consume,另一个是能量是否耗尽IsExhausted

package energy

type Energy interface {
	Consume()
	IsExhausted() bool
}

关于接口可以参考:Go 专栏|接口 interface - 简书 (jianshu.com)

HumanEnergy:

基于C++的知识可以将HumanEnergy理解为是类Energy的子类。

package energy

const MAX_CONSUME_TIMES int = 10

type HumanEnergy struct {
	isHungry     bool
	consumeTimes int
}

func (this *HumanEnergy) Eat() {
	this.consumeTimes = 0
	this.isHungry = false
}

func (this *HumanEnergy) Consume() {
	this.consumeTimes++
	if this.consumeTimes >= MAX_CONSUME_TIMES {
		this.isHungry = true
	}
}

func (this *HumanEnergy) IsExhausted() bool {
	return this.isHungry
}

基础语法参考:Go 语言函数方法 | 菜鸟教程 (runoob.com)

RobotEnergy:

同样地,RobotEnergy也是Energy的子类

package energy

const (
	FULL_PERCENT    int = 100
	CONSUME_PERCENT int = 1
)

type RobotEnergy struct {
	percent int
}

func (this *RobotEnergy) Charge() {
	this.percent = FULL_PERCENT
}

func (this *RobotEnergy) Consume() {
	if this.percent > 0 {
		this.percent -= CONSUME_PERCENT
	}
}

func (this *RobotEnergy) IsExhausted() bool {
	return this.percent == 0
}

Worker:

Worker依赖于Energy这个接口,具体这个接口实际上是指向HumanEnergy还是RobotEnergy,根据需要注入。

这里体现的是依赖倒置原则:

Worker依赖于Energy这个稳定接口,在Produce产品的时候不关心用的能量是HumanEnergy还是RobotEnergy

package worker

import "github.com/yanxicheung/energy/energy"

type Worker struct {
	producerNum int
	Energy      energy.Energy
}

func (this *Worker) Produce() {
	if this.Energy.IsExhausted() {
		return
	}
	this.producerNum++
	this.Energy.Consume()
}

func (this *Worker) GetProduceNum() int {
	return this.producerNum
}

Object:

领域对象是对具体EnergyWorker的组合。

这里会介绍两种组合方式:

第一种是Human采用的委托方式,相当于C++中的包含。

第二种是Robot采用的匿名组合,相当于C++中的继承。

Human:

Human领域对象组合了HumanEnergyWorker。代码如下:

package object

import (
	"github.com/yanxicheung/energy/energy"
	"github.com/yanxicheung/energy/worker"
)

type Human struct {
    // 委托方式
	Energy energy.HumanEnergy
	Worker worker.Worker
}

// 函数名字小写,包外不可见;
func (this *Human) inject() {
	this.Worker.Energy = &this.Energy
}

func NewHuman() *Human {
	h := &Human{}
	h.inject()
	return h
}

this.Worker.Energy = &this.Energy的作用是依赖注入,将Worker依赖的接口Energy实际指向HumanEnergy

由于 Human 使用了委托方式组合了HumanEnergyWorker。 所以Human并没有实现Energy的抽象接口,所以this不可以直接赋值给this.Worker.Energy,否则编译报错:InvalidIfaceAssign

Robot:

Robot领域对象组合了RobotEnergyWorker。代码如下:

package object

import (
	"github.com/yanxicheung/energy/energy"
	"github.com/yanxicheung/energy/worker"
)

type Robot struct {
	// 匿名组合struct 相当与C++中的继承
	energy.RobotEnergy
	worker.Worker
}

func (this *Robot) inject() {
	this.Worker.Energy = this
}

func NewRobot() *Robot {
	r := &Robot{}
	r.inject()
	return r
}

this.Worker.Energy = this的作用是依赖注入,将Worker依赖的接口Energy实际指向robotEnergy

由于 Robot 使用了匿名组合方式组合了RobotEnergyWorker。 所以Robot实现Energy的抽象接口,所以this可以直接赋值给this.Worker.Energy

使用:

Human领域对象的具体使用如下:

func HumanWork() {
	human := object.NewHuman()
	human.Energy.Eat()
	for {
		if human.Energy.IsExhausted() {
			break
		}
		human.Worker.Produce()
	}
	fmt.Printf("human produce %v products in one cycle\n", human.Worker.GetProduceNum())
}

由于Robot使用匿名组合方式组合了RobotEnergyWorker,当我们访问该struct的方法时,可以通过默认的变量名访问:

func RobotWork() {
	robot := object.NewRobot()
	robot.RobotEnergy.Charge()
	for {
		if robot.Energy.IsExhausted() {
			break
		}
		robot.Worker.Produce()
	}
	fmt.Printf("robot produce %v products in one cycle\n", robot.Worker.GetProduceNum())
}

也可以直接访问(略去默认的变量名) :

func RobotWork() {
	robot := object.NewRobot()
	robot.Charge()
	for {
		if robot.IsExhausted() {
			break
		}
		robot.Produce()
	}
	fmt.Printf("robot produce %v products in one cycle\n", robot.GetProduceNum())
}

参考文献:

  1. Implement Domain Object in Golang - 简书 (jianshu.com)
  2. Golang中小类大对象的一种实现 - 简书 (jianshu.com)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值