Presented at SOFSEM 2010. http://www.sofsem.cz/sofsem10/
We design systems so that the components will be stable, but their configurations may change, simply by plugging together things in different ways. If we have to change the internal implementation of the components, the impact is minimal, but if we change the interfaces, this will impact all clients.
Procedural abstraction; data abstraction; objects - responsibility driven design; components; frameworks; aspects; models . Each of these is inadequate because it forces us into a particular way of decomposing systems and fails to capture some aspect of change. (Procedural leaves our data dependencies; data abstraction misses object responsibility and specialization; objects are too fine-grained for reuse; frameworks miss explicit client/supplier contracts; components miss cross-cutting dependencies; model-driven approaches don’t address evolution of the transformations …)
We have a mental model of our software system, but do we really understand the system, and how our model is reflected in the system? There is typically a non-trivial gap between the two. A change in the system is normally driven by a change in our mental model, but do we really understand that change? How do we map that to a change in the system? How do we know what to change, and what impact that change will have on the rest of the system? Do we really know where we are going?
(Apologies to Dave Ungar.) Smalltalk takes some very simple principles and applies them uniformly to yield a very dynamic system.
The Smalltalk project was started because Alan Kay felt that a completely now, full-object-oriented system (language and OS) was needed to build the highly interactive systems of the future.
A Smalltalk system consists of an image file, containing all the objects, and a changes file containing the source code of changes to the system. There may be many snapshots of the system for many users. To run the image, you need the VM, and the source code of the base image. This can be shared amongst all users of a version of Smalltalk.
Everything happens by sending messages. Unary messages are simple commands that follow the receiver, a Smalltalk expression. Binary messages are made of arithmetical operators, and have both a receiver (LHS) and an argument (RHS). 3 + 4 sends the message + to the object 3 with argument 4. Keyword messages take multiple arguments. Unlike most languages, there is one keyword per argument. Python supports a similar syntax.
The unit of compilation in Smalltalk is the method, which is (normally) defined using the class browser. Here we see the additional syntax for comments, return statements, and blocks. Missing from this example are cascades and periods as statement separators. Otherwise we see here almost all of the core syntax of Smalltalk.
DEMO . We will see several of the key tools (Workspace, Class Browser, Inspector, Explorer) and we will see how we can explore the running system, query it, and make changes on the fly.
Here we see the core of Smalltalk’s metamodel. Note that by applying uniformly the rule that “Everything is an Object”, most of the metamodel emerges naturally. We only need a couple of more rules, such as “Every object is an instance of a class” and “Every class is an instances of a metaclass”.. - 2@3 is an instance of Point 2@3 is an Object, so Point inherits from Object Point and Object are objects, so are instances of Object class and Point class Object and Point are classes, so their classes must inherit from Class; but every class is an object, so Class inherits from Object Behavior and Class are instances of classes too These classes are all metaclasses, so are instances of Metaclass, a sibling of Class But Metaclass is an object, so an instance of a class (Metaclass class), which is a Metaclass In fact, we can query the running system to check these facts
DEMO . The Debugger is your Friend -- 2&quot; 50 isPerfect - Create ^ self divisors sum = self - Proceed - Create ^ (1 to: self -1 ) select: [ :each | (self rem: each) = 0] - Try it out inside the debugger! - Proceed (2 to: 1000) select: [:each| each isPerfect]
Pharo is a project that aims to produce a clean, efficient and open source Smalltalk for research and professional development.
Moose has been developed since 1997 in the context of a European project (FAMOOS) and has been used as a platform for numerous reverse engineering projects. The current version of Moose is hosted in Pharo.
Moose is based on a small and simple metamodel to represent object-oriented software source code. Much of the power of Moose lies in the fact that the metamodel can easily be extended by tools to represent new kinds of information (such as dynamic information, history, features, and so on).
System complexity - Clone evolution view Class blueprint - Topic Correlation Matrix - Distribution Map for topics spread over classes in packages Hierarchy Evolution view - Ownership Map
CodeCrawler was the original visualization engine in Moose for “polymetric views”, which map software metrics to simple graphics. CodeCrawler was essentially a framework, so defining new visualizations entailed subclassing existing CodeCrawler classes and specializing them.
DEMO . Mondrian takes a different, component-based approach. Instead of subclassing, you instantiate and configure Mondrian components, which makes it much easier to define new visualizations. We will see how to define a System Complexity view, and we will see how Mondrian is integrated into Moose.
Moose is still in development, but is available for free download.
OO inheritance is nice when conceptual hierarchies coincide with subtyping and reuse by inheritance, but sometimes these simply do not match. Inheritance of features, for example, has nothing to do with specialization hierarchies. More generally, we can have difficulty reusing features across hierarchies of a complex OO application, leading to code duplication or other anomalies.
Traits are fine-grained components that allow you to reuse methods in otherwise unrelated classes. (You can think of this as the Mr Potato Head model of reuse!)
An old idea is to let class inherit from multiple parents (either classes or “mixins”). The problem is that conflicts may easily arise. The usual solution is to provide some sort of default conflict resolution strategy. Unfortunately this can be unstable in the face of changes (a small change may affect distant classes in unexpected ways). Traits, by contrast, give the composing class complete control over the composition.
We apply the principles of Smalltalk to model Traits and ClassTraits in a similar way to Classes and Metaclasses, and we exploit traits themselves to share their behaviour in Pharo. (This, of course, is known as “eating your own dog food”.)
Debuggers have severe limitations when trying to track down the source of bugs that do not immediately manifest themselves as errors. The problem is that the run-time stack no longer contains the information which object was responsible for the error. Our approach is to track the flow of object references by means of first-class aliases. A debugger can then trace back through the aliases to the origin of any error.
DEMO . Object Flow Analysis requires a modified VM that models references as objects. Every time a value is created or passed, a new “alias” is created, which also keeps track of the value of the object at the moment that the reference was passed. This effectively allows a debugger to travel “back in time”. (There is also a back-in-time debugger which we do not demo.)
Back-in-time debuggers capture program history to help tracking down defects where in a traditional debugger the offending context would be gone already. For example, we need to find out where a variable that contains an unexpected value was modified. It is likely that this context is not on the current execution stack anymore The open problem of back-in-time debuggers is that the history quickly consumes too much memory We implemented the object flow model in the VM to capture past object state and object flow The benefit of this approach is that obsolete history can be garbage collected (which is better than just throwing away the oldest data first) Using the GC is possible because the history is represented as real objects
If we need to make changes to a running system, we want to be sure that our changes will not impact clients using it. The solution is to strictly scope all changes to so-called “changeboxes”. Clients see the deployed branch, while we may make changes to the development branch *of the same running system*. This way we can test the system with the same run-time data. Once we are certain of our changes, we can merge them into deployed branch.
DEMO . Every changebox has a number of change specifications and a number of previous ancestors. Different merge strategies can be introduced to resolve conflicts.
Transcript of "Lessons in Software Evolution Learned by Listening to Smalltalk"
Lessons in Software Evolution Learned by Listening to Smalltalk Oscar Nierstrasz scg.unibe.ch
Research Team Adrian Lienhard Adrian Kuhn Fabrizio Perin Lukas Renggli Jorge Ressia David Roethlisberger Niko Schwarz Toon Verwaest Erwann Wernli
Roadmap The trouble with change Smalltalk in a Nutshell Taming Software Change Epilogue — Bringing Models Closer to Code
Roadmap The trouble with change Smalltalk in a Nutshell Taming Software Change Epilogue — Bringing Models Closer to Code The trouble with change
How we enable change Package what is stable Define an interface for configuration
The Trouble with Software Change Understanding the system Bridging the gap Specifying change Managing change Understanding change
Roadmap The trouble with change Smalltalk in a Nutshell Taming Software Change Epilogue — Bringing Models Closer to Code Smalltalk in a Nutshell
To thine own self be true Less is More The object model “is as simple as possible, but no simpler” Reify Everything You can change a Running System “ Everything is an object” is applied consistently Smalltalk models itself, so changing the model changes the system
The origins of Smalltalk Alan Kay’s Dynabook project (1968) Alto — Xerox PARC (1973)
Everything happens by sending messages 2 raisedTo: 1 + 3 factorial 128 First unary, then binary, then keyword: factorial + raisedTo:
A typical method <= aPoint "Answer whether the receiver is neither below nor to the right of aPoint." ^ x <= aPoint x and: [y <= aPoint y] (2@3) <= (5@6) true Keyword message Instance variable Block Binary message Unary message Method name Argument Comment Return
Explicit metamodels enable change Smalltalk Extensible meta model Model repository The Story of Moose: an Agile Reengineering Environment. Oscar Nierstrasz, Stéphane Ducasse, and Tudor Gîrba. ESEC/FSE 2005 Navigation Metrics Querying Grouping Smalltalk Java C++ COBOL … CDIF XMI External Parser CodeCrawler ConAn Van ... Hapax
Class = superclass + state + traits + glue Traits provide and require methods The composing class retains control Traits: A Mechanism for fine-grained Reuse. Stéphane Ducasse, Oscar Nierstrasz, Nathanael Schärli, Roel Wuyts and Andrew Black. ACM TOPLAS, March 2006
Roadmap The trouble with change Smalltalk in a Nutshell Taming Software Change Epilogue — Bringing Models Closer to Code Epilogue — Bringing Models Closer to Code
Systems that support change need to be model-centric and context-aware Model-centric systems are self-describing and can be adapted dynamically Context-aware systems control the scope of change to static or dynamic contexts
A particular slide catching your eye?
Clipping is a handy way to collect important slides you want to go back to later.