Blocking calls and UIs IV
In the previous post we solved the computer vs computer usage. Let’s face the interleaving of computer and human in this one.
With only humans we had the following API:
class Game
def move(location)
def board
def is_finished?
def winner
endAnd with only computers we had the following:
class Game
def move
def board
def is_finished?
def winner
endWe need to support both ways of usage in a generic way. Let’s start by describing what they do and find the things they have in common.
We are calling both move methods to advance to the next player. But, for a human the game requires a location to play. If we keep the location parameter, what do we send when the computer is playing? nil?. And more importantly, how do we know the computer is playing to send nil? Do we need to add the current_player method to Game? That will make the UIs know and duplicate some of the Game logic and violates the single responsibility principle.
We have another option, we can keep the move method without any parameters and provide that location in some other way. But in what way? If the Game knows when it needs the location value, makes sense for the Game to request it when is needed. What object should Game request for the location? The location comes from the user through the UI, but the Game shouldn’t know about any specific UI. We need to use the dependency inversion principle here: Game depends on an abstraction to get the location and the specific UI will provide its implementation for it.
class Game
def initialize(user)
def move
def board
def is_finished?
def winner
endWe have to keep in mind that in a Gui, if the Game asks to get_location, the user may not have selected a location yet. And we cannot block for the same reasons we talked about in the previous post. That means that the object that provides the location could not be ready to be queried.
An implementation could be:
class GuiUser
def when_user_selected_location(location)
@location = location
@is_ready = true
end
def is_ready?
@is_ready
end
def get_location
@is_ready = false
@location
end
endIn the Cli things are much easier. We know that each time we ask_to_select_location it will block and return a valid location. The implementation could be as simple as:
class CliUser
def is_ready?
true
end
def get_location
ask_to_select_location
end
endWith that, we can have all modes working.