douyun7718 2018-09-12 14:41
浏览 112
已采纳

Golang中的工厂功能[关闭]

I have a service object similar to the one shown below which is exposed through HTTP:

type ComputeService struct {

}

func (svc ComputeService) Compute(userType string, data Data) (Result, error) {
   // rate limit based on userType (both check and increment counter)
   // if not rate limited, compute and return result
}

Now, as you can see rate-limiting needs to be done based on userType. Implementation of rate limiter itself changes according to userType. I am thinking of 2 approaches of resolving the rate limiter using userType.

// First approach: for each request new ComputeService object is
// created after resolving RateLimiter using userType in the handler
// itself
type ComputeService struct {
    RateLimiter RateLimiter
}

// Second approach: ComputeService object is initialized during startup
// and only RateLimiter is resolved with every Compute() call by calling
// the provider function
type ComputeService struct {
    RateLimiterProvider func(userType string) RateLimiter
}

Both are testable. Which one would be preferable ? I'm leaning towards the 2nd approach since using it would mean that the handler will be purely read request, delegate to service, write out the response whereas in 1st approach, handler would contain additional step of resolving the rate limiter implementation.

  • 写回答

1条回答 默认 最新

  • doupishan3309 2018-09-12 15:13
    关注

    If you are using a DI system like Dargo you can use its Provider injection to dynamically choose the implementation at runtime.

    In that case your services would look something like the following:

    import "github.com/jwells131313/dargo/ioc"
    
    type RateLimiter interface {
    }
    
    type UserType1RateLimiter struct {
    }
    
    type UserType2RateLimiter struct {
    }
    
    type ComputeService struct {
        RateLimiterProvider ioc.Provider `inject:"RateLimiter"`
    }
    
    func (svc ComputeService) Compute(userType string, data Data) (Result, error) {
        // rate limit based on userType (both check and increment counter)
        // if not rate limited, compute and return result
        raw, err := svc.RateLimiterProvider.QualifiedBy(userType).Get()
        if err != nil {
            return nil, err
        }
    
        limiter := raw.(RateLimiter)
        //...
    }  
    

    This is how you would bind these into the ServiceLocator:

    func initializer() {
        serviceLocator, _ = ioc.CreateAndBind("ExampleLocator", func(binder ioc.Binder) error {
            binder.Bind("RateLimiter", UserType1RateLimiter{}).QualifiedBy("UserType1")
            binder.Bind("RateLimiter", UserType2RateLimiter{}).QualifiedBy("UserType2")
            binder.Bind("ComputeService", ComputeService{})
            return nil
        })
    }
    

    This only applies when you are using something like Dargo, but it still might be useful in your case.

    If you are not using Dargo it seems to me to be a matter of opinion, although I personally would choose the second approach

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 uniapp uview http 如何实现统一的请求异常信息提示?
  • ¥15 有了解d3和topogram.js库的吗?有偿请教
  • ¥100 任意维数的K均值聚类
  • ¥15 stamps做sbas-insar,时序沉降图怎么画
  • ¥15 买了个传感器,根据商家发的代码和步骤使用但是代码报错了不会改,有没有人可以看看
  • ¥15 关于#Java#的问题,如何解决?
  • ¥15 加热介质是液体,换热器壳侧导热系数和总的导热系数怎么算
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line