Tag Archives: complexity

Attributes and Complexity

Attributes can be seen just as a way of gaining some benefits of multiple-inheritance without the awful complexity that comes with that feature. Most languages have wisely done away with MI, and replaced with interfaces, and now attributes.

For example, let’s look at this code fragment in C#:

[Category(“Test Class”)]
public class MyClass
//implementation of MyClass

This assigns an instance of a CategoryAttribute to MyClass. The same thing could be accomplished in C++, using a base class:

public class MyClass : public CategoryAttribute
MyClass() : CategoryAttribute(“Test Class”) { }
//implementation of MyClass

However, the disadvantage of the C++ approach is:

  • How do you get the attributes for an arbitrary object? You can try dynamic_cast<categoryattribute *>(arbitraryObject) and check to see if the result is not NULL.
  • If you want many attributes, you are doing multiple-inheritance in a big way, which is a Bad Thing. You will probably get collisions eventually.
  • The whole idea of using attributes per se is kind of lost when using inheritance. By definition, it violates the inheritance principle of “is a” (MyClass is a Category? ummm… no). Classes “have” attributes (MyClass has a Category? yes)

Managing Complexity – Part 2

Last time, I covered some generalities and anecdotes about minimizing complexity in my life. Today, I have a few more thoughts about complexity as it relates to software.

Software engineering has continually evolved since the inception of computer programming languages. One way of looking at that evolution is to see it in terms of improvements on complexity management. (This is a bit simplistic, since computers have simultaneously become much more complex.)

The first computers were simple by today’s standards, but the programming methodology was very complex: dials, levers, buttons, or physically connecting wires. Then machine language was developed binary code could be entered on a card, later memory, and interpreted.

These early languages required a perfect familiarity with the machine. If the machine changed, the code changed.

Over the years, the advances in languages have largely been a process of hiding the machine’s underlying complexity. ALGOL came around and hid the machine code and provided the foundation for FORTRAN and C. C built further by providing both structured programming tools and an abstraction of the machine’s language–one foot in each world.

Terminals began to have graphics capabilities and SmallTalk was developed to further hide the complexities of both growing code modules and user interface elements. Java hid the complexities of lower-level code like C and C++, and even took away the concept of a physical machine and substituted its own virtual machine, theoretically consistent across all physical platforms. C# has done much the same for Window–hiding the complexity of thousands of APIs in a hierarchical, intuitive framework of managed code.

Modern processors are beasts of infinite complexity and power compared to the early hulking iron giants, but the languages which we use hide nearly all of the complexity that our forebearers had to deal with on a daily basis.

Now it looks I’ve been really writing about abstraction. It’s extremely strongly related, but I don’t think it’s exactly the same thing. Abstraction is thinking at a higher level; minimizing complexity is thinking less.

Modern languages both abstract away lower level concerns and provide tools to minimize the complexity of things at the highest level.

There is increasingly a proliferation of visual tools, begun with GUI editors, but now including visual code designers.
Aspect-oriented programming and attributes are allowing complexity to be further minimized.

In the future, tools such as these, and increased use of COTS will become vital to accomplishing anything. Software complexity will only increase, but hopefully the trend of tools that minimize complexity will also continue.

Perhaps somebody (not me!) should investiage the theory of a total complexity quotient–a measure of the complexity of a system and the complexity of the tools to develop and manage that system. With this number we could measure if complexity overall is increasing or decreasing, and what/when is the crossover point.

Managing Complexity

Software engineers know that one of the keys to achieving development goals is effective complexity management. The single best way of managing complexity is to remove it from the system completely.

As a simple example, in an earlier version of my BRayTracer project (I really need to come up with a better name!), scene objects were edited by double-clicking on them in the tree view, which opened up a dialog window that displayed the properties of the scene object. The user could make changes and then click OK or Cancel.

This data flow introduced complexity by requiring:

  • An additional window to manage
  • Moving data between the main window and the dialog
  • Making a copy of the scene object
  • Allowing the user to cancel or undo previous actions

The functionality of being able to cancel changes necessitated most of that complexity.

While this example wasn’t overly complex, I still felt there was a better, simpler way. You can probably see a number of possible solutions:

  1. Implement a general undo system (more complexity)
  2. Don’t allow users to cancel changes–they’re committed! (possible, but wise?)
  3. Eliminate the dialog by having a child window on the main form (does not allow cancelling, but removes the additional dialog)
  4. Rethink how objects are edited from the ground up (expensive)

I went with option 3. Obviously, there’s a tradeoff here. I sacrificed some functionality for the sake of simplifying the interface and the code. In fact, in usability testing, many users wanted a way to cancel or undo changes. Someday, I’ll have to go back and add a way of doing it. This illustrates the principle that sometimes complexity is unavoidable for certain features (Undo support, for example, is nearly always very complex to implement effectively) and that often what is really going on is shifting the burden of complexity somewhere else.

Minimization of complexity is also tightly coupled to the principle of optimality (ah…but optimality of what?).

The tendency of developers (at least I assume it’s a tendency–I am, of course, generalizing from my own experience and those people I’ve observed) to minimize complexity is something that can carry over to our normal lives. I notice this myself in a number of ways, most of which probably aggravate Leticia to no end 🙂

  • When driving, I almost aways get in the “optimum” lane as soon as possible, that is, the lane from which I’ll have to make the fewest changes. Changing lanes adds to complexity. Having to worry about changing lanes when it becomes crucial adds too much complexity. While there are exceptions for painfully slow people, I change lanes only about 6 times on my 35 mile commute (4 roads and 4 highways).
  • When I cook, everything is optimized for minimal disruption. All ingredients and equipment are pregathered and then prepared in a way which minimizes work, time, and coordination.
  • When I watch movies at home, I try to optimize the experience by first queing up the movie, setting up the environment, volume, and then making popcorn. That way, once the popcorn is done we can begin watching immediately, while it’s hot. I almost can’t watch a movie without popcorn.