hamishmb wrote: ↑26/06/2020, 10:00
How do you feel about the control logic functions getting the readings using the methods of CoreTools.DatabaseConnection, rather than the main loop doing it for them? This might reduce the coupling and make the main loop simpler I think.
I'm not sure I like that. It would decrease the coupling between main and the specific strategy for getting readings, but it would increase the coupling between the control logic and that strategy. Currently, the control logic functions don't have to know anything about how the readings are obtained, and I think it is better that way around. I think making the control logic depend on the specific means of obtaining readings would violate the dependency inversion principle. (Or something related to it.)
This kind relates to what I was saying before in the
Software design for monitoring database readings thread about standardising the interface for the DatabaseConnection class. (Although I think I might have been making a slightly different point in that thread.) Currently run_standalone takes whichever method of fetching readings is being used and converts it into a readings dictionary, which constitutes a standardised interface. If run_standalone isn't going to abstract the readings to a standardised interface, then I think something else should.
[Cue lengthy-aside background music]
One way to remove the code from run_standalone without shifting the dependency and the coupling into the control logic would be to write an abstract ReadingsFetcher class (or choose a better name), which provides a get_readings method (Or getReadings? I've lost track of which style we are using.
) Then, a DatabaseReadingsFetcher (or choose a better name) can be written, which extends ReadingsFetcher and performs a similar job to the code you want to remove from run-standalone. Then all run_standalone would have to do is instantiate a DatabaseReadingsFetcher, as, e.g., controllogic.readings_fetcher. Then, the logic can just call controllogic.readings_fetcher.get_readings() without knowing where they come from, run_standalone doesn't need to know where they come from either, and it's fairly easy to write a different readings fetcher if it the way readings are fetched changes again in the future, and it's easy to switch back and forth between different ways of fetching readings.
If there are some features of the database that the control logic might want to use directly, but which wouldn't necessarily available if the readings are fetched by another means, then hopefully they can be added to the ReadingsFetcher abstraction in such a way that they return sensible data even if the underlying method of fetching the readings doesn't actually support them. In other words, the abstract class' interface should enable the logic to say "I'll give you an opportunity to try this, but if you can't do it, you can fall back on something else." So, just as an example, maybe the logic only wants a specific reading, and the DatabaseConnection can efficiently return just that reading, so we want to provide a method for doing that. If someone then writes a SocketsReadingsFetcher, though, that same method would have to get all of the readings and just filter out the one the logic wants. (Presumably it would store the readings in the Fetcher object, in case the logic wants one of the other readings.)
Or, a completely different way of doing it would be to just write a function that returns a readings dictionary and fill it with the code that would otherwise produce a readings dictionary in run_standalone. If the method of fetching the readings changes, write a new function. Less flexible, but conceptually simpler.
[End lengthy-aside background music]
How does that sound? Am I talking any sense?
hamishmb wrote: ↑26/06/2020, 10:00
Didn't realise that counted as functional programming
Functional is probably (definitely) the wrong word.
What I really meant was a word that describes whatever various styles I'm not super familiar with!
Which is not to say that putting the object in the module namespace is functional, or whatever other style, it's just that I would tend to rely much more heavily on objects, to the point that there would be no need to get a reference to this object into the logic function, because instead of running a function we would be instantiating an object and running a method on that, and any variables/objects the method needed would be inside the object.
But I don't want to say we should do that, because I'm trying to adapt myself to the codebase rather than imposing the style I know onto it. I think it's good to get experience of different ways of doing things.
Putting variables in the module namespace is conceptually pretty darn similar to putting them inside an object.