-
Notifications
You must be signed in to change notification settings - Fork 0
3. Java Patterns
Here you will find detailed information about the java patterns included in this project, how they are implemented and the reasons why they were chosen.
-
LocationChanger: Interface that acts as façade of WorldMap. Reduces the complexity of the world subsystem to two functions:
startIn(Location)
andgoTo(Location)
-
WorldMap: subsystem whose details are hidden
-
Player: user of the façade. Ignores the specific details of implementation of the world.
There's a remarkable aspect about the implementation: The Player and Location classes cannot be decoupled. This is because the Player needs access to the Location in which he is, to get the list of actions he can do (they are different for each location). Also, in the other direction, Location stores a list of players that are inside that location, for several aspects such as interactions between players on the same room.
For that reason this Façade implementation can be considered weak. However, the goal of the pattern is achieved. The Player class knows nothing about the implementation of the world. It just knows that when he calls the moveTo() function, his Location atribute is updated accordingly. The LocationChanger interface is the only access point to the world subsystem.
The creation of the Bots is handled by a Factory Pattern. The classes involved are:
- Game: the client that wants a list of Bots.
- BotBuildDirector: the director in charge of deciding what type of bots to create depending on the difficulty.
- BotFactory: the factory abstract class, with as many subclasses as bot's types.
There main factor involved on the creation of the Bots is the Difficulty selected by the user (or the default one). Depending on it, the number of bots will be greater, and they will be smarter. That logic is encapsullated on the BotBuildDirector.
The main characteristic of a Bot is it's MarketStrategy. The rest of the attributes are generally created (name, sex, initial money...). Then, there is a factory subclass for each strategy, and the abstract BotFactory defines the general attributes mentioned before.
This sequence diagram represents the proccess of creation. The Game (client class) doesn't participate in the proccess at all, but the Director orchestrates everything.
There are several aspects in common for all the classes implementing this pattern:
-
There can only be 1 single instance of the class, so by hiding the constructor this is ensured to happen.
-
The class has to be accessed from many different points of the code, so a call to
getInstance()
simplifies the code a lot.
There are currently 4 classes making use of this pattern:
- EventHandler: it is crucial that there's only one single instance of this class because it holds the list of pending Events.
- WorldMap: as well, it holds the list of Locations, that must be unique because each location has the information of who is inside.
- Market: holds the list of Assets and information about which player bought each asset.
- Banker: accessed from many other classes, and as it's basically an algorithm, there should be only one real instance (or even make it static)
In our project the commands take the name of Actions. As already explained, each Location has its own set of Actions and MoveActions.
The player decides what action to perform by writting down the input in the console. The process of taking that string, parsing it and getting the corresponding Action instance is carried out following the Command Pattern.
The classes involved are:
- Broker: the client of the pattern. It just gets the input and sends it to the ActionParser.
- ActionParser: given a list of Actions, it checks if any of them matches the received string. If so, it returns that action.
Note: the ActionParser doesn't call the execute method of the Action because this is done through the system of events. This mechanism is explained in the Architecture page (EventHandler).
The Bot class doesn't make use of the ActionParser because it doesn't need to parse a string, it chooses directly an action from the list.
The classes involved are:
- Difficulty: This is the interface that implements the adaptation.
- CustomDifficulty: This is the class that can implement a custom difficulty for sandbox-play.
- PremadeDifficulty: This is the enum that can contains every developed by us difficulty for designed play.
This was made so we could have the enum and the class, which need to deliver the same information, function under the same framework (interface), just as the difficulty interface facilitates.
The behaviour of the Bots (non-player characters) is dictated by strategies, which work in conjunction with the State Pattern explained below. We implemented the Strategy Pattern for 3 different areas:
This type of strategies are in charge of performing actions related with Market, such as deciding what Assets to buy or to sell.
The CommonKnoledge class groups some common logic for all of them.
This type of strategies are in charge of deciding how to act in any interaction with other players, for example what to say or how to react to a greeting.
This strategy is used for players in the Bar location. Bar actions contain "Greet someone on the room". The mechanism is as follows:
- A player decides to greet someone on the room. For that it gets the list of players currently in the room.
- It chooses a sentence to say using
GetMessageToSay
- An ActionEvent is created and added to the EventHandler (ignored on the diagram for simplicity)
- The greeted player receives the sentence and chooses a way to react using
ReactToGreeting
Note: the Bots use the strategy, while the broker (real player) is asked by console.
This type of strategies are in charge of deciding which action to perform next, that is, of choosing an Action from the list of possible actions given by the current Location.
Bots manage their behaviour based on strategies. However, they are not directly accessed. Instead we use the State Pattern in conjunction with the Strategy Pattern.
Bots have States, that vary depending on conditions, such as hunger or money. The BotStateGovernor is the class that determines the current state of the Bot, as well as if it should change to another state.
There are several defined states.
A peculiarity to keep in mind is that MarketStrategies are different. They are fixed for the whole execution of the program. It means that, for example, is a Bot has a Knoledgeable Market Strategy (he is good at trading), this is an intrinsic feature of him, and shouldn't change even if the Bot is hungry, sad, or bankrupt. That's the reason why the MarketStrategies are kept outside the State Pattern, and they are a field in the Bot class.
The BotStateGovernor stores the instance of the current State of the Bot. This instance has to be changed every time there's a change on the condition of the Bot (i.e. he's hungry, so the instance of currState should contain a HungryState).
At first we coded this by creating new objects of the desired subtype of State every time there was a change. However we came up with a more efficient desing in which the BotStateGovernor creates instances of each subtype once, and the current state variable just points to one of them. With this idea we got rid of the abusive creation of objects.
The pattern can be sumarized in this diagram: