[Tutor] Proper SQLite cursor handling?
Alan Gauld
alan.gauld at yahoo.co.uk
Sun Jul 4 07:59:15 EDT 2021
On 04/07/2021 01:47, dn via Tutor wrote:
> On 03/07/2021 22.13, Alan Gauld via Tutor wrote:
>> On 03/07/2021 10:02, dn via Tutor wrote:
>>
>>> now, to run a game, the three steps are self-documenting:
>>>
>>> game_parameters = get_game_data_from_DB( db_defns )
>>> game = SolitaireGame( game_name, other_game_data )
>>> store_game_results_in_DB( game.results_for_DB() )
>>
>> From an OOP perspective I'd change that to:
>>
>> parameters = GameParameters( db_defns )
>> game = SolitaireGame( parameters )
>> game.store()
>
>
> Does this mean that within game.store() the code will call a method
> within parameters (GameParameters) to perform the necessary o/p to
> backing-store? It will work (see below). Is it 'good design'/'best
> practice'?
No, my assumption is that the parameters objects sole responsibility is
to identify the parametrs for the current installation/user. Thus things
like the default locations(as per the other running thread) and maybe
some global values such as high scores, other players etc. Among this
would be the database connection(or reference thereto)
The game is then responsible for load()ing the data at init)()
and store()ing the data before closedown. After all, only the game
should know what data is in the object. Exposing the game's
internal data to an external object would be against all
the principles of OOP. As Peter Coad repeatedly says in his
OOP book "Objects should do it to themselves"
In fact I'd probvably reduce the three lines above to just 2:
game = SolitaireGame( GameParameters( db_defns ) ).play()
game.store()
The parametrs are only required during the games lifetime so
there's no need to hold on to a reference outside the game
(unless they are shared across multiple game instances of course)
And the store() method only needs to be called after the game has completed.
> An alternative (re-stating the original outline) might be something like:
>
> parameters = GameParameters( db_defns )
> game = SolitaireGame( parameters )
> parameters.store_results( game.store() )
>
> In this case, game.store() extracts/exports relevant parameter-values
> from the game. These are then handed across an
> interface/'API'/"contract" to parameters.store_results() which performs
> the transfer of values being carried-forward into the next game/play,
> onto the 'DB'/backing-store.
But that then makes another object responsible for the handling
of the games state. That's a bad smell OOPwise. It may be that the Game
uses some kind of data adaptor object internally to do its storage but
that should not be exposed at the higher level application code, it
should be internal to the game. Only the game knows about its data and
it alone is responsible for it.
> There is room for some debate about which of these two approaches is
> 'best' - or a better approach to the decision: which is preferable for
> this application?
Always. But if viewing through the OOP prism there are fewer allowed
approaches. If we permit hybrid design paradigms then all sorts of
options present themselves.
> The reasons for (my) preferring the (second outline) above is that each
> object (GameParameters and SolitaireGame) has its own (separate) set of
> methods, and the two only 'meet' across the two 'mirror' interfaces, ie
> a 'Clean Architecture'. SolitaireGame plays Solitaire. GameParameters
> handles persistence.
That's a different interpretation of GameParamer's responsibilities
than I had in mind. But splitting an objects data handling across
multiple objects is very bad OOP practice. it means changes in one
object need to be reflected in another.
> Whereas, the former case requires that SolitaireGame not only take in
> the data (parameters) but either:
> - understand that the parameters object to include a
> store_results/store_parameters method, or
> - will directly store the parameters itself
> (which would mean that both objects need to know about I/O for
> persistence - whither 'separation of concerns'?).
I would always take the second approach. Only the game should know
about the game data. The parameters only knows where to find it
plus any shared data between games 9but even that could/should
be in the database.
> Perhaps without such separation it might be possible to 'bury' changes
> and make them seem less significant ("major")?
Thats the OOP theory, any changes to schema(ie data) should
only impact the object that manages that data.
> On the other hand, in a commercial situation, "separation" enables the
> work to be split between different people/teams. In this example, we
> could ask our DB-specialist to create GameParameters, whilst our
> games-expert gets-busy with SolitaireGame. The two will have to 'put
> their heads together' to design and agree the interface both up-front
> and in an Agile cyclic-update manner thereafter. Which brings me to...
Clearly defined interfaces are always the key to project management
division of labour. The Game object could itself be split by teams
or the game (and all other objects in the system) could utilize a
database adaptor object to do its storage. The database adaptor
object in turn could be the responsibility of another team - a
database specialist team.
> If achieving working-code is where the objective/satisfaction lies.
> Either of the above, or indeed the paired-functions (used as a more
> simple illustration, earlier in the thread), will 'do the job'.
Absolutely. you can produce code that "does the job" using any approach.
You may wind up with spaghetti but it will work - for a while at least.
But you can apply structured design, OOP, Functional programming or any
other approach and get a working (and maintainable!) result. Which
approach suits the problem domain (and team skills) best is a project
specific decision.
> author is likely to be the only user, then should changes (even
> 'catastrophic changes') need to be made, 'no puppies will die'.
True, but a lot of midnight oil may be burned! (Been there,
bought the tee shirt!)
> storage options, and application-design. Accordingly, the earlier
> suggestion that separating I/O from game-play would enable various (and
> independent) iterations of both game-complexity and storage-medium - all
> the while (post iteration nr1), having a working-application.
The question is whether the separation requires separate objects or
whether it is only a matter of separate methods in the same object.
In OOP every object is responsible for the lifecycle of its own data and
thus incorporates the creation, update, storage and removal of its own
data. This is one of the biggest differences in approach between OOP and
more traditional structured design techniques where separation is
achieved by high level functions that take responsibility for everyone's
data.
These two approaches lead to very different code structures (and very
different performance characteristics depending on the nature of the data!).
> In this project's learning-path we might see some merit in starting-out
> with a dict to hold key:value pairs of parameters. Zero persistence,
That was very much my expectation of the parameters object. It's
main role was navigating the morass of OS/User locations and
config options. Once it has achieved that it simply hands a set
of references to the Game which uses them to load the requisite
state.
> concentrate on the workings of SolitaireGame, and find that over-time
> the requirements for I/O (interface) seem to naturally 'fall out' and
> make themselves obvious.
Most games are interactive and by their nature will lend
themselves to an MVC architecture. The Model object is
what we are discussing, the interaction would be in the
view and controller objects. The model's only I/O is
therefore the management of state data. Any user or network
interaction would be down to the controller(input) and
view(output)
> In the early days, the dict can be easily expanded and altered. When
> SolitaireGame starts to feel 'stable', there will be time and 'spare
> effort' to apply to the I/O object/methods. A dict will lead naturally
> to JSON, and thereafter JSON could lead to MongoDB. Thus, a convenient
> expansion plan/learning-path. Not that it would be difficult to re-form
> the dict into a relational schema either...
But that should all be internal to the Game (and/or its data adaptor).
The adaptor may need to change if we move from SQLite to Mongo say, but
the game's need to store its state remains the same.
I suspect we may be saying the same things but from slightly different
assumptions about architecture....
--
Alan G
Author of the Learn to Program web site
http://www.alan-g.me.uk/
http://www.amazon.com/author/alan_gauld
Follow my photo-blog on Flickr at:
http://www.flickr.com/photos/alangauldphotos
More information about the Tutor
mailing list