-------------------------------------------------------------------------- Adaptive Object-Oriented Software Development Fall 1997 COM 3360/NTU SE737 Karl Lieberherr --------------------------------------------------------------------------- Midterm --------------------------------------------------------------------------- Open book and open notes. Question 1: 27 UNKNOWNS: 4 points each: 98 points Question 2: 50 points Question 3: 14 UNKNOWNS: 3 points each: 42 points Question 4: 40 points TOTAL: 230 To make the grading easier, please put your values for the unknowns on the enclosed answer sheet. THE GAME OF REDUNDANCY AND UNKNOWNS ----------------------------------- Most of the questions in this exam ask you to determine unknowns of the form UNKNOWN1, UNKNOWN2, ... This makes it easier for you to answer the questions, since you get extra context information. Yet you need to master the behavioral objectives of the course to answer the questions. Guessing an answer is not a successful strategy and therefore a "game of redundancy" test is more interesting than a multiple choice test. The questions have the following pattern: I show you several artifacts which are related by the theory of object-oriented design and programming. Because of the dependencies between the artifacts, some of the information is redundant and can be recovered from the context by applying the objectives covered in the course. The information which you should discover is marked UNKNOWNx. If an unknown is not uniquely determined, mark the answer with *CHOICE*. An unknown may be anything, e.g., a number, an identifier, a character, two identifiers with a blank between them, a string etc. If an unknown is the empty string, give NOTHING as answer, e.g., UNKNOWN = NOTHING. Example: 5 + UNKNOWN1 = 8 UNKNOWN1 = 3 --------------- UNKNOWN2 * UNKNOWN3 = 20 UNKNOWN2 = 4 *CHOICE* UNKNOWN3 = 5 *CHOICE* At the beginning of a question we give the number of points per unknown. =============================================================== Question 1: 27 UNKNOWNS: 4 points each: 98 points =============================================================== The following cd and behavior files have been extracted and adjusted from the Demeter/Java source code: // An AP Studio picture of the class graph is also provided. Program = ClassGraph *EOF* . ClassGraph = DList(ClassGraphEntry). ClassGraphEntry : Directive | Definition. Directive : ParseDirective | VisitorDirective. ParseDirective : DoParse | DontParse. DoParse = "*parse*". DontParse = "*noparse*". VisitorDirective : BeginVisitors | EndVisitors. BeginVisitors = "*visitors*". EndVisitors = "*endvisitors*". Definition : ClassDef. ClassDef = ParamClassName [ *s ParseDirective ] *s ClassParts [ *s EOFtoken ] "." . ParamClassName = Ident. ClassParts = "=". EOFtoken = "*EOF*". Main = . DList(S) ~ {S}. // gengrammar.beh -- Grammar generation //=================================================================== UNKNOWN1 { /** Mark classes which are not parsed. */ void markNotParsed() UNKNOWN2 { UNKNOWN3, UNKNOWN4, UNKNOWN5} { (@ boolean classes_are_parsed = true; @) (@ boolean local_to_class, this_class_is_parsed; @) before UNKNOWN6 (@ local_to_class = true; this_class_is_parsed = classes_are_parsed; @) after UNKNOWN7 (@ local_to_class = false; if (!this_class_is_parsed) host.markNotParsed(); @) before UNKNOWN8 (@ if (local_to_class) this_class_is_parsed = false; else classes_are_parsed = false; @) before UNKNOWN9 (@ if (local_to_class) this_class_is_parsed = true; else classes_are_parsed = true; @) } } // inherit.beh -- Inheritance backlinks construction UNKNOWN10 { /** Mark classes between "*visitors*" and "*endvisitors*" as visitors. */ void markVisitors() UNKNOWN11 { UNKNOWN12, UNKNOWN13, UNKNOWN14 } { (@ boolean visitor = false; @) before UNKNOWN15 (@ visitor = true; @) before UNKNOWN16 (@ visitor = false; @) before UNKNOWN17 (@ if (visitor) { host.markVisitor(); } @) } } //---------------- extra code not from demjava UNKNOWN18 { void markVisitor() (@ System.out.println(" marked as visitor " + paramclassname.get_ident().toString()); @) void markNotParsed() (@ System.out.println(" marked as not parsed " + paramclassname.get_ident().toString()); @) } Main { (@ static public void main(String args[]) throws Exception { UNKNOWN19 p = UNKNOWN20.parse(System.in); System.out.println(" done "); p.markVisitors(); p.markNotParsed(); p.universal_trv0(new DisplayVisitor()); } @) } In a class dictionary you may declare your classes as visitors. By default, they are not visitors. A class is a visitor if it appears after *visitors* but before *endvisitors*. You may also declare whether you need a parser for your classes. By default, a parser is generated for a class. A class does not get a parser if it appears after *noparse* but before *parse*. Each class definition may contain an optional *parse* or *noparse* directive which overrides the directive which would otherwise hold. On input D_P = . *noparse* B_NoP = . *visitors* A_P *parse* = . *visitors* // superfluous E_NoP = . *endvisitors* F_NoP = . *parse* *noparse* C_NoP = . *visitors* G_NoP_V = . *parse* H_P = . *endvisitors* *endvisitors* // superfluous *parse* // superfluous the program produces as output: done marked as visitor A_P marked as visitor E_NoP marked as visitor G_NoP_V marked as visitor H_P marked as not parsed B_NoP marked as not parsed E_NoP marked as not parsed F_NoP marked as not parsed C_NoP marked as not parsed G_NoP_V : Program ( : UNKNOWN21 ( : UNKNOWN22 { : Nonempty_ClassGraphEntry_DList ( : UNKNOWN23 ( : ParamClassName ( : Ident "D_P" ) : ClassParts ( UNKNOWN24 ) ) : Nonempty_ClassGraphEntry_DList ( : UNKNOWN25 ( ) : Nonempty_ClassGraphEntry_DList ( : UNKNOWN26 ( : ParamClassName ( : Ident "B_NoP" ) : ClassParts ( ) ) : Nonempty_ClassGraphEntry_DList ( : UNKNOWN27 ( ) ... Find the UNKNOWNS =============================================================== Question 2: 50 points =============================================================== Modify the program from question 1 so that it counts the number of visitor classes in addition to printing the same information as before. Reuse the following class to get the job done. // CountingVisitor = int. // not really a visitor CountingVisitor { init (@ total = 0; @) void inc() (@ total = total + 1; @) int current_result() (@ return total; @) } Mark in the program below ( a copy from question 1) the changes which are necessary. // gengrammar.beh -- Grammar generation //=================================================================== UNKNOWN1 { /** Mark classes which are not parsed. */ void markNotParsed() UNKNOWN2 { UNKNOWN3, UNKNOWN4, UNKNOWN5} { (@ boolean classes_are_parsed = true; @) (@ boolean local_to_class, this_class_is_parsed; @) before UNKNOWN6 (@ local_to_class = true; this_class_is_parsed = classes_are_parsed; @) after UNKNOWN7 (@ local_to_class = false; if (!this_class_is_parsed) host.markNotParsed(); @) before UNKNOWN8 (@ if (local_to_class) this_class_is_parsed = false; else classes_are_parsed = false; @) before UNKNOWN9 (@ if (local_to_class) this_class_is_parsed = true; else classes_are_parsed = true; @) } } // inherit.beh -- Inheritance backlinks construction UNKNOWN10 { /** Mark classes between "*visitors*" and "*endvisitors*" as visitors. */ void markVisitors() UNKNOWN11 { UNKNOWN12, UNKNOWN13, UNKNOWN14 } { (@ boolean visitor = false; @) before UNKNOWN15 (@ visitor = true; @) before UNKNOWN16 (@ visitor = false; @) before UNKNOWN17 (@ if (visitor) { host.markVisitor(); } @) } } UNKNOWN18 { void markVisitor() (@ System.out.println(" marked as visitor " + paramclassname.get_ident().toString()); @) void markNotParsed() (@ System.out.println(" marked as not parsed " + paramclassname.get_ident().toString()); @) } Main { (@ static public void main(String args[]) throws Exception { UNKNOWN19 p = UNKNOWN20.parse(System.in); System.out.println(" done "); p.markVisitors(); p.markNotParsed(); p.universal_trv0(new DisplayVisitor()); } @) } =============================================================== Question 3: 14 UNKNOWNS: 3 points each: 42 points =============================================================== Extend the class dictionary from question 2 so that it accepts the following input: (D_P = *final* *static* A *read-only* B *derived* C) *noparse* (B_NoP : B | C *final* X *private* Y ) *visitors* (A_P *parse* = ) *visitors* // superfluous (E_NoP = ) *endvisitors* (F_NoP = M(S)) *parse* *noparse* (C_NoP = *private* S *s *l + -) *visitors* (G_NoP_V = ) *parse* (H_P = ) *endvisitors* *endvisitors* *parse* Find the UNKNOWNS in the following cd. Do not mention pretty printing commands *s, *l, + or -. Program = ClassGraph *EOF* . ClassGraph = DList(ClassGraphEntry). ClassGraphEntry : Directive | Definition. Directive : ParseDirective | VisitorDirective. ParseDirective : DoParse | DontParse. DoParse = "*parse*". DontParse = "*noparse*". VisitorDirective : BeginVisitors | EndVisitors. BeginVisitors = "*visitors*". EndVisitors = "*endvisitors*". Definition : ClassDef. ClassDef = "UNKNOWN1" ParamClassName [ *s ParseDirective ] *s ClassParts [ *s EOFtoken ] "UNKNOWN2" . ParamClassName = ClassName. ClassName = Ident. PartName = Ident. ClassParts : ConstOrAltClass . ConstOrAltClass : ConstructionClass | AlternationClass *common* + + + List(PartOrSyntax) - - -. ConstructionClass = UNKNOWN3. AlternationClass = UNKNOWN4 + + + Barlist(ClassSpec) UNKNOWN5 - - -. PartOrSyntax : Part | Syntax. Part = [ "<" PartName ">" *s ] List(UNKNOWN7) ClassSpec . PartKeyword : FinalPart | StaticPart | UNKNOWN8| PrivatePart | DerivedPart . FinalPart = "*final*". StaticPart = "*static*". ReadOnlyPart = "*UNKNOWN9*". PrivatePart = "*private*". DerivedPart = "*derived*". ClassSpec = ClassName ["(" Commalist(ClassSpec) ")" ] . Syntax : PlainSyntax | UNKNOWN10. PlainSyntax = String. PrintCommand : PrintIndent | UNKNOWN11 | PrintSkip | PrintSpace. PrintIndent = "+" . PrintUnindent = "UNKNOWN12" . PrintSkip = "*l" . PrintSpace = "*s" . EOFtoken = "*EOF*". Main = . // Parameterized class definitions. UNKNOWN13(S) ~ {*s S}. DList(S) ~ {S}. Commalist(S) ~ S {"," S}. Barlist(S) ~ UNKNOWN14 CountingVisitor = int. =============================================================== Question 4: 40 points =============================================================== Discuss the pros and cons of part-free programming. I am referring to the use of adaptive methods to access parts. Could we write all our behavior files without mentioning part names, provided we select our class graphs accordingly? To access a part b of class A = B. we use A { B my_get_b() to B { before B (@ return_val = host; @)} } which does not refer to a b part (except encoded in the name my_get_b. But the B-part could be deeply nested inside A. If we have A = B B. and we want to access the b1 part, we use A { B my_get_b1() through {-> *,b1,*} to B // B my_get_b1() bypassing {-> *,b2,*} to B {before B (@ return_val = host; @)} } which forces us to mention part names in the strategy. Can we avoid this at the cost of extra classes? Are the extra classes beneficial?