Golang中小类大对象的一种实现
小类大对象
软件设计本质是解决分与合的问题。我们先将系统分解成很多单一职责的小类,然后利用“依赖注入“(Golang)或多重继承(C++)的手段再将它们组合成对象。所以,类应该是小的,对象应该是大的。
文章图片
盲人摸象.png
【Golang中小类大对象的一种实现】类作为一种模块化手段,遵循高内聚低耦合,让软件易于应对变化,可以将类看做是领域对象拥有的职责或扮演的角色;对象作为一种领域对象的的直接映射,解决了过多的类带来的可理解性问题,让领域可以指导设计,设计真正反映领域,领域对象需要真正意义上的生命周期管理。
上帝类是糟糕的,但上帝对象却恰恰是我们所期盼的。就拿大象来说,它很大,大到可以为外部提供多种功能的服务,而对于每种不同的服务需要者,它就扮演不同的角色。对于不同个体,需要的是更加具体的服务,而不是一头大象,因而他也并不关心为他服务的事物背后是否是一头大象。
文章图片
大象组件.png
注:小类大对象和DCI(Data, Context, Interaction)
殊途同归。
转账问题
假设我们有下面的一个转账场景:
-
zhangsan123
帐户中存了1000
元 -
lisi456
帐户中存了200
元 -
zhangsan123
给lisi456
转账300
元
文章图片
transfer-money-classes.png 上图中的类对应于
DCI
中的Methodful Roles
,接口对应于DCI
中的Methodless Roles
。小类的实现
accountIdInfo类的实现 注:Golang中小写字母开头的标识符仅具有包内可见性。
//account_id_info.go
package domaintype AccountId stringtype accountIdInfo struct {
id AccountId
}func newAccountIdInfo(id AccountId) *accountIdInfo {
return &accountIdInfo{id}
}func (a *accountIdInfo) getAccountId() AccountId {
return a.id
}
balance类的实现
//balance.go
package domaintype Amount uinttype balance struct {
amount Amount
}func newBalance(amount Amount) *balance {
return &balance{amount:amount}
}func (b *balance) increase(amount Amount) {
b.amount += amount
}func (b *balance) decrease(amount Amount) {
b.amount -= amount
}func (b *balance) get() Amount {
return b.amount
}
message接口的实现
//message.go
package domainimport "fmt"type message interface {
sendTransferToMsg(to *accountIdInfo, amount Amount)
sendTransferFromMsg(from *accountIdInfo, amount Amount)
}const msgPhone = 0
type msgType intfunc newMessage(msgType msgType) message {
if msgType == msgPhone {
return &phone{}
}
return nil
}type phone struct {}func (p *phone) sendTransferToMsg(to *accountIdInfo, amount Amount) {
fmt.Println("phone: ", "send ", amount, " money to ", to.getAccountId())
}func (p *phone) sendTransferFromMsg(from *accountIdInfo, amount Amount) {
fmt.Println("phone: ", "receive ", amount, " money from ", from.getAccountId())
}
MoneySource类的实现 MoneySource类依赖于accountIdInfo类,balance类和message接口。
//money_source.go
package domainimport "fmt"type MoneySource struct {
accountIdInfo *accountIdInfo
balance *balance
message message
}func newMoneySource(accountIdInfo *accountIdInfo, balance *balance, message message) *MoneySource {
return &MoneySource{accountIdInfo:accountIdInfo, balance:balance, message:message}
}func (m *MoneySource) TransferMoneyTo(to *MoneyDestination, amount Amount) {
fmt.Println("start: ", m.accountIdInfo.getAccountId(), " has ", m.balance.get(), " money")
if m.balance.get() < amount {
panic("insufficient money!")
}
to.TransferMoneyFrom(m.accountIdInfo, amount)
m.balance.decrease(amount)
m.message.sendTransferToMsg(to.getAccountId(), amount)
fmt.Println("end: ", m.accountIdInfo.getAccountId(), " has ", m.balance.get(), " money")
}
MoneyDestination类的实现 MoneyDestination类依赖于accountIdInfo类,balance类和message接口。
//money_destination.go
package domainimport "fmt"type MoneyDestination struct {
accountIdInfo *accountIdInfo
balance *balance
message message
}func newMoneyDestination(accountIdInfo *accountIdInfo, balance *balance, message message) *MoneyDestination {
return &MoneyDestination{accountIdInfo:accountIdInfo, balance:balance, message:message}
}func (m *MoneyDestination) getAccountId() *accountIdInfo {
return m.accountIdInfo
}func (m *MoneyDestination) TransferMoneyFrom(from *accountIdInfo, amount Amount) {
fmt.Println("start: ", m.accountIdInfo.getAccountId(), " has ", m.balance.get(), " money")
m.balance.increase(amount)
m.message.sendTransferFromMsg(from, amount)
fmt.Println("end: ", m.accountIdInfo.getAccountId(), " has ", m.balance.get(), " money")
}
大对象的实现
Account对象的实现
//account.go
package domaintype Account struct {
accountIdInfo *accountIdInfo
balance *balance
message message
MoneySource *MoneySource
MoneyDestination *MoneyDestination
}func NewAccount(accountId AccountId, amount Amount) *Account {
account := &Account{}
account.accountIdInfo = newAccountIdInfo(accountId)
account.balance = newBalance(amount)
account.message = newMessage(msgPhone)
account.MoneySource = newMoneySource(account.accountIdInfo, account.balance, account.message)
account.MoneyDestination = newMoneyDestination(account.accountIdInfo, account.balance, account.message)
return account
}
AccountRepo的实现
//account_repo.go
package domainimport "sync"var inst *AccountRepo
var once sync.Oncetype AccountRepo struct {
accounts map[AccountId]*Account
lock sync.RWMutex
}func GetAccountRepo() *AccountRepo {
once.Do(func() {
inst = &AccountRepo{accounts: make(map[AccountId]*Account)}
})
return inst
}func (a *AccountRepo) Add(account *Account) {
a.lock.Lock()
a.accounts[account.accountIdInfo.getAccountId()] = account
a.lock.Unlock()
}func (a *AccountRepo) Get(accountId AccountId) *Account {
a.lock.RLock()
account := a.accounts[accountId]
a.lock.RUnlock()
return account
}func (a *AccountRepo) Remove(accountId AccountId) {
a.lock.Lock()
delete(a.accounts, accountId)
a.lock.Unlock()
}
API的实现
CreateAccount函数的实现
//create_account.go
package apiimport "transfer-money/domain"func CreateAccount(accountId domain.AccountId, amount domain.Amount) {
account := domain.NewAccount(accountId, amount)
repo := domain.GetAccountRepo()
repo.Add(account)
}
TransferMoney函数的实现
//transfer_money.go
package apiimport "transfer-money/domain"func TransferMoney(from domain.AccountId, to domain.AccountId, amount domain.Amount) {
repo := domain.GetAccountRepo()
src := repo.Get(from)
dst := repo.Get(to)
src.MoneySource.TransferMoneyTo(dst.MoneyDestination, amount)
}
测试
main函数的实现
//main.go
package mainimport "transfer-money/api"func main() {
api.CreateAccount("zhangsan123", 1000)
api.CreateAccount("lisi456", 200)
api.TransferMoney("zhangsan123", "lisi456", 300)
}
运行结果
start:zhangsan123has1000money
start:lisi456has200money
phone:receive300money fromzhangsan123
end:lisi456has500money
phone:send300money tolisi456
end:zhangsan123has700money
小结 本文以转账问题为例,给出了Golang中小类大对象的一种依赖注入实现,重点是Role的交织的姿势,希望对读者有一定的启发。
推荐阅读
- 星际无限|星际无限 | 官方推出Filecoin MinerX奖学金计划,吸引中小型Filecoin矿工
- 三门问题(蒙提霍尔悖论)分析与Golang模拟
- 智慧路上,你我同行|智慧路上,你我同行 ——名班主任万平工作室成员参加第三届中小学班级建设高峰论坛活动
- golang锁竞争性能
- 中国农业大学计算机就业薪资,2020年工资出炉,这个行业倒数第一,不过这类大学专业有金矿可挖...
- 基于rabbitmq实现的延时队列(golang版)
- 中小学机构补课为啥没效果
- 【golang】leetcode中级-字母异位词分组&无重复字符的最长子串
- 远离毒品
- 【golang】leetcode初级-有效的括号&缺失数字