3.4 Invariants and rulesets
In performing object-oriented analysis and building a model, a large number of lessons can be learnt from AI systems built using semantic nets and from semantic data modelling. Specifications that exhibit encapsulation of attributes and operations are all very well but do not necessarily contain the meaning intended by the analyst or the user. To reuse the specification of an object we should be able to read from it what it knows (attributes), what it does (operations), why it does it and how it is related to other objects' interfaces. It is our position that this semantic content is partly contained in the structures of association, classification, composition and usage and by the assertions, invariants and rulesets which describe the behaviour.
The fact is that all semantics limit reuse, although they make it safer. For example, inheritance does so; and so does anything that makes the meaning of an object more specific. In system specification, both aspects are equally important and the trade-off must be well understood and managed with care, depending on the goals of the analysts and their clients.
One must also be aware of the need to decide whether rules belong to individual operations or to the object as a whole. There is no principled reason why operations cannot be expressed in a rule-based language. However, the distinction to be made here is not between the form of expression but the content of the rules. Rules that relate several operations do not belong within those operations and rules which define dependencies between attributes also refer to the object as a whole. Conversely, rules that concern the encapsulated state of the object belong within one of its operations. The most important kind of 'whole object' rules are control rules which describe the behaviour of the object as it participates in structures that it belongs to: rules to control the handling of defaults, multiple inheritance, exceptions and general associations with other objects.
The introduction of encapsulated rulesets was a novel aspect of SOMA. It enhances object models by adding a set of rulesets to each object. Thus, while an object is normally thought to consist of Identifier, Attributes and Operations, a SOMA object consists of Identifier, Attributes, Operations and Rulesets. Rulesets are composed of an unordered set of assertions and rules of either 'if/then' or 'when/then' form. This modelling extension has a number of interesting consequences, the most remarkable of which is that these objects - which are local entities - can encapsulate the rules for global system behaviour; rather as DNA is supposed to encapsulate the morpheme. A further consequence is that objects with rulesets can be regarded as intelligent agents for expert systems developments.
It is widely agreed that it is quite insufficient to specify only the attributes and operations (the signature) of an object. To specify the object completely we must say how these are allowed to interact with each other and what rules and constraints must be obeyed by the object as a whole to maintain its integrity as such. Some languages, such as Eiffel, and some methods, such as BON, Catalysis or Syntropy, achieve a partial solution by using assertions. Assertions in such systems are of two kinds: assertions about the operations and assertions about the whole object. The former are typically pre- and post-conditions while the latter are called class invariants. SOMA and Catalysis add invariance conditions to the operational assertions and SOMA generalizes class invariants to rulesets - which can be chained together to infer new information. There are also assertion facets representing attribute constraints. Here are the definitions:
The facets of an operation may include more than one assertion of any of these types. Assertions may be represented by state-transition diagrams as we shall see.
Object assertions and rulesets
The normal assumption behind the above definitions is that the logic to be used is standard first order predicate calculus (FOPC). We make no such assumption although FOPC is the usual default logic. Other logics that can be used include temporal, fuzzy, deontic, epistemic and non-monotonic logic. Each ruleset in a class determines its own logic locally, although it would be unusual to mix logics in the same class.
We have already seen the distinction between a type and a class: a type has no implementation. We can now distinguish between types and interfaces. An interface is a list of the messages that can be sent to an object with their parameters and return types. Depending on the interpretation, this may include the get and set operations on attributes. This concept is sometimes referred to as the signature of a type. A type on the other hand is a full specification including all the assertions that may be made about the type and its instances and all rulesets.
Figure 19 Cyclic associations suggest invariants.
An invariant, or constraint, is a single rule that is always obeyed by the object whose scope it lies in. It is expressed using the vocabulary provided by the type model. Example invariants for an airline management system might include: 'Every pilot must be the captain or co-pilot of up to one flight per day', 'The captain and co-pilot cannot be the same person' or 'Every flight must be flown by a captain who is qualified for this plane type'. Clearly, the invariant of a type can refer to the public interfaces of other types. Invariants can be expressed informally, as above, or using the high precision of OCL or other formal logic systems.
One possible source of invariants is the existence of cycles in type diagrams. In fact, as we shall see later, any bi-directional association usually requires a pair of invariants - precisely because the pair of rôlenames is a loop. In Figure 19 we can see that all the indicated loops may possibly imply the need for one or more invariants to be stated.
For example, in the upper diagram the pilot of a flight must work for the airline that provides the flight. The reader is encouraged to write invariants for the other cycle in Figure 19.
Rulesets generalize class invariants and permit objects to do several things:
Rules specify second order information, such as dependencies between attributes; for example, a dependency between the age of an employee and her holiday entitlement. Global pre- and post-conditions that apply to all operations may be specified as rules. A typical business rule in a human resources application might include 'change holiday entitlement to six weeks when service exceeds five years' as a rule in the Employee type. With rulesets the notation can cope with analysis problems where an active database is envisaged as the target environment.
Rules and invariants are used to make an object's semantics explicit and visible. This helps with the description of information that would normally reside in a repository, such as business rules for the enterprise. It can also help with inter-operability at quite a low level. For example, if I have an object which computes cube roots, as a client of that object it is not enough to know its operations alone; I need to know that what is returned is a cube root and not a square root. In this simple case the solution is obvious because we can characterize the cube root uniquely with one simple rule: the response times itself twice is equal to the parameter sent. If this rule is part of the interface then all other systems and system components can see the meaning of the object from its interface alone, removing thus some of the complexities of repository technology by shifting it into the object model.
The rules which appear in the rule window may be classified into several, not necessarily exclusive, types, as follows.
The last application of rulesets is by far and away the most esoteric. Object-oriented methods must obviously deal with multiple class inheritance. This extension must include provision for annotating the handling of conflict arising when the same attribute or operation is inherited differently from two parent objects. Of course, types compose monotonically so this problem doesn't arise. This kind of discrimination can only be done with a class. One way to deal with it is to use rulesets to disambiguate multiple inheritance. One can then also define priority rules for defaults and demons. (A demon is a method that wakes up when needed; i.e. when a value changes, or is added or deleted.) That is, these rules can determine how to resolve the conflict that arises when an attribute inherits two different values or perhaps specify whether the default value should be applied before or after inheritance takes place or before or after a demon fires. They may also specify the relative priorities of inheritance and demons. As with attributes and operations, the interface of the object only displays the name of a ruleset. In the case of a backward chaining ruleset this might well include the name of the value being sought: its goal; e.g. If Route: needed SEEK Route:
Control rules are encapsulated within objects, instead of being declared globally. They may also be inherited and overridden (although the rules for this may be slightly complicated in some cases - see (Wills, 1991)). The benefit of this is that local variations in control strategy are possible. Furthermore, the analyst may inspect the impact of the control structure on every object - using a browser perhaps - and does not have to annotate convoluted diagrams to describe the local effects of global control. Genuinely global rules can be contained in a top level object, called something like 'object', and will be inherited by all objects that do not override them. Alternatively, we can set up global rules in a special 'policy blackboard' object. Relevant classes register interest in Policies, which broadcasts rule and status changes to registrants as necessary. This uses, of course, a publish and subscribe pattern. Just as state transition diagrams may be used to describe the procedural semantics of operations, so decision trees may be found useful in describing complex sets of rules.
Control rules concern the operations and attributes of the object they belong to. They do not concern themselves. Thus, they cannot help with the determination of how to resolve a multiple inheritance conflict between rulesets or other control strategy problem related to rulesets. This would require a set of metarules to be encapsulated and these too would require a meta-language. This quickly leads to an infinite regress. Therefore multiple inheritance of rules does not permit conflict resolution. A dot notation is used to duplicate any rulesets with the same name. Thus, if an object inherits rulesets called POLICYA from two superclasses, X and Y, they are inherited separately as X.POLICYA and Y.POLICYA. The case of fuzzy rules is slightly different since fuzzy rules cannot contradict each other as explained in Appendix A. Therefore multiply inherited fuzzy rulesets with the same name may be merged. In both the crisp and fuzzy cases, however, the careful user of the method should decide every case on its merits, since the equivalent naming of the inherited rulesets could have been erroneous.
It is sometimes possible, in simple cases, to specify rules that must be obeyed by all control strategies for multiple inheritance. In the case where objects are identified with only abstract data types - i.e. constructed types representing relations, say, are not permitted - we have a clear set of three rules for inheritance:
These rules are commended as design checks.
Rule-based systems are non-procedural. That is, the ordering of a set of rules does not affect their interpretation. Rules may be grouped into rulesets which concern the derivation of values for one object. In some environments, such as KEE, rules are each bona fide objects, but this approach begs the question of the relationship of a rule to another object. Most expert systems shells and environments encourage the developer to write the rules first and only later identify the objects used by the rules. This enforces a top-down approach and can be useful as a discipline but contradicts an object-oriented approach.