|
| 1 | +// |
| 2 | +// Controller.go |
| 3 | +// PureMVC Go Multicore |
| 4 | +// |
| 5 | +// Copyright(c) 2019 Saad Shams <saad.shams@puremvc.org> |
| 6 | +// Your reuse is governed by the Creative Commons Attribution 3.0 License |
| 7 | +// |
| 8 | + |
| 9 | +package controller |
| 10 | + |
| 11 | +import ( |
| 12 | + "github.com/puremvc/puremvc-go-multicore-framework/src/core/view" |
| 13 | + "github.com/puremvc/puremvc-go-multicore-framework/src/interfaces" |
| 14 | + "github.com/puremvc/puremvc-go-multicore-framework/src/patterns/observer" |
| 15 | + "sync" |
| 16 | +) |
| 17 | + |
| 18 | +/** |
| 19 | +A Multiton `IController` implementation. |
| 20 | +
|
| 21 | +In PureMVC, the `Controller` class follows the |
| 22 | +'Command and Controller' strategy, and assumes these |
| 23 | +responsibilities: |
| 24 | +
|
| 25 | +* Remembering which `ICommand`s are intended to handle which `INotifications`. |
| 26 | +* Registering itself as an `IObserver` with the `View` for each `INotification` that it has an `ICommand` mapping for. |
| 27 | +* Creating a new instance of the proper `ICommand` to handle a given `INotification` when notified by the `View`. |
| 28 | +* Calling the `ICommand`'s `execute` method, passing in the `INotification`. |
| 29 | +
|
| 30 | +Your application must register `ICommands` with the |
| 31 | +Controller. |
| 32 | +
|
| 33 | +The simplest way is to subclass `Facade`, |
| 34 | +and use its `initializeController` method to add your |
| 35 | +registrations. |
| 36 | +*/ |
| 37 | +type Controller struct { |
| 38 | + Key string // The Multiton Key for this Core |
| 39 | + commandMap map[string]func() interfaces.ICommand // Mapping of Notification names to funcs that returns `ICommand` Class instances |
| 40 | + commandMapMutex sync.RWMutex // Mutex for commandMap |
| 41 | + view interfaces.IView // Local reference to View |
| 42 | +} |
| 43 | + |
| 44 | +var instanceMap = map[string]interfaces.IController{} // The Multiton Controller instanceMap. |
| 45 | +var instanceMapMutex sync.RWMutex // instanceMap Mutex |
| 46 | + |
| 47 | +/** |
| 48 | + `Controller` Multiton Factory method. |
| 49 | +
|
| 50 | + - parameter key: multitonKey |
| 51 | + - parameter controllerFunc: reference that returns `IController` |
| 52 | + - returns: the Multiton instance |
| 53 | +*/ |
| 54 | +func GetInstance(key string, controllerFunc func() interfaces.IController) interfaces.IController { |
| 55 | + instanceMapMutex.Lock() |
| 56 | + defer instanceMapMutex.Unlock() |
| 57 | + |
| 58 | + if instanceMap[key] == nil { |
| 59 | + instanceMap[key] = controllerFunc() |
| 60 | + instanceMap[key].InitializeController() |
| 61 | + } |
| 62 | + return instanceMap[key] |
| 63 | +} |
| 64 | + |
| 65 | +/** |
| 66 | + Initialize the Multiton `Controller` instance. |
| 67 | +
|
| 68 | + Called automatically by the GetInstance. |
| 69 | +
|
| 70 | + Note that if you are using a subclass of `View` |
| 71 | + in your application, you should *also* subclass `Controller` |
| 72 | + and override the `InitializeController` method in the |
| 73 | + following way: |
| 74 | +
|
| 75 | + func (self *MyController) InitializeController() { |
| 76 | + self.commandMap = map[string]func() interfaces.ICommand{} |
| 77 | + self.view = MyView.GetInstance(self.Key, func() interfaces.IView { return &MyView{Key: self.Key} }) |
| 78 | + } |
| 79 | +*/ |
| 80 | +func (self *Controller) InitializeController() { |
| 81 | + self.commandMap = map[string]func() interfaces.ICommand{} |
| 82 | + self.view = view.GetInstance(self.Key, func() interfaces.IView { return &view.View{Key: self.Key} }) |
| 83 | +} |
| 84 | + |
| 85 | +/** |
| 86 | + If an `ICommand` has previously been registered |
| 87 | + to handle a the given `INotification`, then it is executed. |
| 88 | +
|
| 89 | + - parameter note: an `INotification` |
| 90 | +*/ |
| 91 | +func (self *Controller) ExecuteCommand(notification interfaces.INotification) { |
| 92 | + self.commandMapMutex.RLock() |
| 93 | + defer self.commandMapMutex.RUnlock() |
| 94 | + |
| 95 | + var commandFunc = self.commandMap[notification.Name()] |
| 96 | + if commandFunc == nil { |
| 97 | + return |
| 98 | + } |
| 99 | + commandInstance := commandFunc() |
| 100 | + commandInstance.InitializeNotifier(self.Key) |
| 101 | + commandInstance.Execute(notification) |
| 102 | +} |
| 103 | + |
| 104 | +/** |
| 105 | + Register a particular `ICommand` class as the handler |
| 106 | + for a particular `INotification`. |
| 107 | +
|
| 108 | + If an `ICommand` has already been registered to |
| 109 | + handle `INotification`s with this name, it is no longer |
| 110 | + used, the new `ICommand` is used instead. |
| 111 | +
|
| 112 | + The Observer for the new ICommand is only created if this the |
| 113 | + first time an ICommand has been regisered for this Notification name. |
| 114 | +
|
| 115 | + - parameter notificationName: the name of the `INotification` |
| 116 | + - parameter commandFunc: reference that returns `ICommand` |
| 117 | +*/ |
| 118 | +func (self *Controller) RegisterCommand(notificationName string, commandFunc func() interfaces.ICommand) { |
| 119 | + self.commandMapMutex.Lock() |
| 120 | + defer self.commandMapMutex.Unlock() |
| 121 | + |
| 122 | + if self.commandMap[notificationName] == nil { |
| 123 | + self.view.RegisterObserver(notificationName, &observer.Observer{Notify: self.ExecuteCommand, Context: self}) |
| 124 | + } |
| 125 | + self.commandMap[notificationName] = commandFunc |
| 126 | +} |
| 127 | + |
| 128 | +/** |
| 129 | + Check if a Command is registered for a given Notification |
| 130 | +
|
| 131 | + - parameter notificationName: |
| 132 | + - returns: whether a Command is currently registered for the given `notificationName`. |
| 133 | +*/ |
| 134 | +func (self *Controller) HasCommand(notificationName string) bool { |
| 135 | + self.commandMapMutex.RLock() |
| 136 | + defer self.commandMapMutex.RUnlock() |
| 137 | + |
| 138 | + return self.commandMap[notificationName] != nil |
| 139 | +} |
| 140 | + |
| 141 | +/** |
| 142 | + Remove a previously registered `ICommand` to `INotification` mapping. |
| 143 | +
|
| 144 | + - parameter notificationName: the name of the `INotification` to remove the `ICommand` mapping for |
| 145 | +*/ |
| 146 | +func (self *Controller) RemoveCommand(notificationName string) { |
| 147 | + self.commandMapMutex.Lock() |
| 148 | + defer self.commandMapMutex.Unlock() |
| 149 | + |
| 150 | + if self.commandMap[notificationName] != nil { |
| 151 | + self.view.RemoveObserver(notificationName, self) |
| 152 | + delete(self.commandMap, notificationName) |
| 153 | + } |
| 154 | +} |
| 155 | + |
| 156 | +/** |
| 157 | + Remove an IController instance |
| 158 | +
|
| 159 | + - parameter multitonKey: of IController instance to remove |
| 160 | +*/ |
| 161 | +func RemoveController(key string) { |
| 162 | + instanceMapMutex.Lock() |
| 163 | + defer instanceMapMutex.Unlock() |
| 164 | + |
| 165 | + delete(instanceMap, key) |
| 166 | +} |
0 commit comments