包装类型的惯用方法是什么?

问题描述 投票:0回答:2

我想包装goquery.Selection以更方便地获取HTML和选择器字符串。

要访问goquery.Selection的方法,我应该在下面的代码中实现一些辅助方法,如Get()吗?

type MySelection goquery.Selection

// Without this helper method, I should always use type conversion
// to use goquery.Selection's methods.
func (s *MySelection) Get() *goquery.Selection {
    sel := s.(goquery.Selection)
    return sel
}

func (s *MySelection) HTML() string {
    // html, _ := s.Html() <- error
    html, _ := s.Get().Html()
    return html
}

func (s *MySelection) String() string {
    return fmt.Sprintf("%v#%v.%v",
        goquery.NodeName(s.Get()),
        s.Get().AttrOr("id", "(undefined)"),
        s.Get().AttrOr("class", "(undefined)"))
}

有没有更好的方法来处理这种情况?

go type-conversion
2个回答
2
投票

你也可以嵌入

type MySelection struct {
    goquery.Selection
    some payload //if needed
}

你将获得免费的mySelection的goquery.Selection方法,可以添加或覆盖一些。


1
投票

好吧,有几种方法可以“处理这个问题”。但是不要把它命名为Get()it isn't idiomatic

从最佳实践的角度来看,我建议:

  • 将代码与代码分离。
  • 实施反腐败层(包装其包装的包装)

原因很多。但是对于Go来说,最好保持简单 - 这归结为一个问题:你想要对代码进行单元测试吗?

如果答案是肯定的,那么我绝不会直接使用第三方包。我用他自己的界面包装他们的包。然后,在我的所有代码中使用(注入)该接口,以便允许我在单元测试中模拟它。

同样有几种模式和观点;但是,我将展示一个允许进行单元测试的包装器。

goquery_wrapper.go

package mypackage

import (
  "path/to/goquery.Selection"
)

var _mySelector *mySelector // Go stdlib uses underscores for private types

type mySelector interface {
  Html() string
  ...
}

type MySelector struct {
}

func (ms *MySelector) Html() {
  // your custom version
}

// initialize the global var with your wrapper
func init() {
  _mySelector = &MySelector{ ... }
}

foo.go

package mypackage

func Foo() {

  // uses the global var initialized with init()
  data := _mySelector.Html()

  // IoC using D.I. through constructors
  obj := NewSomething(_mySelector)

  // IoC using D.I. through methods
  result := bar.Process(_mySelector, "input data")
}

bar_test.go

package mypackage

import (
  "testing"
)

type mockSelector struct {
  HtmlWasCalled bool
  HtmlReturnsThis string
}

func (ms mockSelector) Html() string {
  ms.HtmlWasCalled = true
  return ms.HtmlReturnsThis
}

func TestBar(t *testing.T) {

  // arrange

  // override your global var
  oldMS := _mySelector
  _mySelector := &mockSelector{
    HtmlReturnsThis: "<b>success</b>",
  }

  // act

  // since foo.Bar is using the global var, it now uses
  // our mock we set above.
  result := foo.Bar("sample input")

  // assert
  if result != expected {
    t.Fail()
  }

  // put it back the way it was
  _mySelector = oldMS
}

func TestFoo(t *testing.T) {

  // arrange
  mock := &mockSelector{
    HtmlReturnsThis: "<b>success</b>",
  }

  // act

  // or, just inject your mock if using IoC
  result := bar.Process(mock, "sample input")

  // assert
  ...

}

这使我在单元测试期间不必处理第三方程序包的细微差别。效果很好,除非包的API很大。然后,我甚至质疑为什么我使用包开始,如果它是那么复杂。

热门问题
推荐问题
最新问题