Home ] Overview ] Publications ] OMG ] Case Study ] Support ] Other Work ] Search ]
horiz-space.GIF (952 bytes)

Concept Map
How to...
Model Kinds
This 'n That






Relationships with Traditional OOA

The best practices of traditional OOA/D are well defined in Catalysis. However, Catalysis makes certain distinctions that clarify issues that have remained unresolved in traditional methods. If you are experienced with an existing method, here are some things to be aware of (you will find more details in the FAQ):

Abstraction, Precision, and Deferred Decisions

All good methods attempt to support abstraction. Catalysis, however, takes the position that abstraction too often equates to fuzzy, ambiguous, and unreliable; and aims to make even abstract models precise enough to be the basis for traceability and testing. The specific kinds of abstraction supported effectively separate decisions that are best deferred. Through all this there is still a focus on the language of the client, and techniques to make the models closely match the client's understanding and natural expression of the problem. 

Refinement and Fractal Modeling

The idea of refinement is central to Catalysis, and notably lacking from the most popular OOA/D methods (Fusion and Syntropy are notable exceptions). It lets you make very clear separations between abstract descriptions and particular ways of realizing them; yet it supports traceability and testing in a way far more concrete than the naive OOAD view of "model the domain and elaborate into your code". In particular, it makes fractal modeling practical, so the same techniques are used at all levels without any loss of precision or generality. We model a software system, corporate department, and Java object using the same techniques; and similarly for a business task, system use-case, and Java message.

Deferred Localization of Behavior

Traditional OOA/D approaches assign behaviors to classes very early in the development process, and consequently lose sight of the overall system behavior. Jacobsen's use-case approach somewhat rectified this by stepping back to the user-tasks and then working out the software object interactions to realize that task. What it missed, however, is the power of a uniformly fractal approach based upon refinement.

A traditional use case describes the behavior of a group of objects (system + actors); a traditional operation in OOP describe a single object's response to a request.
You can (and should) describe the net effect of a use case separately from choosing an interaction sequence. That net effect is its goal, and many use case practitioners (e.g. Cockburn) recognize its value. This net effect may involve multiple actors + system. A Catalysis joint action covers precisely this form of use case, and lets you define that goal precisely so it applies to many different possible realizations via interactions.
Part of the design process is about assigning responsibilities, and eventually, describing the detailed protocols each object supports and how they combine to realize the use case. Behavior localization in Catalysis, with refinement, covers precisely this form of use case.
The same process applies to the system and its users (you do "business design" to decide what the system is responsible for, and how the actors must correspondingly behave), and to any group of objects in a software design. Hence it can be used recursively.
You can treat the entire system as one object for the purpose of localization at the requirements level; and treat any component as an object at intermediate levels, until you get to OOP classes.
Anytime you feel comfortable with early localization of behaviors, you can always do so in Catalysis by using the effect construct: this allows you to describe behaviors in terms of localized effects ("guest check into the hotel result: the room is occupied and the guest account is charged") without committing to the actual interactions in the implementation).

Traditional OOAD methods (OMT, Booch, Objectory) have focused on the class as the main unit of modeling, often mixing implementation concerns (sharing of data members and method bodies) with specification concerns. This is a well recognized shortfall of traditional OO languages as well, rectified in languages like Java by explicit separation of interfaces from implementation constructs.

Catalysis can correctly be considered an interface-centric approach to component and object development. Types model interfaces abstractly, yet precisely (using attribute models and action refinement), and tests specified against interfaces apply independent of the implementation. Collaborations model sets of related types and their roles in interactions - precisely the basis of design-patterns. Collaboration composition allows us to build designs by composing fragments of design or architectural patterns that are also interface-based. And the pluggable-framework approach to code gives us alternate mechanisms to using this approach in the implementation as well.

Frameworks and (entire) model inheritance

Catalysis frameworks are expressive enough to model specification patterns, patterns of attributes and invariants, patterns of generic classes, collaboration and design patterns, and even patterns of methods and instance variables in code. They capture the key part of such patterns: the relationships between the parts that hold even as the individual parts themselves are specialized or substituted for.

Attributes vs. Instance Variables

Catalysis makes a strong distinction between an attribute modeled on a type, and an instance variable stored in a class. Attributes abstract stored data; the key questions to ask when modeling an attribute are:

Does it make it convenient for me to express some constraint or behavior with precision?
Would it be a valid abstraction (i.e. computable from) any stored implementation?

This very simple idea provides immediate benefit at every level:

business tasks - effects and constraints expressed in terms of attributes of business objects, like customer account or product price independent of how, and even if, this information is represented in software.
code - where attributes on an interface or type help specify its behavior precisely without committing to how a particular class will represent that information.
Parameterized Attributes

Once you understand that model attributes do not have to be directly stored, but may be computed instead, it is a simple step to understand parameterized attributes. Just as it is reasonable to model the price of a product as an attribute of Product, so also we can model the price (for a particular quantity) of a product as a parameterized attribute: Product. price_of (quantity): $. What we are doing here is saying that there are potentially many different prices for different quantities of products, deferring the details of a specific quantity-based pricing policy without any loss of precision.

Why not model these as operations? Because, in Catalysis the fundamental distinction is between abstraction of state (attribute) and abstraction of behavior (action). An attribute, whether parameterized or not, is an abstraction of state; attributes are related to other attributes by static invariants, while actions are specified by postconditions.

Design vs. Specification Types

When modeling, there are some objects whose behavior is of primary importance (e.g. a PrintSystem at the analysis level); and others that help to structure the description of its attributes and behaviors (e.g. a PrintJob may be a useful way to conceptualize and make precise the behavior of the PrintSystem, but it may never actually exist in an implementation). In Catalysis these two uses are referred to as Design Types and Specification Types. Design types will have actions of interest, while specification types will often only have attributes (and, optionally, effects).

Responsibility-Driven Design

How do we describe "responsibilities" in Catalysis? There is a surprisingly crisp answer, allowing us to use responsibility-driven design and support it with precise models whenever appropriate:

Responsibilities for "knowing" things are modeled with attributes, possibly with parameters. Thanks to refinement, this defers decisions about instance variables.
"A product knows the price for a given quantity of itself"
Product.priceFor (quantity)
Responsibilities for "doing" things are modeled with abstract actions, effects, and invariants. Thanks to refinement, this defers decisions about concrete interface or methods.
A product is responsible for maintaining its inventory above a threshold
invariant Product::  inventory > threshold
A machine is responsible for scheduling its own maintenance
action Machine::schedule_maintenance
Views and Multiple Specs

There is no need for a model or specification of an object type to say all there is to be said about that type in a single diagram or piece of text. In fact, most complex models are best broken into parts (views, or subject areas); and different aspects of the same objects and actions are described incrementally in those subject areas. This makes the models and documentation more maintainable, since you could locally change certain aspects (e.g. performance, concurrency, logging) without editing the contents of the others.  

Use Cases

Use cases are a perennial favourite, and are strongly supported in Catalysis. We do make some critical distinctions and clarifications about the different between the abstract use-case itself, the sequence of finer-grained use-cases that realizes it, and the meaning of <<include>> and <<extend>> relationships.

Email suggestions to webmaster@catalysis.org. All contents copyrighted 1998.