手撸golang 架构设计原则 迪米特法则

Posted ioly

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了手撸golang 架构设计原则 迪米特法则相关的知识,希望对你有一定的参考价值。

手撸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

以上是关于手撸golang 架构设计原则 迪米特法则的主要内容,如果未能解决你的问题,请参考以下文章

手撸golang 架构设计原则 单一职责原则

手撸golang 架构设计原则 依赖倒置原则

手撸golang 架构设计原则 接口隔离原则

手撸golang 架构设计原则 里氏替换原则

手撸golang 架构设计原则 开闭原则

设计原则之迪米特法则的概念及实例代码操作