如何在 Swift 中使用访客模式减少样板文件?
How can I reduce boilerplate with the visitor pattern in Swift?
我正在 Swift 2.2 中为工作项目实现访问者模式。
这样我就不必精简我的源代码并节省时间,我将使用 example of visitor pattern in swift by Oktawian Chojnacki.
protocol PlanetVisitor {
func visit(planet: PlanetAlderaan)
func visit(planet: PlanetCoruscant)
func visit(planet: PlanetTatooine)
}
protocol Planet {
func accept(visitor: PlanetVisitor)
}
class PlanetAlderaan: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class PlanetCoruscant: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class PlanetTatooine: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class NameVisitor: PlanetVisitor {
var name = ""
func visit(planet: PlanetAlderaan) { name = "Alderaan" }
func visit(planet: PlanetCoruscant) { name = "Coruscant" }
func visit(planet: PlanetTatooine) { name = "Tatooine" }
}
我一直试图解决的问题是减少从 Planet
派生的每个 class 上的样板文件。
如您所见,它们都具有重复的相同功能 func accept(visitor: PlanetVisitor) { visitor.visit(self) }
.
我已经尝试在 Planet
协议上放置一个默认实现,并在基础 class 和 Swift 上实现它,由于编译时重载解析,似乎不允许它。
示例:
协议的默认实现:
extension Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
基础Class:
class PlanetBase: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class PlanetAlderaan: PlanetBase {}
class PlanetCoruscant: PlanetBase {}
class PlanetTatooine: PlanetBase {}
有谁知道可以使 accept
函数通用并自动应用于从 Planet
派生的每个具体 class 的方法?这不是一个关键问题,但却是一个很大的难题!
简短回答:不可能,这是设计使然。
访问者模式适用于行星数量稳定但访问者数量未知的情况。因此,您计划在访问者中进行未来的扩展,一次编写此样板。然后可以在不更改行星的情况下添加更多访问者。
在大型项目中,您可能会使用代码生成。
不推荐,您的选择是直接切换行星,不需要样板代码:
func foo(planet: Planet) {
if planet is PlanetAlderaan {
name = "Alderaan"
}
else if planet is PlanetCoruscant {
name = "Coruscant"
}
else if planet is PlanetTatooine {
name = "Tatooine"
}
}
这是 error-prone,因为您很容易忘记行星。访问者模式强制您为所有情况编写代码,否则将无法编译。
阅读@paiv 的回答我知道你可以减少样板代码同时避免忘记大小写问题:
enum Planet {
case alderaan
case coruscant
case tatooine
func accept(visitor: PlanetVisitor) {
visitor.visit(planet: self)
}
}
protocol PlanetVisitor {
func visit(planet: Planet)
}
class NameVisitor: PlanetVisitor {
var name = ""
func visit(planet: Planet) {
switch planet {
case .alderaan:
name = "Alderaan"
case .coruscant:
name = "Coruscant"
case .tatooine:
name = "Tatooine"
}
}
}
如果您不在 switch
中使用 default
,则可以保证编译器不会让代码在未处理的情况下进行编译。
但我认为其他一些样板文件可能会迁移到 Planet
类型中。
我正在 Swift 2.2 中为工作项目实现访问者模式。
这样我就不必精简我的源代码并节省时间,我将使用 example of visitor pattern in swift by Oktawian Chojnacki.
protocol PlanetVisitor {
func visit(planet: PlanetAlderaan)
func visit(planet: PlanetCoruscant)
func visit(planet: PlanetTatooine)
}
protocol Planet {
func accept(visitor: PlanetVisitor)
}
class PlanetAlderaan: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class PlanetCoruscant: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class PlanetTatooine: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class NameVisitor: PlanetVisitor {
var name = ""
func visit(planet: PlanetAlderaan) { name = "Alderaan" }
func visit(planet: PlanetCoruscant) { name = "Coruscant" }
func visit(planet: PlanetTatooine) { name = "Tatooine" }
}
我一直试图解决的问题是减少从 Planet
派生的每个 class 上的样板文件。
如您所见,它们都具有重复的相同功能 func accept(visitor: PlanetVisitor) { visitor.visit(self) }
.
我已经尝试在 Planet
协议上放置一个默认实现,并在基础 class 和 Swift 上实现它,由于编译时重载解析,似乎不允许它。
示例:
协议的默认实现:
extension Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
基础Class:
class PlanetBase: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class PlanetAlderaan: PlanetBase {}
class PlanetCoruscant: PlanetBase {}
class PlanetTatooine: PlanetBase {}
有谁知道可以使 accept
函数通用并自动应用于从 Planet
派生的每个具体 class 的方法?这不是一个关键问题,但却是一个很大的难题!
简短回答:不可能,这是设计使然。
访问者模式适用于行星数量稳定但访问者数量未知的情况。因此,您计划在访问者中进行未来的扩展,一次编写此样板。然后可以在不更改行星的情况下添加更多访问者。
在大型项目中,您可能会使用代码生成。
不推荐,您的选择是直接切换行星,不需要样板代码:
func foo(planet: Planet) {
if planet is PlanetAlderaan {
name = "Alderaan"
}
else if planet is PlanetCoruscant {
name = "Coruscant"
}
else if planet is PlanetTatooine {
name = "Tatooine"
}
}
这是 error-prone,因为您很容易忘记行星。访问者模式强制您为所有情况编写代码,否则将无法编译。
阅读@paiv 的回答我知道你可以减少样板代码同时避免忘记大小写问题:
enum Planet {
case alderaan
case coruscant
case tatooine
func accept(visitor: PlanetVisitor) {
visitor.visit(planet: self)
}
}
protocol PlanetVisitor {
func visit(planet: Planet)
}
class NameVisitor: PlanetVisitor {
var name = ""
func visit(planet: Planet) {
switch planet {
case .alderaan:
name = "Alderaan"
case .coruscant:
name = "Coruscant"
case .tatooine:
name = "Tatooine"
}
}
}
如果您不在 switch
中使用 default
,则可以保证编译器不会让代码在未处理的情况下进行编译。
但我认为其他一些样板文件可能会迁移到 Planet
类型中。