It was Confucius who said isn’t it great when friends visit from distant places. 子曰, “ 有朋自远方来,不亦乐乎 ”
The visitor pattern allows new algorithms to operate on collections of heterogeneous objects without needing to modify or subclass the collection class. It’s similar to the strategy pattern. — Design Pattern in Swift
protocol Person {
func accept ( visitor : Visitor )
}
protocol Visitor {
func visit ( person : Dwarf )
func visit ( person : Maiden )
func visit ( person : Knight )
}
struct Dwarf : Person {
let name : String
let expectedAge : Int
func accept ( visitor : Visitor ) {
visitor . visit ( self )
}
}
struct Maiden : Person {
let name : String
let expectedAge : Int
func accept ( visitor : Visitor ) {
visitor . visit ( self )
}
}
struct Knight : Person {
let name : String
let expectedAge : Int
let skillLevel : Int
func accept ( visitor : Visitor ) {
visitor . visit ( self )
}
}
struct People : Person {
let group : [ Person ]
func accept ( visitor : Visitor ) {
group . map { $ 0. accept ( visitor ) }
}
}
//: A healthProVisitor can increase each person's age by different amount of years
class HealthProVisitor : Visitor {
var totalExpectedAgeOld = 0
var totalExpectedAge = 0
func visit ( person : Dwarf ) {
totalExpectedAgeOld += person . expectedAge
totalExpectedAge += person . expectedAge + 1
}
func visit ( person : Knight ) {
totalExpectedAgeOld += person . expectedAge
totalExpectedAge += person . expectedAge + 5
}
func visit ( person : Maiden ) {
totalExpectedAgeOld += person . expectedAge
totalExpectedAge += person . expectedAge + 10
}
}
//: a prettyNameVisitor can form memoriable names for others to remember
class PrettyNameVisitor : Visitor {
var names : [ String ] = []
func visit ( person : Maiden ) {
names . append ( "Pretty Maiden \(person.name)" )
}
func visit ( person : Knight ) {
names . append ( "Knight \(person.name) with \(person.skillLevel) level of skills" )
}
func visit ( person : Dwarf ) {
names . append ( "A plain dwarf named \(person.name) " )
}
}
//: Testing
print ( "Created a group of people Helen (32), Joe (99), Eleot(45)..." )
let people = People ( group : [ Maiden ( name : "Helen" , expectedAge : 32 ),
Dwarf ( name : "Joe" , expectedAge : 99 ),
Knight ( name : "Eleot" , expectedAge : 32 , skillLevel : 45 )
])
let healthProVisitor = HealthProVisitor ()
let prettyNameVisitor = PrettyNameVisitor ()
print ( "The healthProVisitor is visiting the group..." )
people . accept ( healthProVisitor )
print ( "Before visit, total expected age:\(healthProVisitor.totalExpectedAgeOld)" )
print ( "After visit, total expected age:\(healthProVisitor.totalExpectedAge) \n " )
print ( "The prettyNameVisitor is visiting the group..." )
people . accept ( prettyNameVisitor )
print ( "After visit, people's names: \(prettyNameVisitor.names) \n " )
class InsuranceVisitor : Visitor {
var salesRecord :[ String : Bool ] = [ : ]
func visit ( person : Dwarf ) {
salesRecord [ person . name ] = getQualification ( person . expectedAge )
}
func visit ( person : Knight ) {
salesRecord [ person . name ] = getQualification ( person . expectedAge )
}
func visit ( person : Maiden ) {
salesRecord [ person . name ] = getQualification ( person . expectedAge )
}
private func getQualification ( expectedAge : Int ) -> Bool {
return expectedAge > 45
}
}
//: Testing
print ( "A new visitor, the insurance guy! Let's see what's his sales record say after the visit" )
let insuranceVisitor = InsuranceVisitor ()
people . accept ( insuranceVisitor )
insuranceVisitor . salesRecord . map {
print ( "\($0.0), is eligiable for insurance?: \($0.1)" )
}