Pergunta

I'm experimenting with Clean Architecture for my turn-based strategy game project. I have use cases that form hierarchies and need to be coordinated with each other. I would like some advice on how to design these interactions.

Example

My entry point to the game is the TurnUseCase. It gives information about the current turn, the list of actions that are possible in that turn and allows to end the turn. The end of the turn bit is important as it is causing my issues.

Inside the turn, players can perform various actions. Each has its own use case, for example MoveUseCase, AttackUseCase or DefendUseCase. After some actions, the turn should end.

Question

How the AttackUseCase should pass information to the TurnUseCase that the turn should now end? Without that information, the TurnUseCase can't progress the game to another player.

  • Should I have the dependency between these use cases? Do I need a Factory to create all these use cases and manage relationships between them?
  • Should I use Observer to notify the other use case?
  • Should I create some kind of System Use Case that will monitor the Entities and it will trigger the end of the turn when certain conditions are met (ie: number of possible actions is 0)? Could this logic be part of TurnUseCase?

I would appreciate any suggestions on how to solve this problem.

Edit

Here are some details as requested by @candied_orange to help with the answer.

Who should be responsible for use case creation? It does not seem to be efficient to create use cases from other use cases due to dependencies that the child use case requires (Presenter, Repositories, etc). At the same time, I'm reluctant to move that responsibility up as this is part of the domain (game rules) to know which operation is possible at any given moment in the game. Most of the examples and articles show use case created by Controller and only situations where one use case operate independently of other use cases. From Doc Brown's answer I understand that this kind of problems are not addressed by Clean Architecture rules. Nevertheless I would like to see some hints from people that have used this approach in larger system.

What should be the use case life cycle? In my particular problem, I create all the use cases dynamically when the turn starts. That is partly because the game is asymmetric and different players have different use cases to choose from. However, maybe it is needed to have all the use cases active all the time and validate if their usage is possible in any given moment of the game?

What is the best way to override the use case? This kind of operation I've planned to use for tutorial purposes where I have to interrupt the normal game flow and explain the game rules. I wanted the tutorial to decorate the normal game flow so the normal use cases are not aware of the tutorial. To make this work I would need to have a place where all use cases live so I can override them in runtime. This goes back to question about who should create use cases.

It there a concept of System use case? By that, I mean a use case that is triggered by the system rather than a user. This goes back to Bart van Ingen Schenau's question about user manually triggering the end of the turn. Yes, the user can do it manually when he doesn't want to move anything. However, in the normal scenario, I would like the turn to end automatically. This is also because I have other situations when child use case have to do something on the parent so I'm interested in solving this problem rather than forcing the user to confirm the end of the turn manually.

Foi útil?

Solução

The Clean Architecture does not say anything about implementing interfaces, dependencies or interactions between different use cases. Use cases are all part of the same ring. So at this part of the system, you are on your own, have to start thinking by yourself and make your own decisions about how you are going to implement things in a meaningful manner.

Some things you need to start thinking about:

  • What exactly are the game rules (especially for ending a turn), and how is this information represented in the system? Without clarifying this basic requirement, don't even start thinking about different solutions.

  • How shall the data flow between the parts look like? Will each action write information about the changes to the game's state back to a global instance? Like a "game" class? Or a database? Can a third party instance (like the code which will handle the turn) ask that global instance about that information or when a turn ended? Or does such a central instance not exist, and the different components have to communicate directly with each other?

  • How are the use cases implemented? Is each use case is a class on its own in your system? Where are the related objects MoveUseCase, AttackUseCase or DefendUseCase created, and what is their life time? Does the TurnUseCase create them during the turn? Does it call an synchronous method of each those objects to deal with the use case? Then it should be quite simple for a TurnUseCase object to ask any of the other objects for any information after each action. Or do you have an event based system, where each use case communicates with the other ones through asynchronous event channels? Then something like an Observer could be appropriate.

  • Will there be something like a game loop, maybe in conjunction with some kind of event queue (see this post)? Where is is placed in your system, and how will it interact with the other components?

  • How decoupled do you need those use cases for unit testing purposes? This depends on the complexity of each, and the tests you intend to write. If each action is not a class, but a simple method in a TurnUseCase class, you probably won't need any decoupling measures.

As a general rule: start with the most simple design you can think of, and if it turns out to be "too simple" for your requirements, then start refactoring.

Licenciado em: CC-BY-SA com atribuição
scroll top