我试图更好地理解如何在 Go 中使用 protected 空间。我来自 java,这意味着我可以通过 protected 继承访问值,因为这里只有组合,我想确保我走在正确的道路上。
问题:我想在子实现中设置一个值,但不在通用接口(interface)上公开一个 setter 。
当确实没有层次结构时,为“子类”提供 setter 的最佳方法是什么?
这意味着我想要:
type Bottom interface {
GetYouSome()
// rote things
SetSpeed(int)
DeliveryMechanism() chan string
}
请注意,没有 SetDeliveryMechanism(chan string) 方法。
我想我会从一个实现死记硬背的基础开始,这意味着实际的实现将提供“GetYouSome”方法。我也希望它们存在于不同的包中。将有许多实现,我希望命名空间被沙盒化(即它们都可以使用常量 DefaultPort)。
为了说明这个问题,我做了一个小项目。它的布局如下:
.
└── src
├── main.go
└── parent
├── child
│ └── child.go
└── parent.go
在 child.go 中,我们实际上创建了一些类型的 Bottom,但在 parent.go 中,我们实际上定义了样板代码( setter / getter )。问题是我无法在任何地方实例化 channel !
理想的实现应该是这样的:
//------------- parent.go -------------
package parent
type Bottom interface {
GetYouSome()
// rote things
SetSpeed(int)
DeliveryMechanism() chan string
}
// Intended to implement the boring things
type GenericBottom struct {
speed int
deliveryChan chan string
}
func (bot *GenericBottom) SetSpeed(speed int) {
bot.speed = speed
}
func (bot GenericBottom) DeliveryMechanism() chan string {
return bot.deliveryChan
}
//------------- child.go -------------
package child
import "parent"
func New(speed int) parent.Bottom {
impl := new(Composite)
impl.name = "simple"
impl.SetSpeed(speed)
// illegal! not exported
// impl.deliveryChannel = make(chan string)
return impl
}
// intended so that we can seamlessly treat the Composite
// as a Bottom
type Composite struct {
parent.GenericBottom
name string
}
func (a Composite) GetYouSome() {
fmt.Println("Inside the actual implementation")
}
我认为有两种方法可以解决这个问题。
(1) 创建一个子类来包装 GenericBottom 类,传递所有方法。真令人难过,它仍然存在我无法直接访问 deliveryChannel 类的问题。我必须在父包中构建一个 new 构造函数,然后在子类中显式设置实例。
//------------- parent.go -------------
func NewGenericBottom() GenericBottom {
return GenericBottom{0, make(chan string)}
}
//------------- child.go -------------
func New(speed int) parent.Bottom {
impl := new(ExplicitComposite)
impl.name = "explicit"
// now I can set the channel? Nope
// impl.gb = parent.GenericBottom{speed, make(chan string)}
impl.gb = parent.NewGenericBottom()
impl.SetSpeed(speed)
return impl
}
// this has to pass through each method
type ExplicitComposite struct {
gb parent.GenericBottom
name string
}
func (e ExplicitComposite) GetYouSome() {
fmt.Println("Inside the explicit implementation")
}
func (e ExplicitComposite) DeliveryMechanism() chan string {
return e.gb.DeliveryMechanism()
}
func (e *ExplicitComposite) SetSpeed(speed int) {
e.gb.SetSpeed(speed)
}
或者! (2) 我可以在 GenericBottom 上添加一个 setter 方法。但是任何使用 GenericBottom 的人都可以转换为访问权限吗?我不会真的受到“保护”。
像这样:
//------------- parent.go -------------
func (bot *GenericBottom) SetChannel(c chan string) {
bot.deliveryChan = c
}
//------------- child.go -------------
type CheatersBottom struct {
parent.GenericBottom
name string
}
func (a CheatersBottom) GetYouSome() {
fmt.Println("Inside the cheaters bottom")
}
func NewCheatersBottom(speed int) parent.Bottom {
impl := new(CheatersBottom)
impl.SetChannel(make(chan string))
impl.SetSpeed(speed)
return impl
}
当确实没有层次结构时,为“子类”提供 setter 的最佳方法是什么?
最佳答案
您的主要问题是您是一名 Java 程序员。我的意思不是刻薄或中伤 Java 程序员;我的意思是,Java 中的通用思维方式与您在 Go 中的设计思维方式完全不同。也许不如尝试用 C 编写 Haskell 代码那么多,但它们仍然是完全不同的思维方式。
我已经写了很多草稿试图“修复”你的代码,但你设计它的方式与没有真正概念继承的语言根本不一致。如果你发现自己曾经在 Go 中使用过“base”或“parent”这些词,甚至在某种程度上使用过“generic”这个词,你可能会与类型系统大打出手。我认为这是大多数从 OO 语言转向 Go 的人必须经历的阶段。
我建议查看 Go 标准库并查看它们如何布置包。通常,您会发现以下内容: 一个包将定义一个或多个接口(interface),以及在这些接口(interface)上运行的函数。然而,接口(interface)的实际具体实现的数量要么不存在,要么非常少。然后,在另一个包中,他们提供实用程序的具体实现。最引人注目的是,与您的代码相比,永远不会有任何部分实现,除非实现一个接口(interface)自动部分实现另一个接口(interface)。
您的 GenericBottom 不一定是“错误”的,我当然自己制作了隐藏的伪抽象类,但在 Go 中,您想要的是将它们不导出为 genericBottom,并且在与所有具体实现相同的包中。那或在internal package从 Go 1.4 开始。
您现在可能会说,“但是如果我需要在别处定义我的接口(interface)的其他具体实现怎么办?”好吧,您将复制代码。这可以被视为(并且可能是)Go 类型系统的弱点,但在不同的包中实现接口(interface)时重复一些代码绝对是惯用的和常见的。这就是像 internal 这样的东西是为了减轻一些。
另外请注意,不要将包视为类。一开始我也有这个问题。大中型的包里面有10个以上的文件是比较不起眼的。只要它们是一个单一的概念单元,那就是它们的用途。这就是通过让类型共享一个包来在 Go 中实现“ protected ”功能的方式(实际上更像是默认/无修饰符)。他们都可以访问彼此未导出的字段。
最后一条建议是不要从 NewX 方法返回接口(interface)。你应该(几乎)总是返回一个指向结构的指针。通常你想要构造一个类型,并将它作为接口(interface)传递给另一个方法,而不是首先接收接口(interface)。
我建议完全重写。挑战自己;每次你想到“ child 类”这样的词时,停下来做点别的事情。看看你能想出什么。我认为您会发现,虽然一开始有点困难,但通过以与类型系统协作的方式编写代码,您最终会完成更多工作,即使这会导致一些代码卡顿。
关于inheritance - 使用设置保护值的 Golang 继承,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32533992/
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在使用omniauth/openid时遇到了一些麻烦。在尝试进行身份验证时,我在日志中发现了这一点:OpenID::FetchingError:Errorfetchinghttps://www.google.com/accounts/o8/.well-known/host-meta?hd=profiles.google.com%2Fmy_username:undefinedmethod`io'fornil:NilClass重要的是undefinedmethodio'fornil:NilClass来自openid/fetchers.rb,在下面的代码片段中:moduleNetclass
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h