在你的GUI架构中,使用This interface{}字段和类型断言确实不是最优的方案,这破坏了类型安全并增加了运行时错误的风险。以下是几种更优雅的实现方案:
方案1:使用接口定义组件行为
type Widget interface {
GetPosition() (int, int)
SetPosition(x, y int)
GetParent() *Window
HandleKeyPress(event KeyEvent)
IsFocused() bool
GetType() WidgetType
}
type TextBox struct {
widgetBase
Text string
}
func (t *TextBox) HandleKeyPress(event KeyEvent) {
if t.IsFocused() && event.Char != 0 {
t.Text += string(event.Char)
}
}
// 事件处理代码
for _, child := range window.Children {
child.HandleKeyPress(event)
}
方案2:嵌入具体类型并重写方法
type Widget struct {
X, Y int
Parent *Window
Type WidgetType
}
type TextBox struct {
Widget
Text string
}
func (t *TextBox) HandleKeyPress(event KeyEvent) {
if t.IsFocused() && event.Char != 0 {
t.Text += string(event.Char)
}
}
// 事件分发
for _, child := range window.Children {
switch w := child.(type) {
case *TextBox:
w.HandleKeyPress(event)
case *Button:
w.HandleKeyPress(event)
// 其他组件类型
}
}
方案3:使用函数字段和类型安全的包装器
type Widget struct {
X, Y int
Parent *Window
Type WidgetType
// 类型安全的处理器
keyPressHandler func(event KeyEvent)
}
type TextBox struct {
Widget
Text string
}
func NewTextBox() *TextBox {
tb := &TextBox{
Widget: Widget{Type: TextBoxType},
Text: "",
}
tb.keyPressHandler = tb.handleKeyPress
return tb
}
func (t *TextBox) handleKeyPress(event KeyEvent) {
if t.IsFocused() && event.Char != 0 {
t.Text += string(event.Char)
}
}
// 事件处理
for _, child := range window.Children {
if child.keyPressHandler != nil {
child.keyPressHandler(event)
}
}
方案4:使用访问者模式
type WidgetVisitor interface {
VisitTextBox(tb *TextBox)
VisitButton(btn *Button)
// 其他组件访问方法
}
type Widget interface {
Accept(visitor WidgetVisitor)
}
type KeyEventVisitor struct {
Event KeyEvent
}
func (v *KeyEventVisitor) VisitTextBox(tb *TextBox) {
if tb.IsFocused() && v.Event.Char != 0 {
tb.Text += string(v.Event.Char)
}
}
// 在TextBox中实现Accept
func (t *TextBox) Accept(visitor WidgetVisitor) {
visitor.VisitTextBox(t)
}
// 事件处理
visitor := &KeyEventVisitor{Event: event}
for _, child := range window.Children {
child.Accept(visitor)
}
推荐使用方案1或方案2,它们提供了更好的类型安全性和代码可维护性。方案1通过接口完全抽象了组件行为,方案2在保持简单性的同时提供了类型安全的方法调用。