Observer

godo·2022년 8월 19일
0

Swift - Design Patterns

목록 보기
19/24

무엇인가 일어났을 때 알려주는 것

Events

protocol Invocable : class
{
    func invoke(_ data: Any)
}

protocol Disposable
{
    func dispose()
}

class Event<T>
{
    typealias EventHandler = (T) -> ()
    var eventHandlers = [Invocable]()
    
    func raise(_ data: T)
    {
        for handler in eventHandlers
        {
            handler.invoke(data)
        }
    }
    
    
    // (U) -> (T) -> ()
    func addHandler<U: AnyObject>(
        target: U,
        handler: @escaping (U) -> EventHandler
    ) -> Disposable
    {
        let subscription = Subscription(target: target, handler: handler, event: self)
        eventHandlers.append(subscription)
        return subscription
    }
}

class Subscription<T: AnyObject, U> : Invocable, Disposable
{
    weak var target : T?
    let handler : (T) -> (U) -> ()
    let event: Event<U>
    
    init(target: T?,
         handler: @escaping (T) -> (U) -> (),
         event: Event<U>
    )
    {
        self.target = target
        self.event = event
        self.handler = handler
    }
    
    func invoke(_ data: Any)
    {
        if let t = target
        {
            handler(t)(data as! U)
        }
    }
    
    func dispose()
    {
        event.eventHandlers = event.eventHandlers.filter { $0 as AnyObject? !== self }
    }
}


class Person
{
    // event
    let fallsIll = Event<String>()
    
    init(){}
    
    func catchCold()
    {
        fallsIll.raise("400 Seoul Road")
    }
}


class Demo
{
    
    init()
    {
        let p = Person()
        let sub = p.fallsIll.addHandler(
            target: self, handler: Demo.callDoctor
        )
        
        p.catchCold()
        
        sub.dispose()
        
        p.catchCold()
    }
    
    func callDoctor(address: String)
    {
        print("We need a doctor at \(address)")
    }
}

func main()
{
    let _ = Demo()

}

Property Observers

class person
{
    var age: Int = 0
    {
        willSet(newValue)
        {
           print("About to set age to \(newValue)")
        }
        didSet
        {
            print("We just Changed age from \(oldValue) to \(age)")
        }
    }
}

class demo
{
    init()
    {
        let p = person()
        p.age = 20
        p.age = 22
    }
}

func main()
{
    let _ = demo()
}

Handling Dependent Observable Properties

class Human
{
    private var oldCanVote = false
    
    var age: Int = 0
    {
        willSet(newValue)
        {
            print("About to set age to \(newValue)")
            oldCanVote = canVote
            
        }
        didSet
        {
            print("We just Changed age from \(oldValue) to \(age)")
            
            if age != oldValue
            {
                propertyChanged.raise(("age", age))
            }
            
            if canVote != oldCanVote
            {
                //
                propertyChanged.raise(("canVote", canVote))
                
            }
        }
    }
    
    var canVote: Bool
    {
        return age >= 16
    }
    
    let propertyChanged = Event<(String, Any)>()
}



final class RefBool
{
    var value: Bool
    init(_ value: Bool)
    {
        self.value = value
    }
}




class Human2
{
    private var _age : Int = 0
    
    var age: Int
    {
        get { return age }
        set(value)
        {
            if _age == value { return }
            
            // cache
            let oldCanVote = canVote
            
            var cancelSet = RefBool(false)
            propertyChanging.raise(("age", value, cancelSet))
            
            if cancelSet.value
            {
                return
            }
            
            // assign & notify
            
            _age = value
            propertyChanged.raise(("age", _age))
            
            if oldCanVote != canVote
            {
                propertyChanged.raise(("canVote", canVote))
            }
        }
    }
    
    var canVote: Bool
    {
        return age >= 16
    }
    
    let propertyChanged = Event<(String, Any)>()
    let propertyChanging = Event<(String, Any, RefBool)>()
}


class Demo3
{
    init()
    {
//        let h = Human()
//        h.propertyChanged.addHandler(target: self, handler: Demo3.propChanged)
//        h.age = 20
//        h.age = 22
        
        let h = Human2()
        h.propertyChanged.addHandler(target: self, handler: Demo3.propChanged)
        h.propertyChanging.addHandler(target: self, handler: Demo3.propChanging)
        
        h.age = 20
        h.age = 22
        h.age = 12
    }
    
    func propChanging(args: (String, Any, RefBool))
    {
        if args.0 == "age" && (args.1 as! Int) < 14
        {
            print("Cannot allow to set the age < 14")
            args.2.value = true
        }
    }
    
    func propChanged(args: (String, Any))
    {
        if args.0 == "age"
        {
            print("Person's age has been changed to \(args.1)")
        }
        else if args.0 == "canVote"
        {
            print("Voting status has changed to \(args.1)")
        }
    }
}
profile
☀️☀️☀️

0개의 댓글