关于golang:手撸golang-架构设计原则-接口隔离原则

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

缘起

最近温习设计模式
拜读谭勇德的<<设计模式就该这样学>>
本系列笔记拟采纳golang练习之

接口隔离准则

接口隔离准则(Interface Segregation Principle, ISP)指用多个专门的接口,而不应用繁多的总接口,客户端不应该依赖它不须要的接口。设计接口时,该当留神以下几点:
(1)一个类对另一个类的依赖应该建设在最小接口上。
(2)建设繁多接口,不要建设宏大臃肿的接口。
(3)尽量细化接口,接口中的办法尽量少。
_

场景

  • 设计一个动物接口
  • 不同动物可能有eat(), fly(), swim()等办法
  • 设计实现动物接口的Bird类和Dog类

IBadAnimal.go

不好的接口设计, 接口办法很多, 比拟臃肿, 须要实现接口时累赘很重

package interface_segregation

type IBadAnimal interface {
    ID() int
    Name() string

    Eat() error
    Fly() error
    Swim() error
}

BadBird.go

BadBird实现了IBadAnimal接口.
BadBird是不反对Swim()的, 但因为接口要求, 只能返回无意义的谬误应酬.

package interface_segregation

import (
    "errors"
    "fmt"
)

type BadBird struct {
    iID int
    sName string
}

func NewBadBird(id int, name string) IBadAnimal {
    return &BadBird{
        iID: id,
        sName: name,
    }
}

func (me *BadBird) ID() int {
    return me.iID
}

func (me *BadBird) Name() string {
    return me.sName
}

func (me *BadBird) Eat() error {
    fmt.Printf("%v/%v is eating\n", me.Name(), me.ID())
    return nil
}

func (me *BadBird) Fly() error {
    fmt.Printf("%v/%v is flying\n", me.Name(), me.ID())
    return nil
}

func (me *BadBird) Swim() error {
    return errors.New(fmt.Sprintf("%v/%v cannot swimming", me.Name(), me.ID()))
}

BadDog.go

BadDog实现IBadAnimal接口.
原本BadDog是不反对Fly()办法的, 但因为接口要求, 因而只能返回无意义谬误.

package interface_segregation

import (
    "errors"
    "fmt"
)

type BadDog struct {
    iID int
    sName string
}


func NewBadDog(id int, name string) IBadAnimal {
    return &BadDog{
        iID: id,
        sName: name,
    }
}

func (me *BadDog) ID() int {
    return me.iID
}

func (me *BadDog) Name() string {
    return me.sName
}

func (me *BadDog) Eat() error {
    fmt.Printf("%v/%v is eating\n", me.Name(), me.ID())
    return nil
}

func (me *BadDog) Fly() error {
    return errors.New(fmt.Sprintf("%v/%v cannot fly", me.Name(), me.ID()))
}

func (me *BadDog) Swim() error {
    fmt.Printf("%v/%v is swimming\n", me.Name(), me.ID())
    return nil
}

IGoodAnimal.go

更好的接口设计. 将动物接口拆分为根本信息接口IGoodAnimal, 以及三个可选的能力接口:
ISupportEat, ISupportFly, ISupportSwim

package interface_segregation


type IGoodAnimal interface {
    ID() int
    Name() string
}

type ISupportEat interface {
    Eat() error
}

type ISupportFly interface {
    Fly() error
}

type ISupportSwim interface {
    Swim() error
}

GoodAnimalInfo.go

实现IGoodAnimal接口, 提供动物的id,name等根本属性

package interface_segregation

type GoodAnimalInfo struct {
    iID int
    sName string
}


func (me *GoodAnimalInfo) ID() int {
    return me.iID
}

func (me *GoodAnimalInfo) Name() string {
    return me.sName
}

GoodBird.go

更好的Bird实现, 异味代码更少.
通过集成GoodAnimalInfo实现IGoodAnimal接口, 并选择性实现ISupportEat, ISupportFly.

package interface_segregation

import "fmt"

type GoodBird struct {
    GoodAnimalInfo
}

func NewGoodBird(id int, name string) IGoodAnimal {
    return &GoodBird{
        GoodAnimalInfo{
            id,
            name,
        },
    }
}

func (me *GoodBird) Eat() error {
    fmt.Printf("%v/%v is eating\n", me.Name(), me.ID())
    return nil
}

func (me *GoodBird) Fly() error {
    fmt.Printf("%v/%v is flying\n", me.Name(), me.ID())
    return nil
}

GoodDog.go

更好的Dog实现, 异味代码更少.
通过集成GoodAnimalInfo实现IGoodAnimal接口, 并选择性实现ISupportEat, ISupportSwim.

package interface_segregation

import "fmt"

type GoodDog struct {
    GoodAnimalInfo
}

func NewGoodDog(id int, name string) IGoodAnimal {
    return &GoodDog{
        GoodAnimalInfo{
            id,
            name,
        },
    }
}

func (me *GoodDog) Eat() error {
    fmt.Printf("%v/%v is eating\n", me.Name(), me.ID())
    return nil
}

func (me *GoodDog) Swim() error {
    fmt.Printf("%v/%v is swimming\n", me.Name(), me.ID())
    return nil
}

interface_segregation_test.go

单元测试

package main

import (
    isp "learning/gooop/principles/interface_segregation"
    "testing"
)

func Test_ISP(t *testing.T) {
    fnLogIfError := func(fn func() error) {
        e := fn()
        if e != nil {
            t.Logf("error = %s\n", e.Error())
        }
    }

    fnTestBadAnimal := func (a isp.IBadAnimal) {
        fnLogIfError(a.Eat)
        fnLogIfError(a.Fly)
        fnLogIfError(a.Swim)
    }

    fnTestBadAnimal(isp.NewBadBird(1, "BadBird"))
    fnTestBadAnimal(isp.NewBadDog(2, "BadDog"))


    fnTestGoodAnimal := func(a isp.IGoodAnimal) {
        if it,ok := a.(isp.ISupportEat);ok {
            fnLogIfError(it.Eat)
        } else {
            t.Logf("%v/%v cannot eat", a.Name(), a.ID())
        }

        if it,ok := a.(isp.ISupportFly);ok {
            fnLogIfError(it.Fly)
        } else {
            t.Logf("%v/%v cannot fly", a.Name(), a.ID())
        }

        if it,ok := a.(isp.ISupportSwim);ok {
            fnLogIfError(it.Swim)
        } else {
            t.Logf("%v/%v cannot swim", a.Name(), a.ID())
        }
    }

    fnTestGoodAnimal(isp.NewGoodBird(11, "GoodBird"))
    fnTestGoodAnimal(isp.NewGoodDog(12, "GoodDog"))
}

测试输入

$ go test -v interface_segregation_test.go 
=== RUN   Test_ISP
BadBird/1 is eating
BadBird/1 is flying
    interface_segregation_test.go:12: error = BadBird/1 cannot swimming
BadDog/2 is eating
    interface_segregation_test.go:12: error = BadDog/2 cannot fly
BadDog/2 is swimming
GoodBird/11 is eating
GoodBird/11 is flying
    interface_segregation_test.go:42: GoodBird/11 cannot swim
GoodDog/12 is eating
    interface_segregation_test.go:36: GoodDog/12 cannot fly
GoodDog/12 is swimming
--- PASS: Test_ISP (0.00s)
PASS
ok      command-line-arguments  0.002s

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理