美文网首页程序员设计模式
手撸golang 结构型设计模式 门面模式

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

作者: 老罗话编程 | 来源:发表于2021-02-01 21:11 被阅读0次

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

    缘起

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

    门面模式

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

    场景

    • 某在线商城, 推出了积分兑换礼品的功能
    • 兑换礼品有几个步骤, 涉及到若干子系统:
      • 积分系统, 检查用户积分是否足够
      • 库存系统, 检查礼品是否有库存
      • 物流系统, 安排礼品发货并生成发货订单
    • 为简化业务层接口, 以门面模式设计统一的积分兑换API接口 - IGiftExchangeService

    设计

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

    单元测试

    facade_pattern_test.go

    package structural_patterns
    
    import (
        "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)
    PASS
    ok      command-line-arguments  0.002s
    

    GiftInfo.go

    礼品信息实体

    package facade
    
    type 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 facade
    
    type 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 facade
    
    import "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 facade
    
    import "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 facade
    
    
    var 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 facade
    
    var 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)

    相关文章

      网友评论

        本文标题:手撸golang 结构型设计模式 门面模式

        本文链接:https://www.haomeiwen.com/subject/nubstltx.html