Modern Programming: Object Oriented Programming and Best Practices
上QQ阅读APP看书,第一时间看更新

Building Objects

What then is a personal computer? One would hope that it would be both a medium for containing and expressing arbitrary symbolic notions, and also a collection of useful tools for manipulating these structures, with ways to add new tools to the repertoire.

Alan C. Kay, "A Personal Computer for Children of All Ages"

Smalltalk is both a very personal and a very live system. This affected the experience of using, building, and sharing objects built in the system, which were all done in a way very different from the edit-compile-assemble-link-run workflow associated with COBOL and later languages.

As an aside, I'm mostly using "Smalltalk" here to mean "Smalltalk-80 and later things that derived from it without changing the experience much." Anything that looks and feels "quite a lot like" a Smalltalk environment, such as Pharo or Squeak, is included. Things that involve a clearly more traditional workflow, like Java or Objective-C, are excluded. Where to draw the line is left intentionally ambiguous: try out GNU Smalltalkhttp://smalltalk.gnu.org/) and decide whether you think it is "a Smalltalk" or not.

A Smalltalk environment is composed of two parts: the virtual machine can execute Smalltalk bytecode, and the image contains Smalltalk sources, bytecode, and the definitions of classes and objects.

So, the image is both personal and universal. Personal in the sense that it is unique to me, containing the objects that I have created or acquired from others; universal in the sense that it contains the whole system: there are no private frameworks, no executables that contain the Directory Services objects but not the GUI objects, and no libraries to link before I can use networking.

This makes it very easy to build things: I make the objects I need, and I find and use the objects that I can already take advantage of. On the other hand, it makes sharing quite fraught: if I need to make a change to a system object for some reason, you cannot take in my change without considering the impact that change will have on everything else in your image. If you want to add my class to your image, you have to make sure that you don't already have a class with that name. We cannot both use the same key on the Smalltalk dictionary for different purposes.

It's also live, in that the way you modify the image is by interacting with it. Methods are implemented as Smalltalk bytecode (though that bytecode may simply be a request to execute a "primitive method" stored on the virtual machine) by writing the method into a text field and sending a message to the compiler object asking it to compile the method. Classes are added by sending a message to an existing class, asking it to create a subclass. Objects are created by sending a new message to a class.

While there is editing, compilation and debugging, this all takes place within the image. This makes for a very rapid prototype and feedback experience (unsurprising, as one vision behind Smalltalk was to let children explore the world and computers in tandem — https://mprove.de/diplom/gui/kay72.html. Any change you make affects the system you are using, and its effects can be seen without rebuilding or quitting an application to launch it again. Similarly, the system you are using affects the changes you are making: if an object encounters a message to which it does not respond or an assertion is not satisfied, then the debugger is brought up, so you can correct your code and carry on.

The fast feedback afforded by building UIs out of the objects that represent UI widgets was used by lots of Rapid Application Development tools, such as NeXT's Interface Builder, Borland's Delphi and Microsoft's Visual Basic. These tools otherwise took a very different position to the trade-offs described previously.

While an IDE like Eclipse might be made out of Java, a Java developer using Eclipse is not writing Java that modifies the Eclipse environment, even where the Java package they are writing is an Eclipse plugin. Instead, they use the IDE to host tools that produce another program containing their code, along with references to other packages and libraries needed for the code to work.

This approach is generic rather than personal (anyone with the same collection of packages and libraries can make the standalone code work without any step integrating things into their image) and specific rather than universal (the resulting program – mistakes aside – contains only the things needed by that program).

This one key difference – that there is a "build phase" separating the thing you're making from the thing you're making it in – is the big distinction between the two ways of building objects, and one of the ways in which the transfer of ideas in either direction remains imperfect.

Those Rapid Application Development tools with their GUI builders let you set up the UI widgets from the vendor framework and configure their properties, by working with live objects rather than writing static code to construct a UI. In practice, the limitations on being able to do so are:

  • To understand the quality of a UI, you need to work with the real information and workflows the interface exposes, and that is all in the program source that's sat around in the editor panes and code browsers, waiting to be compiled and integrated with the UI layout into the (currently dormant) application.
  • Changes outside the capability of the UI editor tool cannot be reflected within it. Changing the font on a label is easily tested; writing a new text transform to be applied to the label's contents is not.
  • The bits of a UI that you can test within a UI builder are usually well-defined by the platform's interface guidelines anyway, so you never want to change the font on a label.

In practice, even with a UI builder you still have an edit-build-debug workflow.

A similar partial transfer of ideas can be seen in test-driven development. A quick summary (obviously, if you want the long version, you could always buy my bookhttps://qualitycoding.org/test-driven-ios-development-book/) is that you create an object incrementally by thinking of the messages you want to send it, then what it should do in response, then you send those messages and record whether you get the expected responses. You probably do not get the expected response, as you have not told the object how to behave yet, so you add the bit of behavior that yields the correct response and move on to the next message, after doing a bit of tidying up.

In the world of Smalltalk, we have already seen that something unexpected happening leaves you in the debugger, where you can patch up the thing that's broken. So, the whole of the preceding process can be resummarised as "think of a message, type it in, hit do it, edit the source until the debugger stops showing up," and now you have an increment of working software in your image.

In the world of Java, even though the same person wrote both the SUnit and JUnit testing tools, the process is (assuming you already have a test project with the relevant artefacts):

  1. Write the code to send the message
  2. Appease the compiler
  3. Build and run the test target
  4. Use the output to guide changes, back in the editor
  5. Repeat 3 and 4 until the test passes

So, there's a much longer feedback loop. That applies to any kind of feedback, from acceptance testing to correctness testing. You can't build the thing you're building from within itself, so there's always a pause as you and your computer switch context.

The reason for this context switch is only partly due to technology: in 2003, when Apple introduced Xcode, they made a big deal of "fix and continue," a facility also available in Java environments, amongst others: when the source code is changed, within certain limits, the associated object file can be rebuilt and injected into the running application without having to terminate and re-link it. However, that is typically not how programmers think about their activities. The worldview that lends us words like "toolchain" and "pipeline" is one of sequential activities, where a program may end up "in production" but certainly doesn't start there. People using the programs happens at the end, when the fun is over.