手撸golang 架构设计原则 迪米特法则
缘起
最近复习设计模式
拜读谭勇德的<<设计模式就该这样学>>
该书以java语言演绎了常见设计模式
本系列笔记拟采用golang练习之
迪米特法则
迪米特法则(Law of Demeter, LoD)又叫作最少知道原则(Least KnowledgePrinciple, LKP),指一个对象应该对其他对象保持最少的了解,尽量降低类与类之间的耦合。
_
场景
- TeamLeader每天需要查看未完成的项目任务数
- TeamLeader指派TeamMember进行任务统计
- TeamMember提供对Task的汇总方法, 返回未完成的任务数
坏的设计:
- Leader: 我需要统计未完成任务数
- Member: 好的, 我可以统计, 但是任务清单在哪里呢?
- Leader: ... 我稍后给你吧
好的设计:
- Leader: 我需要统计未完成任务数
- Member: 好的. 任务清单我知道在那里, 我会搞定的
- Leader: 好兵!
Task.go
定义任务信息, 以及加载任务清单的方法
package law_of_demeter
type TaskStatus int
const OPENING TaskStatus = 0
const DONE TaskStatus = 1
const CANCLED TaskStatus = 2
const DENIED TaskStatus = 3
type Task struct {
iID int
iStatus TaskStatus
}
func NewTask(id int, status TaskStatus) *Task {
return &Task{
id,
status,
}
}
func (me *Task) ID() int {
return me.iID
}
func (me *Task) Status() TaskStatus {
return me.iStatus
}
func LoadTaskList() []*Task {
tasks := make([]*Task, 0)
tasks = append(tasks, NewTask(1, OPENING))
tasks = append(tasks, NewTask(2, DONE))
tasks = append(tasks, NewTask(3, CANCLED))
tasks = append(tasks, NewTask(4, DENIED))
return tasks
}
ITeamLeader.go
定义TeamLeader的接口
package law_of_demeter
type ITeamLeader interface {
CountOpeningTasks() int
}
BadTeamLeader.go
不好的ITeamLeader实现, 同时耦合了Task和BadTeamMember两个类
package law_of_demeter
import "fmt"
type BadTeamLeader struct {
iID int
sName string
}
func (me *BadTeamLeader) CountOpeningTasks() int {
tasks := LoadTaskList()
member := NewBadTeamMember(11, "王Member")
sum := member.countOpeningTasks(tasks)
fmt.Printf("%v CountOpeningTasks, got %v", me.sName, sum)
return sum
}
BadTeamMember.go
不好的示例. 统计任务数的实现, 要求过多的参数, 增加调用方的耦合度和使用难度
package law_of_demeter
type BadTeamMember struct {
iID int
sName string
}
func NewBadTeamMember(id int, name string) *BadTeamMember {
return &BadTeamMember{
id,
name,
}
}
func (me *BadTeamMember) countOpeningTasks(lstTasks []*Task) int {
sum := 0
for _,it := range lstTasks {
if it.Status() == OPENING {
sum++
}
}
return sum
}
GoodTeamLerader.go
更好的ITeamLeader实现, 只依赖了GoodTeamMember
package law_of_demeter
import "fmt"
type GoodTeamLeader struct {
iID int
sName string
}
func (me *GoodTeamLeader) CountOpeningTasks() int {
member := NewGoodTeamMember(11, "王Member")
sum := member.countOpeningTasks()
fmt.Printf("%v CountOpeningTasks, got %v", me.sName, sum)
return sum
}
GoodTeamMember.go
更好的TeamMember, 对外屏蔽了任务列表的获取细节
package law_of_demeter
type GoodTeamMember struct {
iID int
sName string
}
func NewGoodTeamMember(id int, name string) *GoodTeamMember {
return &GoodTeamMember{
id,
name,
}
}
func (me *GoodTeamMember) countOpeningTasks() int {
sum := 0
tasks := LoadTaskList()
for _,it := range tasks {
if it.Status() == OPENING {
sum++
}
}
return sum
}
law_of_demeter_test.go
单元测试
package main
import "testing"
import (lod "learning/gooop/principles/law_of_demeter")
func Test_LOD(t *testing.T) {
bl := lod.NewBadTeamLeader(1, "张Leader")
bl.CountOpeningTasks()
gl := lod.NewGoodTeamLeader(2, "李Leader")
gl.CountOpeningTasks()
}
测试输出
$ go test -v law_of_demeter_test.go
=== RUN Test_LOD
张Leader CountOpeningTasks, got 1
李Leader CountOpeningTasks, got 1
--- PASS: Test_LOD (0.00s)
PASS
ok command-line-arguments 0.002s