Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

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. 

...

The most obvious way to add any kind of data storage to the existing class is to add some more code. And if you really want data storage, if you want it in the worst way, then chances are the worst way is to add more methodsBut 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. It 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. 

...

My standard technique for breaking the impasse is to think not about 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 . 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 imagine began by imagining code like this:

Code Block
MagicBlackBox.LoadDataLoadCheckboxData()
!... Use the list box
MagicBlackBox.SaveDataSaveCheckboxData()

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

...

Code Block
MagicBlackBox.SetQueue(q,q.iconfield)
MagicBlackBox.LoadDataLoadCheckboxData()
!... Use the list box
MagicBlackBox.SaveDataSaveCheckboxData()

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 even for me, if enough time has passed).

...

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: 

Code Block
MagicBlackBox.ListCheckbox &= ListCheckbox
MagicBlackBox.LoadDataLoadCheckboxData() 
!... Use the list box
MagicBlackBox.SaveDataSaveCheckboxData()

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.

...

Code Block
ListCheckbox.MagicBlack &= MagicBlackBox
ListCheckBox.LoadData()LoadCheckboxData
!... Use the list box
ListCheckBox.SaveData()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". This is because 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 though a call like ListCheckbox.LoadDataLoadCheckboxData() 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'm mixing 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.