I'm an old VB6 developer and am trying to learn some OOP methods. I am working on a Winforms application that has multiple objects that live for the life of the application, such as logging, audio, and socket connections to applications running on other machines. Certain things like a button press or socket message received can trigger actions that do things with one or more of these objects. For example, when a user clicks a Login button, I might want to log it to a file, open a socket connection, play audio to the user, and start up the microphone. It looks like the Command pattern might do what I want, but how do I pass the dependencies cleanly when there are so many? So it might look like:
Public Interface ICommand
Sub Execute()
End Interface
Public Class LoginCommand
Implements ICommand
Protected _Receiver As LoginCommandReceiver
Public Sub New(Receiver As LoginCommandReceiver)
_Receiver = Receiver
End Sub
Public Sub Execute() Implements ICommand.Execute
_Receiver.Login()
End Sub
End Class
Public Class LoginCommandReceiver
Protected _Logger As LogManager
Protected _Gateway As IGatewayConnection
Protected _Audio As IAudioPlayer
Protected _VR As IVREngine
Public Sub New(Logger As LogManager, Gateway As IGatewayConnection, Audio As IAudioPlayer, VR As IVREngine)
_Logger = Logger
_Audio = Audio
_Gateway = Gateway
_VR = VR
End Sub
Public Sub Login()
_Logger.LogEvent("Starting login")
_Audio.PlayRingtone()
_Gateway.Connect()
_VR.Start()
_Logger.LogEvent("Login complete")
End Sub
End Class
So now the constructor for the LoginCommandReceiver is getting messy, even in this simple example. I thought about putting the required dependencies into a container class. The constructor can then look like:
Public Sub New(Container As ApplicationContainer)
With Container
_Logger = .Logger
_Audio = .Audio
_Gateway = .Gateway
_VR = .VR
End With
End Sub
I actually have that container class built (without the Command pattern) but the container class is getting pretty big (almost 30 objects), and it doesn't seem right to pass that huge thing all over to classes that only need a fraction of what it contains. Service locator pattern seems appropriate but I'm reading that should be avoided.
I also thought about making each step of the process its own command, like LogCommand, PlayRingtoneCommand, GatewayConnectCommand, and VRStartCommand. Then each subcommand only needs to know about one of the application-level objects. For example, playing the ringtone only needs to know about the audio object:
Public Class PlayRingtoneCommand
Implements ICommand
Protected _Receiver As PlayRingtoneCommandReceiver
Public Sub New(Receiver As PlayRingtoneCommandReceiver)
_Receiver = Receiver
End Sub
Public Sub Execute() Implements ICommand.Execute
_Receiver.PlayRingtone()
End Sub
End Class
Public Class PlayRingtoneCommandReceiver
Protected _Audio As IAudioPlayer
Public Sub New(Audio As IAudioPlayer)
_Audio = Audio
End Sub
Public Sub PlayRingtone()
_Audio.PlayRingtone()
End Sub
End Class
Now each command will have a single object in its constructor, which is cleaner. But somewhere, somebody still has to know about all the objects in order to create the list of commands. Outside of making this giant container, the only other time I have all objects instances together is at program startup when I'm wiring everything together. Do I need to create instances of all command types at that point? There could be a lot of them. Or is there some other design that will make this cleaner?
Aucun commentaire:
Enregistrer un commentaire