Pattern for creating an instance of a class from a UI form

  softwareengineering

The following is a contrived example of a potential code smell
that I’ve repeatedly encountered when implementing a GUI with which instances of a class created by letting the user fill out a form. The question is if the pattern I use is correct, or if there is a better way.

The app’s core logic contains a class Data that is initialized with
a Dict (among other things):

class Data:
    def __init__(self, stuff: Dict, *args, **kwargs) -> None:
        self.stuff = stuff
        # ...

The attribute self.things is read-only. Note that __init__ has other arguments,
which are used to set other attributes of Data.

The main use-case is that raw is read from a .json file:

with open(path, 'r') as f:
    raw = f.read()
stuff = json.loads(raw)
data = Data(stuff, ...)

Instances of Data may be created using a UI form,
in which the user enters a filesystem path (from which raw is read)
and the other parameters of __init__.
The instances should also be managed by the app’s UI; when the app is closed, the
Data instances should be serialized, saved, and later loaded again.
Out of the potentially very large database of Data objects,
only very few are required at the same time.

To prevent massive data duplication (let’s say the file at path is
very large, and potentially used by many Data objects) and to allow
editing the Data instances by changing the path that the raw
parameter is read from, the file path should be saved, not the data
itself.

This means that the Data instances themselves cannot be serialized.
Instead, I’ve opted to use an intermediate class

class DataInfo:
    def __init__(self, path: str, *args, **kwargs) -> None:
        self.path = path
        # Store everything in attributes...

    def deploy(self) -> Data:
        with open(path, 'r') as f:
            raw = f.read()
        stuff = json.loads(raw)
        data = Data(stuff, ...)
        return data

The instances of this class are used to store the initialization args
for Data, and on DataInfo instances are serialized/loaded.
Once the Data object is actually required (remember that
only very few of the instances are required at each moment), it is
constructed by calling deploy.

Maybe I’m misjudging this, but this pattern feels slightly awkward. Is this (roughly) the correct pattern for this situation? Does it have a name? Or are there more readable/less intrusive or well-known patterns that solve this problem (by “well-known” I mean something that for, for instance, be suggested in the official Qt documentation)?

1

The general strategy (to load the actual data of a Data object only on-demand) seems to be fine. And yes, this has indeed a common name, it is called Lazy Loading.

The way you implemented will surely work. Let me suggest a different approach, though.

My first thought when I saw this design was

  • why can a Data object not simply load it’s data on-demand “behind the scenes”, automatically, when the data is needed first?

Instead of introducing another class DataInfo which the caller must know and deal with, a Data object might be constructed with a path parameter, and load the related file immediately when the first accessor method is called.

That would abstract the lazy loading completely away from the code which uses the Data objects, so making the usage a little bit simpler.

There may be reasons why you want to keep Data objects separated from the path of their related JSON file. For this case, you may consider to design the solution with a proxy class, where DataProxy has almost the same public interface as Data, holds the path and a reference to a Data object which is lazy-loaded when it is required. As a variant of this, one may make Data itself the proxy, and give the current Data class a different name like DataContainer.

IMHO there is not enough context in the question to judge if any of these approaches is really simpler or easier to handle than your current approach. But I am sure you can evaluate by yourself if they will bring you any benefits for your system.

LEAVE A COMMENT