April 17, 2014 3:30 PM

The "Subclass as Client" Pattern

A few weeks ago, Reginald Braithwaite wrote a short piece discouraging us from creating class hierarchies. His article uses Javascript examples, but I think he intends his advice to apply everywhere:

So if someone asks you to explain how to write a class hierarchy? Go ahead and tell them: "Don't do that!"

If you have done much object-oriented programming in a class-based language, you will recognize his concern with class hierarchies: A change to the implementation of a class high up in the hierarchy could break every class beneath it. This is often called the "fragile base class" problem. Fragile code can't be changed without a lot of pain, fixing all the code broken by the change.

I'm going to violate the premise of Braithwaite's advice and suggest a way that you can make your base classes less fragile and thus make small class hierarchies more attractive. If you would like to follow his advice, feel free to tell me "Don't do that!" and stop reading now.

The technique I suggest follows directly from a practice that OO programmers use to create good objects, one that Braithwaite advocates in his article: encapsulating data tightly within an object.

JavaScript does not enforce private state, but it's easy to write well-encapsulated programs: simply avoid having one object directly manipulate another object's properties. Forty years after Smalltalk was invented, this is a well-understood principle.

The article then shows a standard example of a bank account object written in this style, in which client code uses the object without depending on its implementation. So far, so good.

What about classes?

It turns out, the relationship between classes in a hierarchy is not encapsulated. This is because classes do not relate to each other through a well-defined interface of methods while "hiding" their internal state from each other.

Braithwaite then shows an example of a subclass method that illustrates the problem:

    ChequingAccount.prototype.process = function (cheque) {
      this._currentBalance = this._currentBalance - cheque.amount();
      return this;
    }

The ChequingAccount directly accesses its _currentBalance member, which it inherits from the Account prototype. If we now change the internal implementation of Account so that it does not provide a _currentBalance member, we will break ChequingAccount.

The problem, we are told, is that objects are encapsulated, but classes are not.

... this dependency is not limited in scope to a carefully curated interface of methods and behaviour. We have no encapsulation.

However, as the article pointed out earlier, JavaScript does not enforce private state for objects! Even so, it's easy to write well-encapsulated programs -- by not letting one object directly manipulate another object's properties. This is a design pattern that makes it possible to write OO programs even when the language does not enforce encapsulation.

The problem isn't that objects are encapsulated and classes are not. It's that we tend treat superclasses differently than we treat other classes.

When we write code for two independent objects, we think of their classes as black boxes, sealed off from external inspection. The data and methods defined in the one class belong to it and its objects. Objects of one class must interact with objects of another via a carefully curated interface of methods and behavior.

But when we write code for a subclass, we tend to think of the data and methods defined in the superclass as somehow "belonging to" instances of the subclass. We take the notion of inheritance too literally.

My suggestion is that you treat your classes like you treat objects: Don't let one class look into another class and access its state directly. Adopt this practice even when the other class is a superclass, and the state is an inherited member.

Many OO programs have this pattern. I usually call it the "Subclass as Client" pattern. Instances of a subclass act as clients of their superclass, treating it -- as much as possible -- as an independent object providing a set of well-defined behaviors.

When code follows this pattern, it takes Braithwaite's advice for designing objects up a level and follows it more faithfully. Even instance variables inherited from the superclass are encapsulated, accessible only through the behaviors of the superclass.

I don't program in Javascript, but I've written a lot of Java over the years, and I think the lessons are compatible. Here's my story.

~~~~~

When I teach OOP, one of the first things my students learn about creating objects is this:

All instance variables are private.

Like Javascript, Java doesn't require this. We can tell the compiler to enforce it, though, through use of the private modifier. Now, only methods defined in the same class can access the variable.

For the most part, students are fine with this idea -- until we learn about subclasses. If one class extends another, it cannot access the inherited data members. The natural thing to do is what they see in too many Java examples in their texts and on the web: change private variables in the superclass to protected. Now, all is right with the world again.

Except that they have stepped directly into the path of the fragile base class problem. Almost any change to the superclass risks breaking all of its subclasses. Even in a sophomore OO course, we quickly encounter the problem of fragile base classes in our programs. But other choice do we have?

Make each class a server to its subclasses. Keep the instance variables private, and (in Braithwaite's words) carefully curate an interface of methods for subclasses to use. The class may be willing to expose more of its identity to its subclasses than to arbitrary objects, so define protected methods that are accessible only to its subclasses.

This is an intentional extension of the class's interface for explicit interaction with subclasses. (Yes, I know that protected members in Java are accessible to every class in the package. Grrr.)

This is the same discipline we follow when we write well-behaved objects in any language: encapsulate data and define an interface for interaction. When applied to the class-subclass relationship, it helps us to avoid the dangers of fragile base classes.

Forty years after Smalltalk was invented, this principle should be better understood by more programmers. In Smalltalk, variables are encapsulated within their classes, which forces subclasses to access them through methods defined in the superclass. This language feature encourages the writer of the class to think explicitly about how instances of a subclass will interact with the class. (Unfortunately, those methods are public to the world, so programmers have to enforce their scope by convention.)

Of course, a lazy programmer can throw away this advantage. When I first learned OO in Smalltalk, I quickly figured out that I could simply define accessors with the same names as the instance variables. Hurray! My elation did not last long, though. Like my Java students, I quickly found myself with a maze of class-subclass entanglements that made programming unbearable. I had re-invented the Fragile Base Class problem.

Fortunately, I had the Smalltalk class library to study, as well as programs written by better programmers than I. Those programs taught me the Subclass as Client pattern, I learned that it was possible to use subclasses well, when classes were designed carefully. This is just one of the many ways that Smalltalk taught me OOP.

~~~~~

Yes, you should prefer composition to inheritance, and, yes, you should strive to keep your class hierarchies as small and shallow as possible. But if you apply basic principles of object design to your superclasses, you don't need to live in absolute fear of fragile base classes. You can "do that" if you are willing to carefully curate an interface of methods that define the behavior of a class as a superclass.

This advice works well only for the class hierarchies you build for yourself. If you need to work with a class from an external package you don't control, then you can't be control the quality of those class's interfaces. Think carefully before you subclass an external class and depend on its implementation.

One technique I find helpful in this regard is to build a wrapper class around the external class, carefully define an interface for subclasses, and then extend the wrapper class. This at least isolates the risk of changes in the library class to a single class in my program.

Of course, if you are programming in Javascript, you might want to look to the Self community for more suitable OO advice than to Smalltalk!


Posted by Eugene Wallingford | Permalink | Categories: Patterns, Software Development

April 11, 2014 10:29 AM

Famous Last Words

With respect for Rands, I've adapted a paragraph from one of his engineering management pieces to my experience:

You're a department chair now. Congratulations. Either you sucked at teaching and research and wanted to try a different avenue of influence, or you're fed up with every other chair you've worked for and now you're going to REALLY GOING TO SHOW US how it's done.

It's much easier to be fed up than to show people how it's really done. Trust me.

This week has been a challenge. I have faced several things that need to be done well. Most of them are, in ways, peripheral to the core mission of the department and university, but they are important to students, faculty, and external stakeholders. If they are handled poorly, the experience people have will undercut everything else we do well.

So these are the things that a department head must do well, in the trenches and with not much fanfare. They consume a lot of emotional energy an introvert like me, and they don't offer a lot of immediate, tangible rewards. But they are worth my attention.

After a number of years in this position, I have found that it's a lot easier for me to imagine knocking the ball out of the park than to make contact. I keep working at it.

In this regard, I have begun to learn to combine empathy with the proper application of a scientific mindset to how people behave. This idea is echoed in a passage from Timothy Burke:

Anything that real people do in the world is by definition interesting. By "interesting", I mean worthy of the kind of investigation that puts curiosity and honesty well before judgment.

Curiosity about individual people and honest communication are generally my best tools in the trenches. Forgetting to turn off the judgment centers in my brain never works well.


Posted by Eugene Wallingford | Permalink | Categories: Managing and Leading

April 09, 2014 3:26 PM

Programming Everywhere, Vox Edition

In a report on the launch of Vox Media, we learn that line between software developers and journalists at Vox is blurred, as writers and reporters work together "to build the tools they require".

"It is thrilling as a journalist being able to envision a tool and having it become a real thing," Mr. Topolsky said. "And it is rare."

It will be less rare in the future. Programming will become a natural part of more and more people's toolboxes.


Posted by Eugene Wallingford | Permalink | Categories: Computing, General

April 04, 2014 12:43 PM

Creative Recombination of Existing Ideas

In a post on his move to California, Jon Udell notes that he may be out of step with the dominant view of the tech industry there:

And I think differently about innovation than Silicon Valley does. I don't think we lack new ideas. I think we lack creative recombination of proven tech, and the execution and follow-through required to surface its latent value.

As he found with the Elm City project, sometimes a good idea doesn't get traction quickly, even with sustained effort. Calendar aggregation seems like such a win even for a university the size of mine, yet a lot of folks don't get it. It's hard to know whether the slowness results from the idea, the technology, or the general resistance of communities to change how they operate.

In any case, Udell is right: there is a lot of latent value in the "creative recombination" of existing ideas. Ours is a remix culture, too. That's why it's so important to study widely in and out of computing, to build the base of tools needed to have a great idea and execute on it.


Posted by Eugene Wallingford | Permalink | Categories: Computing

March 31, 2014 3:21 PM

Programming, Defined and Re-imagined

By Chris Granger of Light Table fame:

Programming is our way of encoding thought such that the computer can help us with it.

Read the whole piece, which recounts Granger's reflection after the Light Table project left him unsatisfied and he sought answers. He concludes that we need to re-center our idea of what programming is and how we can make it accessible to more people. Our current idea of programming doesn't scale because, well,

It turns out masochism is a hard sell.

Every teacher knows this. You can sell masochism to a few proud souls, but not to anyone else.


Posted by Eugene Wallingford | Permalink | Categories: Computing, Software Development

March 29, 2014 10:06 AM

Sooner

That is the advice I find myself giving to students again and again this semester: Sooner.

Review the material we cover in class sooner.

Ask questions sooner.

Think about the homework problems sooner.

Clarify the requirements sooner.

Write code sooner.

Test your code sooner.

Submit a working version of your homework sooner. You can submit a more complete version later.

A lot of this advice boils down to the more general Get feedback sooner. In many ways, it is a dual of the advice, Take small steps. If you take small steps, you can ask, clarify, write, and test sooner. One of the most reliable ways to do those things sooner is to take small steps.

If you are struggling to get things done, give sooner a try. Rather than fail in a familiar way, you might succeed in an unfamiliar way. When you do, you probably won't want to go back to the old way again.


Posted by Eugene Wallingford | Permalink | Categories: Patterns, Software Development, Teaching and Learning

March 18, 2014 2:30 PM

Deploy So That You Can Learn The Rest

In this interview prior to Monday's debut of FiveThirtyEight, Joe Coscarelli asked Nate Silver if the venture was ready to launch. Silver said that they they were probably 75-80% ready and that it was time to go live.

You're going to make some mistakes once you launch that you can't really deal with until you actually have a real product.

If they waited another month, they'd probably feel like they were ... 75-80% ready. There are some things you can't learn "unless your neck is on the line".

It ought not be surprising that Silver feels this way. His calling card is using data to make better decisions. Before you can have big data, or good data, you have to have data. It is usually better to start collecting it now than to start collecting it later.


Posted by Eugene Wallingford | Permalink | Categories: Software Development

March 14, 2014 3:47 PM

We're in a Dr. Seuss Book

Sass, Flexbox, Git, Grunt? Frank Chimero whispers:

(Look at that list, programmers. You need to get better at naming things. No wonder why people are skittish about development. It's like we're in a Dr. Seuss book.)

For new names, it's time to hunt.
I will not Git!
I will not Grunt!

Nevermore shall we let these pass.
No more Flexbox!
No more Sass!


Posted by Eugene Wallingford | Permalink | Categories: Software Development

March 12, 2014 3:55 PM

Not Content With Content

Last week, the Chronicle of Higher Ed ran an article on a new joint major at Stanford combining computer science and the humanities.

[Students] might compose music or write a short story and translate those works, through code, into something they can share on the web.

"For students it seems perfectly natural to have an interest in coding," [the program's director] said. "In one sense these fields might feel like they're far apart, but they're getting closer and closer."

The program works in both directions, by also engaging CS students in the societal issues created by ubiquitous networks and computing power.

We are doing something similar at my university. A few years ago, several departments began to collaborate on a multidisciplinary program called Interactive Digital Studies which went live in 2012. In the IDS program, students complete a common core of courses from the Communication Studies department and then take "bundles" of coursework involving digital technology from at least two different disciplines. These areas of emphasis enable students to explore the interaction of computing with various topics in media, the humanities, and culture.

Like Stanford's new major, most of the coursework is designed to work at the intersection of disciplines, rather than pursuing disciplines independently, "in parallel".

The initial version of the computation bundle consists of an odd mix of application tools and opportunities to write programs. Now that the program is in place, we are finding that students and faculty alike desire more depth of understanding about programming and development. We are in the process of re-designing the bundle to prepare students to work in a world where so many ideas become web sites or apps, and in which data analytics plays an important role in understanding what people do.

Both our IDS program and Stanford's new major focus on something that we are seeing increasingly at universities these days: the intersections of digital technology and other disciplines, in particular the humanities. Computational tools make it possible for everyone to create more kinds of things, but only if people learn how to use new tools and think about their work in new ways.

Consider this passage by Jim O'Loughlin, a UNI English professor, from a recent position statement on the the "digital turn" of the humanities:

We are increasingly unlikely to find writers who only provide content when the tools for photography, videography and digital design can all be found on our laptops or even on our phones. It is not simply that writers will need to do more. Writers will want to do more, because with a modest amount of effort they can be their own designers, photographers, publishers or even programmers.

Writers don't have to settle for producing "content" and then relying heavily on others to help bring the content to an audience. New tools enable writers to take greater control of putting their ideas before an audience. But...

... only if we [writers] are willing to think seriously not only about our ideas but about what tools we can use to bring our ideas to an audience.

More tools are within the reach of more people now than ever before. Computing makes that possible, not only for writers, but also for musicians and teachers and social scientists.

Going further, computer programming makes it possible to modify existing tools and to create new tools when the old ones are not sufficient. Writers, musicians, teachers, and social scientists may not want to program at that level, but they can participate in the process.

The critical link is preparation. This digital turn empowers only those who are prepared to think in new ways and to wield a new set of tools. Programs like our IDS major and Stanford's new joint major are among the many efforts hoping to spread the opportunities available now to a larger and more diverse set of people.


Posted by Eugene Wallingford | Permalink | Categories: Computing, General, Teaching and Learning

March 11, 2014 4:52 PM

Change The Battle From Arguments To Tests

In his recent article on the future of the news business, Marc Andreessen has a great passage in his section on ways for the journalism industry to move forward:

Experimentation: You may not have all the right answers up front, but running many experiments changes the battle for the right way forward from arguments to tests. You get data, which leads to correctness and ultimately finding the right answers.

I love that clause: "running many experiments changes the battle for the right way forward from arguments to tests".

While programming, it's easy to get caught up in what we know about the code we have just written and assume that this somehow empowers us to declare sweeping truths about what to do next.

When students are first learning to program, they often fall into this trap -- despite the fact that they don't know much at all. From other courses, though, they are used to thinking for a bit, drawing some conclusions, and then expressing strongly-held opinions. Why not do it with their code, too?

No matter who we are, whenever we do this, sometimes we are right, and sometimes, we are wrong. Why leave it to chance? Run a simple little experiment. Write a snippet of code that implements our idea, and run it. See what happens.

Programs let us test our ideas, even the ideas we have about the program we are writing. Why settle for abstract assertions when we can do better? In the end, even well-reasoned assertions are so much hot air. I learned this from Ward Cunningham: It's all talk until the tests run.


Posted by Eugene Wallingford | Permalink | Categories: General, Software Development, Teaching and Learning