280 likes | 295 Views
This chapter provides an overview of why specifications are important in software construction, how to effectively write specifications for desired behavior, and advice from Liskov. It also covers some formal ways to specify behavior using tools like Z and OCL.
E N D
SpecificationsLiskov Chapter 9 SWE 619 Last Updated Fall 2008
Overview/Agenda • Review why we use specifications as a software construction tool? • Restating the reasons from chapter 1 • How to write good specs (for desired behavior)? • Advice from Liskov • Some formal ways to specify behavior • Z, OCL (SWE 623)
Why Specifications? • What do specifications do? • Who is the target audience? • How are they constructed? • What is their use? • Do they evolve?
Construction • Intended for: designers, implementers, testers, maintenance people, clients • In short: intended for people [users] • How formal can specifications be? • Must clearly state what the must the software do • Can informal specs be clear? • Must be understood by users of the specification.
Use • Implementers: understand system requirements, tasks to be performed • Testers: Understand functional requirements • Clients: understand how to use the software
Understanding abstractions • What must be understood? • Behavior of some abstraction • How to specify behavior? • abstract state, changes to abstract state • abstract descriptions • Leads to multiple implementations that satisfy an abstraction • Satisfy? Implementations follow contract i.e., pre and post conditions
Meaning of an abstraction • The set of implementations that satisfy an abstraction is the specificand set. • Specificand set is the meaning of an abstraction • Example public int 3DigitNumber() //E: return an integer greater than 99 // and less than 1000 What is the meaning of this abstraction?
Liskov example static int p (int y) // R: y > 0 // E: returns x such that x > y Specificand set: (possible implementations) {return y + 1;}, { return y + k ;} (k > 0) {return y * 2;}, {return y * k ; } (k > 1) {return 4y – 2;} What about {return 4y – 3;} ?
Liskov example (contd.) static int p (int y) // R: true // E: returns x such that x > y • What does strengthening/weakening of pre/post conditions do to the specificand set? • weakening pre smaller specificand set • New set is a subset of old set • strengthening post smaller specificand set • Again, new set is a subset of old set
Specs for desired behavior • Note that “desired” differs from “correct” • Specs determine specificand set • Do all members of specificand set exhibit desired behavior? • Throw out implementations that do not • Are there missing members of specificant set? • Add implementations that are missing
Sufficiently restrictive specs • Rephrase specs prune specificand set • Example: Iterator for a (mutable) bag bag = [Book, Book, Pen, Pencil] //E: returns every element in bag • Type of implementations for this spec? • Duplicates? • Elements not in bag? • What about order? • What about mutability?
Refining specs for desired behavior • If do not desire duplicates (i.e. Pen once, Book once): // E: returns all unique elements in bag only once • If order matters: // E: returns elements in order they were put in the bag (FIFO) • If mutability matters: // R: No change to bag while iterator in use // E: returns every element in bag
Refining specs for desired behavior (contd.) • If mutability matters: //E: returns every element in bag, throws // CME if iterator modified while in use • If only elements in bag desired: //E: returns only the elements in bag
Sufficiently general specs • Apparent contradiction with the earlier goal of pruning specs! • What if the specs are over specified? • i.e., some valid and desired implementations are not the members of the specificand set • Over specifications are bad in the sense that some desired implementations are lost
Classic over specification e.g. Consider a container with method: public Object getElement() • Desire any one element from the container • Spec: //E: returns 3rd element in the container • Obviously over-constrained.
Generality of specs • Specifications come in 2 flavors • Operational: A recipe or algorithm • Definitional: Some property or constraints that inputs/outputs satisfy • Prefer definitional specs • More freedom to implementer regarding choice of algorithm • But not a natural choice for most students
Clarity • Every user of the spec should have the same interpretation of the spec! • Possible if we allow formal specs • Not easy to understand formal specs! • Clear, unambiguous specifications are hard to get • Expect to make mistakes! • What to do? • Allow redundancy in specs
Redundancy • Specs are not concise • Easier to understand • 2 flavors • i.e. redundant definition • e.g. provide an example • Reduces chances of missing an important point
Liskov example static boolean subset (Set s1, Set s2) throws NPE // E: if s1 or s2 is null, throws NPE else returns true // if s1 is a subset of s2, else returns false • Makes sense to every reader • What could be wrong here?
Example contd. • What if s1 equals s2? • Design decision about what to return for improper subset. • Rephrasing specs as: //E: … NPE else, returns true if every element of s1 is an element of s2, else false • Is this a better spec?
Example continued • Second spec allows improper subset • Eliminates ambiguity. • But why not use accepted terms? • Liskov: don’t replace, include both! //E: …NPE, returns true if s1 is a subset of s2, i.e., returns true if every element of s1 is an element of s2, else false
Alternate spec • Still better way to make sense to a wide variety of audience is to add more redundancy (without being repetitive) // E: … NPE, returns true if s1 is a subset of s2, i.e., returns true if every element of s1 is an element of s2, else false, e.g., subset({7}, {3, 7}) returns true, subset({3}, {7}) returns false, subset ({7}, {7}) returns true
How much effort into specs? • Avoid verbosity, repetitiveness but not compromise on clarity • Examples are powerful as specifiers, good idea to include them • Only a handful of examples can help you think about restrictive and general specs
Test Driven Development as a Specification Exercise Basic idea: Define tests first Then develop code to satisfy tests Lots of process stuff as well Connection to current lecture: Specification examples are tests Specification examples greatly help specifier understand abstraction Rule of thumb: Most development artifacts are more useful if they are executable 24
Formal specifications • Specifications/ Analysis in Z (Zed) • Occasionally taught in SWE 623 • Basis for OCL in UML
Specification in Z • Scenario: We maintain a membership list and an associated phone database. [Person, Phone] |----PhoneDB----------------------------------- |members: P Person (‘set of’ person) |phones : Person Phone (relation) |------------------------------------------------------- |dom phones ⊆ members (invariant) |---------------------------------------------------
Z Operation: Assign a Phone • Scenario: Someone would like a phone. (Note: Missing precondition) |----Assign----------------------------------- | p? : Person; n? : Phone | PhoneDB |------------------------------------------------------- | phone’ = phone { p? n? } | members’ = members |---------------------------------------------------
Example • members {jim, sue} • phones {(jim, 1231), (sue, 3956)} • Assign(alice, 1231) • Cool Z property: Can calculate minimal preconditions!! • Simple analysis: leave out preconditions and find minimum constraint to maintain invariants!