手撸golang 结构型设计模式 门面模式

缘起

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

门面模式

门面模式(Facade Pattern)又叫作外观模式,提供了一个对立的接口,用来拜访子系统中的一群接口。其次要特色是定义了一个高层接口,让子系统更容易应用,属于结构型设计模式。
_

场景

  • 某在线商城, 推出了积分兑换礼品的性能
  • 兑换礼品有几个步骤, 波及到若干子系统:

    • 积分零碎, 检查用户积分是否足够
    • 库存零碎, 查看礼品是否有库存
    • 物流零碎, 安顿礼品发货并生成发货订单
  • 为简化业务层接口, 以门面模式设计对立的积分兑换API接口 - IGiftExchangeService

设计

  • GiftInfo: 礼品信息实体. 礼品也是一种库存物品.
  • GiftExchangeRequest: 积分兑换礼品申请
  • IGiftExchangeService: 积分兑换礼品服务, 该服务是一个Facade, 外部调用了多个子系统的服务
  • IPointsService: 用户积分治理服务的接口
  • IInventoryService: 库存治理服务的接口
  • IShippingService: 物流下单服务的接口
  • tMockGiftExchangeService: 积分兑换礼品服务的实现类
  • tMockPointsService: 用户积分治理服务的实现类
  • tMockInventoryService: 库存治理服务的实现类
  • tMockShippingService: 物流下单服务的实现类

单元测试

facade_pattern_test.go

package structural_patternsimport (    "learning/gooop/structural_patterns/facade"    "testing"    "time")func Test_FacadePattern(t *testing.T) {    iUserID := 1    iGiftID := 2    // 事后存入1000积分    e := facade.MockPointsService.SaveUserPoints(iUserID, 1000)    if e != nil {        t.Error(e)        return    }    // 事后存入1个库存    e = facade.MockInventoryService.SaveStock(iGiftID, 1)    if e != nil {        t.Error(e)        return    }    request := &facade.GiftExchangeRequest{        ID: 1,        UserID: iUserID,        GiftID: iGiftID,        CreateTime: time.Now().Unix(),    }    e, sOrderNo := facade.MockGiftExchangeService.Exchange(request)    if e != nil {        t.Log(e)    }    t.Logf("shipping order no = %v", sOrderNo)}

测试输入

$ go test -v facade_pattern_test.go === RUN   Test_FacadePattern    facade_pattern_test.go:36: shipping order no = shipping-order-666--- PASS: Test_FacadePattern (0.00s)PASSok      command-line-arguments  0.002s

GiftInfo.go

礼品信息实体

package facadetype GiftInfo struct {    ID int    Name string    Points int}func NewGiftInfo(id int, name string, points int) *GiftInfo {    return &GiftInfo{        id, name, points,    }}

GiftExchangeRequest.go

积分兑换礼品申请

package facadetype GiftExchangeRequest struct {    ID int    UserID int    GiftID int    CreateTime int64}

IGiftExchangeService.go

积分兑换礼品的接口, 该接口是为不便客户端调用的Facade接口

package facade// 礼品兑换服务type IGiftExchangeService interface {    // 兑换礼品, 并返回物流单号    Exchange(request *GiftExchangeRequest) (error, string)}

IPointsService.go

模仿用户积分治理服务的接口

package facade// 用户积分服务type IPointsService interface {    GetUserPoints(uid int) (error, int)    SaveUserPoints(uid int, points int) error}

IInventoryService.go

模仿库存治理服务的接口

package facade// 库存服务type IInventoryService interface {    GetGift(goodsID int) *GiftInfo    GetStock(goodsID int) (error, int)    SaveStock(goodsID int, num int) error}

IShippingService.go

模仿物流下单服务的接口

package facade// 物流下单服务type IShippingService interface {    CreateShippingOrder(uid int, goodsID int) (error, string)}

tMockGiftExchangeService.go

实现积分兑换礼品服务. 外部封装了积分服务, 库存服务和物流下单服务的调用.

package facadeimport "errors"type tMockGiftExchangeService struct {}func newMockGiftExchangeService() IGiftExchangeService {    return &tMockGiftExchangeService{}}var MockGiftExchangeService = newMockGiftExchangeService()// 模仿环境下未思考事务提交和回滚func (me *tMockGiftExchangeService) Exchange(request *GiftExchangeRequest) (error, string) {    gift := MockInventoryService.GetGift(request.GiftID)    if gift == nil {        return errors.New("gift not found"), ""    }    e, points := MockPointsService.GetUserPoints(request.UserID)    if e != nil {        return e, ""    }    if points < gift.Points {        return errors.New("insufficient user points"), ""    }    e, stock := MockInventoryService.GetStock(gift.ID)    if e != nil {        return e, ""    }    if stock <= 0 {        return errors.New("insufficient gift stock"), ""    }    e = MockInventoryService.SaveStock(gift.ID, stock-1)    if e != nil {        return e, ""    }    e = MockPointsService.SaveUserPoints(request.UserID, points - gift.Points)    if e != nil {        return e, ""    }    e,orderNo := MockShippingService.CreateShippingOrder(request.UserID, gift.ID)    if e != nil {        return e, ""    }    return nil, orderNo}

tMockPointsService.go

模仿实现用户积分治理服务

package facadeimport "errors"var MockPointsService = newMockPointsService()type tMockPointsService struct {    mUserPoints map[int]int}func newMockPointsService() IPointsService {    return &tMockPointsService{        make(map[int]int, 16),    }}func (me *tMockPointsService) GetUserPoints(uid int) (error, int) {    n,ok := me.mUserPoints[uid]    if ok {        return nil, n    } else {        return errors.New("user not found"), 0    }}func (me *tMockPointsService) SaveUserPoints(uid int, points int) error {    me.mUserPoints[uid] = points    return nil}

tMockInventoryService.go

模仿实现库存治理服务

package facadevar MockInventoryService = newMockInventoryService()type tMockInventoryService struct {    mGoodsStock map[int]int}func newMockInventoryService() IInventoryService {    return &tMockInventoryService{        make(map[int]int, 16),    }}func (me *tMockInventoryService) GetGift(id int) *GiftInfo {    return NewGiftInfo(id, "mock gift", 100)}func (me *tMockInventoryService) GetStock(goodsID int) (error, int) {    n,ok := me.mGoodsStock[goodsID]    if ok {        return nil, n    } else {        return nil, 0    }}func (me *tMockInventoryService) SaveStock(goodsID int, num int) error {    me.mGoodsStock[goodsID] = num    return nil}

tMockShippingService.go

模仿实现物流下单服务

package facadevar MockShippingService = newMockShippingService()type tMockShippingService struct {}func newMockShippingService() IShippingService {    return &tMockShippingService{}}func (me *tMockShippingService) CreateShippingOrder(uid int, goodsID int) (error, string) {    return nil, "shipping-order-666"}

门面模式小结

门面模式的长处
(1)简化了调用过程,不必深刻理解子系统,以防给子系统带来危险。
(2)缩小零碎依赖,涣散耦合。
(3)更好地划分拜访档次,进步了安全性。
(4)遵循迪米特法令
门面模式的毛病
(1)当减少子系统和扩大子系统行为时,可能容易带来未知危险。
(2)不合乎开闭准则。
(3)某些状况下,可能违反繁多职责准则。

(end)