[英]How can I reduce boilerplate with the visitor pattern in Swift?
我正在Swift 2.2中为工作项目实现访问者模式。
因此,我不需要简化我的源代码并节省一些时间,我将使用Oktawian Chojnacki在swift中的访客模式示例 。
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
每个类的样板。 正如你所看到的,他们都有相同的功能重复func accept(visitor: PlanetVisitor) { visitor.visit(self) }
。
我已经尝试在Planet
协议上放置一个默认实现并在基类上实现它,并且由于编译时重载解析,Swift似乎不允许它。
例子:
协议的默认实施:
extension Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
基类:
class PlanetBase: Planet {
func accept(visitor: PlanetVisitor) { visitor.visit(self) }
}
class PlanetAlderaan: PlanetBase {}
class PlanetCoruscant: PlanetBase {}
class PlanetTatooine: PlanetBase {}
有没有人知道一种方法, accept
函数可以通用并自动应用于派生自Planet
每个具体类? 这不是一个关键问题,但它是一个很棒的难题!
简短的回答:不可能,这是设计的。
当您拥有稳定数量的行星但未知数量的访客时,访客模式适用于该情况。 因此,您计划访问者的未来扩展,编写此样板文件一次。 无需更改行星即可添加更多访客。
在大型项目中,您可以使用代码生成。
不推荐 ,你的替代方案是直接切换行星,不需要样板代码:
func foo(planet: Planet) {
if planet is PlanetAlderaan {
name = "Alderaan"
}
else if planet is PlanetCoruscant {
name = "Coruscant"
}
else if planet is PlanetTatooine {
name = "Tatooine"
}
}
这很容易出错,因为你很容易忘记行星。 访问者模式强制您为所有情况编写代码,否则将无法编译。
阅读@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
类型中迁移。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.