Many-to-many checkboxes revisited, part 5: Persisting the data

by Unknown user

Read Part 4

When I started refactoring the UI code for the original many-to-many checkboxes class I had a pretty clear idea of where I wanted to go. 

When it came to storing the checkbox data in a database or some other data, however, I found myself thinking about a number of different factors/possibilities/requirements, including:

  • Checkboxes in a simple queue
  • Checkboxes in a file loaded ABC browse
  • Checkboxes in a page loaded ABC browse
  • Storing results in a database
  • Storing results in non-database form (e.g. XML)

The most obvious way to add any kind of data storage to the existing class is to add some more code. But chances are that's the worst possible way to create this capability. Most likely it will result in a bloated, inflexible, difficult to maintain class. 

I'm a fan of the Single Responsibility principle, which states that "every context (class, function, variable, etc.) should have a single responsibility, and that responsibility should be entirely encapsulated by the context."

I'm far from perfect in applying this principle. For instance, it can probably be argued that the existing CML_UI_ListCheckbox class does too much and could be broken down into multiple classes. But it doesn't seem overly complex yet, and I'm okay with describing it as having the responsibility to display a toggleable checkbox in a list. 

I'm not okay with this class also persisting those values in some kind of data store.

But how should I design a new class that can handle this functionality?

Sometimes, as with the CML_UI_ListCheckbox class, the requirements are clear to me and the design comes easily. Other times I struggle with class design. 

This is one of those other times.

My standard technique for breaking the impasse is not to think about how I want to implement the class to accomplish some end, but rather to think about how I want to use the class. 

That's an important distinction. Most developers I know have a built-in bias to think about implementation details when presented with a problem, and I'm no different. I immediately think about the code. And why shouldn't I? I'm a developer, right? But thinking about the code can get in the way of a good implementation. 

Thinking about the box

To solve tricky class design problems, don't think inside or outside the box; think about the box. That is, the black box (or boxes).

If you could have a magic class that did exactly what you wanted, how would you want to use it? 

I began by imagining code like this:

MagicBlackBox.LoadCheckboxData()
!... Use the list box
MagicBlackBox.SaveCheckboxData()

That's a good start, although it's incomplete. How does MagicBlackBox know where to load data to and where to save it from?

If I know I'll always be using the CML_UI_ListCheckbox with a simple queue, then MagicBlackBox doesn't even need to know about CML_UI_ListCheckbox. All it needs to know is the queue and the field in the queue that holds the checkbox flag:

MagicBlackBox.SetQueue(q,q.iconfield)
MagicBlackBox.LoadCheckboxData()
!... Use the list box
MagicBlackBox.SaveCheckboxData()

There are two things I don't like about this approach, however. One is that it won't be sufficient for page-loaded ABC browses, for the reason I gave last time. The other is that in the implementing code there's no obvious connection between MagicBlackBox and my checkbox class, which may be confusing for anyone who maintains my code at some later date (or for me, if enough time has passed).

I prefer to have some connection between two classes that work together, and that often takes the form of one class having a reference to the other. (There's also a more advanced technique available using Interfaces, which I hope to get to later in this series.)

If I do use a reference to one class in another class, then I have a decision to make: should MagicBlackBox know about CML_UI_ListCheckbox, or should CML_UI_ListCheckbox know about MagicBlackBox, or should they know about each other? 

I really don't like the last option as it can lead to inconsistencies in the implementing code - sometimes I might write the code from the CML_UI_ListCheckbox perspective and sometimes from the MagicBlackBox perspective. I prefer to have just one perspective. 

Given an instance of CML_UI_ListCheckbox that leaves the choices as something like this:

MagicBlackBox.ListCheckbox &= ListCheckbox
MagicBlackBox.LoadCheckboxData() 
!... Use the list box
MagicBlackBox.SaveCheckboxData()

In this example MagicBlackBox will maintain a reference to the CML_UI_ListCheckbox instance, and then use the queue properties associated with that instance to figure out where to load data and where to get data to save.

The actual implementation of the data loading and saving is still unknown.

Another possibility is to drive everything from the CML_UI_ListCheckbox instance:

ListCheckbox.MagicBlack &= MagicBlackBox
ListCheckBox.LoadCheckboxData
!... Use the list box
ListCheckBox.SaveCheckboxData

I like this approach better because it allows me to work from the perspective of the list box.

I tend to envision applications as a loose hierarchy, with the user interface code at the top and the data layer on the bottom. In between are various modeling and business logic layers. (Some standard architectures have evolved along these lines - for web applications have a look at Model-View-Controller, for non-web apps see Model View ViewModel.) As a general rule I don't like classes to see "up". To my mind classes that are higher up typically make use of classes that are lower down, but not vice versa. Lower layers exist to be used by higher layers, not to make use of higher layers. (And yes, my definition of what is "up" and what is "down" is arbitrary. If you go looking for diagrams of application architectures you'll see various orientations.)

At first glance a call like ListCheckbox.LoadCheckboxData() makes me uneasy, because it suggests that this class is talking to a database of some kind, and that would mean that I'm mixing my UI layer and my data layer. But I am going to have to get the checkbox data from somewhere, and I am going to have to save it to somewhere, and it probably won't be the checkbox class that does the actual saving and loading. I think I'll leave that in for now until I have a clearer picture of how everything fits together.

This pretty typical of my class design process. I often change method and even class names as I go along, and sometimes classes go away entirely as I realize they don't fit with what I'm trying to accomplish. 

Next time I'll look in more detail at how to go about saving and loading checkbox data.