Introduce a Separate Validation Layer
Introduce a Separate Validation Layer. As with the grid, creating an instance of the
GameState class should fail when the supplied combination of cells and the starting player’s mark don’t make sense.
00:13 For example, it’s currently possible to create an invalid game state that doesn’t reflect genuine gameplay. You can test this yourself. Start an interactive Python interpreter session in the virtual environment where you’d previously installed your library and run the code seen on-screen.
Here you initialize a new
GameState using a grid comprising a syntactically correct string with the right characters and length. But such a cell combination is semantically incorrect because one player isn’t allowed to fill the entire grid with their mark. Because validating the game state is relatively involved, implementing it in the domain model would violate the single-responsibility principle and make your code less readable.
Go ahead and create two new files in your project:
validators.py. You’ll store various helper functions in
validators.py and a few exception classes in the
exceptions.py file to decouple game state validation from the model.
For improved code consistency, you can extract the grid validation that you defined earlier in the
.__post_init__() method and move it into the newly created Python module and wrap it in a new function.
and then you call it in the grid’s
.__post_init__() hook, which now uses a higher-level vocabulary to communicate its intent. Previously, some low-level details, such as the use of regular expressions, were leaking into the model, and it wasn’t immediately clear what the
.__post_init__() method does.
But in this case, the imported
validators module wants to import the
models module, which hasn’t been fully processed yet. This is a very common problem in Python when you start using type hints.
The only reason you’d need to import
models is because of a type hint in your validating function. You could get away without the import statement by surrounding the type hint with quotes to make a forward declaration as seen earlier on in the course, but you’ll follow a different idiom this time.
Grid conditionally. The
TYPE_CHECKING constant is
False at runtime, but third-party tools such as mypy will pretend it’s
True when performing static type checking to allow the import statement to run.
However, because you no longer import the required type at runtime, you must now use forward declarations or take advantage of
from __future__ import annotations, which will implicitly turn annotations into string literals.
Note that the
__future__ import was originally intended to make the migration from Python 2 to Python 3 more seamless. Today, you can use it to enable various language features planned for future releases.
05:01 Once the feature becomes part of the standard Python distribution and you don’t need to support older language versions, you can remove the import. You can read more about this behavior at the link to the Python documentation seen on-screen.
With all of this plumbing in place, you are finally ready to constrain the game state to comply with the tic-tac-toe rules. In the next section of the course, you’ll add a few
GameState validation functions to the new
Become a Member to join the conversation.