240 likes | 253 Views
Learn about specifying a class in object-oriented programming, including methods, invariants, and different types of specs like model-based, property-based, and trace-based.
E N D
Topics in OO, Design Patterns, Reasoning About Program Behavior ...(part 2) Neelam Soundarajan Computer Sc. & Eng. e-mail: neelam@cse
Specifying A Class (methods) For each method: Specify pre-condition, post-condition; Pre-condition: Assertion on parameters, values of member variables; Post-condition: parameters, results, values of memebers at start and end. E.g.: pre-deposit(x) ≡ true; ?? post-deposit(x) ≡ [(x = #x) && (bal = #bal + x) pre-withdraw(x) ≡ true; ?? post-withdraw(x)≡ [(x = #x) && (bal = #bal - x)
Specifying A Class (class invariant) Invariants: Often useful to specify a class invariant; Example: (bal > 0) Note: Invariant need not hold during method execution. But: What if one method calls another?
Concrete vs. Abstract Specs Concrete specs: In terms of member variables of class. Disadv: Violates spirit of abstraction; why should client think in terms of internal details of class? Adv: Easier for the class implementer to work with. Abstract specs: In terms of an “abstract” model of class. (See Jones book for detailed discussion.)
Abstract Specs Model-based: Define a conceptual (math) model corresponding to the class; map class operations into corresponding operations on model (rather: specify pre- and post-conditions in terms of model); Property-based: Specify properties of the various operations in terms of axioms. Trace-based: In terms of traces of calls to methods of the calls and what those calls will return.
Model-based Specs Goal: Make model as “abstract” as possible. The designer will choose a representation based on implementation considerations. Proof of correctness: Provide a retrieve function that maps back from the representation to the model/abstraction. Adequacy: At least one rep element for any abstract value (usually shown by construction).
Abs Abs Fa rep retr Rep Rep Fr Model-based Specs (contd.) For operations: Fa is an abstract operation, Fr the corr. op. in the impl.: retr(Fr(rep(a))) = Fa(a) [For all a in Abs] I.e.: Fr’s behavior is same as Fa’s. Better: retr(Fr(r)) = Fa(retr(r)) [For all r in Rep]
Model-based Specs (contd.) Domain rule: pre-A(retr(r)) → pre-R(r) [for all r in Rep] I.e., pre-cond. of the operation in the rep is not too “narrow” Result rule: [pre-A(retr(r1)) && post-R(r1, r2) ] → post-A(retr(r1), retr(r2)) I.e., Any pair of states in the post-R relation, when viewed under the retr() function, must satisfy the post-A relation.
Model-based Specs (contd.) Advantages: • Provides concrete model; easy to understand. Disadvantages: • May bias implementation (i.e., too concrete). Complications: • Operations are partial and non-deterministic; • Retrieve functions are many-to-one (so their inverses are not functions).
Model-based Specs (contd.) Example: • A “biased” Queue which keeps the old elements • A “rep” function from Queue to Queueb cannot be constructed. • Def: A model-oriented spec is based on an underlying set of states. It is biased if there exist different elements of the set that cannot be distinguished by any sequence of operations. • Problems with abstraction/retrieve functions; see paper by Bruce Weide and others and the references therein.
Property-based Specs Idea: Specify properties of the various operations in terms of axioms. Detail: Spec consists of a signature and a set of equations.
Property-based Specs (contd.) Spec for a Stack of integers: init: → Stack push: N x Stack → Stack top: Stack → (N U Error) remove: Stack → Stack isEmpty: Stack → Bool top(init()) = Error top(push(i,s)) = i remove(init()) = init() remove(push(i,s)) = s isEmpty(init()) = true isEmpty(init()) = false
Property-based Specs (contd.) Advantages: Abstract: no model bias. Disadvantages: May be too abstract? Equations may not be obvious - may not even have a model! Or, conversely equations may not exist. E.g.: Veloso’s traversable stack: in addition to normal operations, stack can be read from a point indicated by cursor; cursor can be reset to top of stack or moved down one element; normal pop and push only with cursor at top. Easy to specify with model; impossible with prop-based spec?
Trace-based Specs Idea: Specify in terms of the structure of traces of calls to methods of the class and the values returned. Detail: Specify the legal traces; (behavior of the module in response to illegal traces is undefined). Specify which traces are equivalent to each other. Specify the output of a legal trace that ends in a function call (i.e., a method that returns a value).
Trace-based Specs (contd.) Definition: Legal traces: Which series of procedure calls are legal (i.e., not regarded as error)? These are indicated by semantic assertions of the form L(trace). Values: What is the output of a legal trace that ends in a function call? These are denoted by assertions of the form V(trace) = value
Trace-based Specs (e.g.) NAME stack SYNTAX push: int; pop: ; top: → int; SEMANTICS (forall T, i) (L(T) → L(T.push(i)) (forall T, i) (L(T.top) ↔ L(T.pop)) [not: (L(T.top) → L(T.pop)) (forall T, i) (T ≡ T.push(i).pop) (forall T, i) (L(T.top) → T ≡ T.top) (forall T, i) (L(T) → V(T.push(i).top = i)
Trace-based Specs (contd.) Problem: Trace-based specs can become complex quickly. Solution: Hoffman and Snodgrass introduce a number of heuristics to help with this. E.g.: Define a normal-form for traces. Then most of the spec need only worry about normal-form traces. For STACK, normal-form: sequence of push’s.
Trace-based Specs (contd.) Advantages: No bias; everything specified in terms of observable behavior. [This is, in fact, a general principle that one could adopt in specifying behaviors.] Disadvantages: Specs can get too complex. (The idea of normal-forms for traces as well as using suitably defined functions/macros seem to help with this.)
Behavioral Subtyping (Liskov, Wing: Behavioral notion of subtyping, 1994.) Basic Idea: Substitutability principle is good. Question: How to formalize it so it works in the presence of aliasing and in the presence of multiple users? Part of the answer: The subtype operations’ specs must be consistent with those of the corresponding supertype operation.
Behavioral Subtyping (contd.) Example: A given object may be treated as a bounded bag by one user (using operations, put, get); and used as a bounded stack by another user (operations: push, pop, swapTop). Requirement: The first user should be able to reason about the object using invariant and history properties of bounded bag.
Behavioral Subtyping (contd.) [Note: Turns out there are some technical problems that arise in some subtle situtations; I won’t go into them; read Liskov and Wing for the details.] Constraint-based approach: Have a constraint clause in base type that expresses history properties that must be preserved by subtype. [e.g.: bound of the bag is not changed.] Explanation-based approach: For each extra method in subtype, explain how to implement it using methods already defined in supertype. [e.g.: swapTop = pop; push; ]
Behavioral Subtyping (contd.) For σ to be behavioral subtype of τ: Iσ and Iτ are the invariants; A is an abstraction function (from subtype to supertype). mt.pre and mt.post are pre/post- conditions of m() in τ; similarly ms.pre and ms.post in σ. Constraint-based approach: 1. Invariant-rule: Iσ(s) → Iτ(A(s)) 2. mt.pre[A(xpre)/xpre] → ms.pre 3. ms.post → mt.post[A(xpre)/xpre, A(xpost)/xpost] 4. Constraint rule: For any computation c, and states s, s’ in c, Cσ → Cτ[A(xs)/xs, A(xs’)/xs’ ]
Behavioral Subtyping (contd.) For σ to be behavioral subtype of τ: Explanation-based approach: 1. Invariant-rule: As before 2. Pre-conditions: As before (not quite?) [For somewhat strange example, see L-W paper.] 3. Post-conditions: As before. 4. Extension rule: Formalizes the requirement that each additional method of σ can be written as a sequence of calls to methods of τ. The L-W paper considers all kinds of involved situations.
Behavioral Subtyping How do you define behavioral subtyping in the property-based approach? Ans: Not sure! I suspect it will be kind of difficult because you would have to work with the axioms that make up the spec. How do you define behavioral subtyping in the trace-based approach? Ans: Not sure! But I think this is actually doable. If you are looking to work on an interesting (and, I think, solvable) problem, this may be one. Talk to me.