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

ChongTuWang
• 阅读 3241

手撸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
点赞
收藏
评论区
推荐文章
美凌格栋栋酱 美凌格栋栋酱
6个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
java之设计模式
看了设计模式,感觉自己很多不理解什么意思,通过看博客别人写的理解,总结了一下,方便查阅。一、设计模式六大原则1、单一职责原则:定义:应该有且只有一个原因引起类的变化。注意:这里的类不光指类,也适用于方法和接口,比如我们常说的一个方法实现一个功能。2、开放封闭原则:定义:类、模块、函数等
zdd小小菜鸟 zdd小小菜鸟
2年前
设计模式–六大原则
设计模式–六大原则tex在238中设计模式中,我们提炼出了六大面向对象设计原则我们可以不知道那数量繁多的设计模式,但一定要记住这六大设计原则1.开闭原则(OpenClosePrinciple
zdd小小菜鸟 zdd小小菜鸟
2年前
设计模式面试
设计模式面试1.单例设计模式使用设计模式为了代码复用,增加可维护性。设计模式的六大原则:开闭原则、里氏代换原则、依赖倒转原则、接口隔离原则、迪米特法则(最少知道原则)、合成/聚合复用原则Singleton(创建):保证一个类仅
架构师日记-深入理解软件设计模式 | 京东云技术团队
本文从设计模式与编程语言的关系,设计模式与架构模式的区别,设计原则和设计模式的关系等几个维度进行了分析和解答。关于设计模式应该如何学习和应用的问题,给出了学习意见和实践心得。
xiguaapp xiguaapp
4年前
一句话总结java七大设计原则
开闭原则:对扩展开放,对修改关闭。依赖倒置原则:高层应该不依赖地层。单一职责原则:一个类只干一件事儿。接口隔离原则:一个接口只干一件事儿迪米特法则:不该知道的就不要知道。里氏替换原则:子类重写方法功能发生改变,但是不影响父类方法的语义。合成复用原则:尽量使用组合实现代码复用,不要用继承,要解耦。
待兔 待兔
3年前
彻底弄懂类设计原则之 - 单一职责原则
类设计原则之单一职责类的设计原则之单一职责原则,是最常用的类的设计的原则之一英文:SRP,SimpleResponsibilityPrinciple中文:单一职责原则这是面向对象类设计中的第一个原则,也是看起来最简单的一个原则,但是实际上远远没有这么简单,很多人不一定真正理解了!类设计,通俗的讲就是怎么去写一个类更好,应该遵循什么样的原则
马丁路德 马丁路德
4年前
React 之设计原则
编写该文档的目的是,使开发者更易于了解我们如何决策React(应该做哪些,不应该做哪些),以及我们的开发理念。我们非常欢迎来自社区的贡献,但如若违背这些理念,实非我们所愿。注意:文章描述了React自身的设计原则,而非React组件或应用,阅读者需要对React有深入的理解。如需React的入门文档,查看。
待兔 待兔
3年前
彻底弄懂类设计原则之 - 开闭原则
类设计原则之开闭原则OCP,OpenClosedPrinciple,中文翻译为开闭原则当第一次看到OCP时,我就蒙了,什么开,什么闭啊,完全摸不到头脑然后就去网上找各种博客,各种解释,最多的一种就是:对扩展开放,对修改关闭当时我就更蒙了,去问了很多“高手”,他的回答让我更加迷茫:不修改代码就可以增加新功能这是多么神奇的事啊,不修改代码
Stella981 Stella981
3年前
From Apprentice To Artisan 翻译 19
上一篇(https://my.oschina.net/zgldh/blog/388610)InterfaceSegregationPrinciple接口隔离原则Introduction介绍TheInterfaceSegre
高可用 - 隔离原则
前言当讨论高可用时,那么必然有与之对应的低可用甚至不可用,但无论是哪种可用描述,其中都暗含了一个大众共识,即不存在永久稳定运行的系统程序。事实上,几十年前图灵也论证过类似的问题,称为“停机问题”,具体的描述是:能否为A计算机编程,使得程序才能在有限时间内推