Josh Bloch Charlie Garrod School of Computer Science 15-214 1 - - PowerPoint PPT Presentation

josh bloch charlie garrod
SMART_READER_LITE
LIVE PREVIEW

Josh Bloch Charlie Garrod School of Computer Science 15-214 1 - - PowerPoint PPT Presentation

Principles of Software Construction tis a Gift to be Simple or Cleanliness is Next to Godliness Midterm 1 and Homework 3 Post-Mortem Josh Bloch Charlie Garrod School of Computer Science 15-214 1 Administrivia Homework 4a due Thursday,


slide-1
SLIDE 1

1

15-214

School of Computer Science

Principles of Software Construction ’tis a Gift to be Simple or Cleanliness is Next to Godliness Midterm 1 and Homework 3 Post-Mortem

Josh Bloch Charlie Garrod

slide-2
SLIDE 2

2

15-214

Administrivia

  • Homework 4a due Thursday, 11:59 p.m.

– Design review meeting is mandatory

  • Office hours schedule this week TBD
  • Final exam scheduled: Friday, December 16th, 5:30-8:30 pm
slide-3
SLIDE 3

3

15-214

Key concepts from last Tuesday…

slide-4
SLIDE 4

4

15-214

Understanding system behavior with sequence diagrams

  • A system sequence diagram is a model that shows, for one

scenario of use, the sequence of events that occur on the system’s boundary

  • Design goal: Identify and define the interface of the system

– Two components: A user and the overall system

slide-5
SLIDE 5

5

15-214

Formalize system behavior with behavioral contracts

  • A system behavioral contract describes the pre-conditions and

post-conditions for some operation identified in the system sequence diagrams

– System-level textual specifications, like software specifications

slide-6
SLIDE 6

6

15-214

Using interaction diagrams to assign object responsibility

  • For a given system-level operation, create an object interaction

diagram at the implementation-level of abstraction

– Implementation-level concepts:

  • Implementation-like method names
  • Programming types
  • Helper methods or classes
  • Artifacts of design patterns
slide-7
SLIDE 7

7

15-214

Heuristics for responsibility assignment

  • Controller heuristic
  • Information expert heuristic
  • Creator heuristic

Goals Heuristics Patterns Principles

slide-8
SLIDE 8

8

15-214

Another design principle: Minimize conceptual weight

  • Label the concepts for a proposed object

– Related to representational gap and cohesion

slide-9
SLIDE 9

9

15-214

Object-level artifacts of this design process

  • Object interaction diagrams add methods to objects

– Can infer additional data responsibilities – Can infer additional data types and architectural patterns

  • Object model aggregates important design decisions

– Is an implementation guide

slide-10
SLIDE 10

10

15-214

Creating an object model

  • Extract data, method names, and types from interaction diagrams

– Include implementation details such as visibilities

slide-11
SLIDE 11

11

15-214

Outline

  • A formal design process (conclusion)
  • Midterm exam post-mortem
  • Permutation generator post-mortem
  • Cryptarithm post-mortem
slide-12
SLIDE 12

12

15-214

Exam grading fortune cookies…

slide-13
SLIDE 13

13

15-214

Exam grading fortune cookies…

slide-14
SLIDE 14

14

15-214

Exam grading fortune cookies…

slide-15
SLIDE 15

15

15-214

Exam grading fortune cookies…

slide-16
SLIDE 16

16

15-214

Midterm exam results

  • Average: 38 out of 77
  • Standard deviation: 15
slide-17
SLIDE 17

17

15-214

Outline

  • A formal design process (conclusion)
  • Midterm exam post-mortem
  • Permutation generator post-mortem
  • Cryptarithm post-mortem
slide-18
SLIDE 18

18

15-214

Anyone know a simpler expression for this?

if (myDog.hasFleas()) { return true; } else { return false; }

slide-19
SLIDE 19

19

15-214

Hint: it’s not this

return myDog.hasFleas() ? true : false;

slide-20
SLIDE 20

20

15-214

Please do it this way from now on

We reserve the right to deduct points if you don’t

return myDog.hasFleas();

slide-21
SLIDE 21

21

15-214

Also, we saw some hash functions like these

return 31 * x + 31 * y; // Multiplication doesn’t help! return 31 * x + 32 * y; // Multiplication hurts! return Objects.hash(map); // Objects.hash unnecessary!

slide-22
SLIDE 22

22

15-214

Here's how these should look

return 31 * x + 31 * y; return 31 * x + y; return 31 * x + 32 * y; return 31 * x + y; return Objects.hash(map); return map.hashCode();

slide-23
SLIDE 23

23

15-214

What should a hash code look like, in general?

  • Single-field object

– field.hashCode()

  • Two-field object

– 31*field1.hashCode() + field0.hashCode()

  • 3-field object

– 31*(31*field2.hashCode() + field1.hashCode) + field0.hashCode

– = 312 * field2.hashCode() + 31 * field1.hashCode() + field0.hashCode()

  • N-field object

– Repeatedly multiply total by 31 and add in next field

  • = Σ 31i · hashCode(fieldi)

– Alternatively: Objects.hash(field0, field1, … fieldN)

  • For much more information, see Effective Java Item 9
slide-24
SLIDE 24

24

15-214

We saw a lot of public enum types on the exam

  • Enums appropriate only if you know all values at compile time

– They are not appropriate if you’ll be adding values at run time

  • There is the extensible enum pattern (EJ Item 34)

– Enum type(s) and class(es) implement a common interface – But this is not a good match for this problem! – Why not?

slide-25
SLIDE 25

25

15-214

We saw a lot of public enum types on the exam

  • Enums appropriate only if you know all values at compile time

– They are not appropriate if you’ll be adding values at run time

  • There is the extensible enum pattern (EJ Item 34)

– Enum type(s) and class(es) implement a common interface – But this is not a good match for this problem! – The resulting type is not a value type; equals & hashCode won’t work – You cannot override equals and hashCode in an enum – Could fix using instance control, but the result would be a mess

slide-26
SLIDE 26

26

15-214

We saw many solutions that used trees

public abstract class Unit { private static class BaseUnit extends Unit { ... } public static final Unit METER = new BaseUnit(...); public static final Unit KILOGRAM = new BaseUnit(...); public static final Unit SECOND = new BaseUnit(...); private enum Op { MULTIPLY, DIVIDE } private static class DerivedUnit extends Unit { private final Unit left, right; private final Op op; … } public Unit multiply(Unit other) { return new DerivedUnit(...); } }

What’s wrong with this representation?

slide-27
SLIDE 27

27

15-214

Trees describe the expression, not the result!

  • Work fine for for multiplication and division
  • Awful for equals, hashCode, and toString

– Infinitely many trees represent the same unit! – We want to erase construction process, not highlight it

  • What are the key components of an SI unit?

– The answer dictates an appropriate internal representation

slide-28
SLIDE 28

28

15-214

A good, basic solution – fields and constructor

public class Unit { /** Representation: exponents on the base units in this unit */ private final int mExp; private final int kgExp; private final int sExp; private Unit(int mExp, int kgExp, int sExp) { this.mExp = mExp; this.kgExp = kgExp; this.sExp = sExp; } // Base Units public static final Unit METER = new Unit(1, 0, 0); public static final Unit KILOGRAM = new Unit(0, 1, 0); public static final Unit SECOND = new Unit(0, 0, 1); public static final Unit UNITLESS = new Unit(0, 0, 0);

slide-29
SLIDE 29

29

15-214

A good, basic solution – Object methods

@Override public boolean equals(Object o) { if (!(o instanceof Unit)) return false; Unit u = (Unit) o; return u.mExp == mExp && u.kgExp == kgExp && u.sExp == sExp; } @Override public int hashCode() { return 31 * 31 * mExp + 31 * kgExp + sExp; } @Override public String toString() { return (str("m", mExp) + str("kg", kgExp) + str("s", sExp)).trim(); } private static String str(String sym, int exp) { switch (exp) { case 0: return ""; case 1: return sym + " "; default: return String.format("%s^%d ", sym, exp); } }

slide-30
SLIDE 30

30

15-214

A good, basic solution – arithmetic methods

public Unit times(Unit other) { return new Unit( mExp + other.mExp, kgExp + other.kgExp, sExp + other.sExp); } public Unit dividedBy(Unit other) { return new Unit( mExp - other.mExp, kgExp - other.kgExp, sExp - other.sExp); } public Unit squared() { // Convenience method; not required return this.times(this); }

slide-31
SLIDE 31

31

15-214

A good, basic solution – Part b: derived units

public static final Unit HERTZ = UNITLESS.dividedBy(SECOND); public static final Unit NEWTON = KILOGRAM.times(METER) .dividedBy(SECOND.squared()); public static final Unit PASCAL = NEWTON.dividedBy(METER.squared()); public static final Unit JOULE = NEWTON.times(METER); public static final Unit WATT = JOULE.dividedBy(SECOND); private static final Map<Unit, String> symbols = new HashMap<>(); static { symbols.put(HERTZ, "Hz"); symbols.put(NEWTON, "N"); symbols.put(PASCAL, "Pa"); symbols.put(JOULE, "J"); symbols.put(WATT, "W"); }

@Override public String toString() {

String result = symbols.get(this); if (result != null) return result;

slide-32
SLIDE 32

32

15-214

A more flexible solution – fields and constructor

public class Unit { private enum Base { m, kg, s } // Names must be actual symbols! private static final Base[] BASES = Base.values(); // Optimization private final Map<Base, Integer> exponents; private Unit(Base base) { exponents = new EnumMap<Base, Integer>(Base.class); for (Base b : BASES) exponents.put(b, 0); if (base != null) exponents.put(base, 1); } public static final Unit METER = new Unit(Base.m); public static final Unit KILOGRAM = new Unit(Base.kg); public static final Unit SECOND = new Unit(Base.s); public static final Unit UNITLESS = new Unit((Base) null);

slide-33
SLIDE 33

33

15-214

A more flexible solution – Object methods

@Override public boolean equals(Object o) { return o instanceof Unit && ((Unit) o).exponents.equals(exponents); } @Override public int hashCode() { return exponents.hashCode(); } @Override public String toString() { result = ""; for (Base b : BASES) { int exp = exponents.get(b); if (exp == 1) { result += b + " "; } else if (exp != 0) { result += String.format("%s^%d ", b, exp); } } return result.trim(); }

slide-34
SLIDE 34

34

15-214

A more flexible solution – arithmetic methods

public Unit times(Unit other) { return timesOrDiv(other, 1); } public Unit dividedBy(Unit other) { return timesOrDiv(other, -1); } private Unit timesOrDiv(Unit other, int op) { Map<Base, Integer> newExps = new EnumMap<>(Base.class); for (Base b : BASES) newExps.put(b, exponents.get(b) + op * other.exponents.get(b)); return new Unit(newExps); } private Unit(Map<Base, Integer> exponents) { this.exponents = exponents; }

slide-35
SLIDE 35

35

15-214

Some solutions were correct but repetitious

  • Repetition isn’t just inelegant, it’s toxic
  • Avoiding repetition is essential to good programming
  • Provides not just elegance, but quality
  • Ease of understanding aids in

– Establishing correctness – Maintaining the code

  • If code is repeated, each bug must be fixed repeatedly

– If you forget to fix one occurrence, program is subtly broken

slide-36
SLIDE 36

36

15-214

Outline

  • A formal design process (conclusion)
  • Midterm exam post-mortem
  • Permutation generator post-mortem
  • Cryptarithm post-mortem
slide-37
SLIDE 37

37

15-214

Design comparison for permutation generator

  • Template Method pattern

– Easy to code – Ugly to use

  • Command pattern

– Easy to code – Reasonably pretty to use

  • Iterator pattern

– Tricky to code because algorithm is recursive and Java lacks yield iterators – Gorgeous to use

  • Performance of all three is similar
slide-38
SLIDE 38

38

15-214

A complete (!), general-purpose permutation generator

using the command pattern

slide-39
SLIDE 39

39

15-214

How do you test a permutation generator?

Make a list of items to permute (integers should do nicely) For each permutation of the list { Check that it’s actually a permutation of the list Check that we haven’t seen it yet Put it in the set of permutations that we have seen } Check that the set of permutations we’ve seen has right size (n!) Do this for all reasonable values of n, and you’re done!

slide-40
SLIDE 40

40

15-214

And now, in code – this is the whole thing!

static void exhaustiveTest(int size) { List<Integer> list = new ArrayList<>(size); for (int i = 0; i < size; i++) list.add(i); Set<Integer> elements = new HashSet<>(list); Set<List<Integer>> alreadySeen = new HashSet<>(); doForAllPermutations(list, (perm) -> { Assert.assertEquals(perm.size(), size); Assert.assertEquals(new HashSet(perm), elements); Assert.assertFalse("Duplicate", alreadySeen.contains(perm)); alreadySeen.add(new ArrayList<>(perm)); }); Assert.assertEquals(alreadySeen.size(), factorial(size)); } @Test public void test() { for (int size = 0; size <= 10; size++) exhaustiveTest(size); }

slide-41
SLIDE 41

41

15-214

Pros and cons of exhaustive testing

  • Pros and cons of exhaustive testing

+ Gives you absolute assurance that the unit works + Exhaustive tests can be short and elegant + You don’t have to worry about what to test

  • Rarely feasible; Infeasible for:
  • Nondeterministic code, including most concurrent code
  • Large state spaces
  • If you can test exhaustively, do!
  • If not, you can often approximate it with random testing
slide-42
SLIDE 42

42

15-214

Outline

  • A formal design process (conclusion)
  • Midterm exam post-mortem
  • Permutation generator post-mortem
  • Cryptarithm post-mortem
slide-43
SLIDE 43

43

15-214

A fast, fully functional cryptarithm solver in 6 slides

To refresh your memory, here’s the grammar cryptarithm ::= <expr> "=" <expr> expr ::= <word> [<operator> <word>]* word ::= <alphabetic-character>+

  • perator ::= "+" | "-" | "*"
slide-44
SLIDE 44

44

15-214

Cryptarithm class (1) - fields

slide-45
SLIDE 45

45

15-214

Cryptarithm class (2) - constructor

slide-46
SLIDE 46

46

15-214

Parsing a word into an expression M Y O N E

10 10 10 10

* * * * + + + +

(((M * 10 + O) * 10 + N) * 10 + E) * 10 + Y = M * 104 + O * 103 + N * 102 + E * 10 + Y

slide-47
SLIDE 47

47

15-214

Cryptarithm class (3) - word parser

slide-48
SLIDE 48

48

15-214

Cryptarithm class (4) – operator parser

slide-49
SLIDE 49

49

15-214

Cryptarithm class (5) – solver

slide-50
SLIDE 50

50

15-214

Cryptarithm class (6) - helper functions

slide-51
SLIDE 51

51

15-214

Cryptarithm solver command line program

slide-52
SLIDE 52

52

15-214

Conclusion

  • Good habits really matter

– “The way to write a perfect program is to make yourself a perfect programmer and then just program naturally.” – Watts S. Humphrey, 1994

  • Don’t just hack it up and say you’ll fix it later

– You probably won’t – but you will get into the habit of just hacking it up

  • Representations matter! Choose carefully.

– If your code is getting ugly, think again – “A week of coding can often save a whole hour of thought.”

  • Not enough to be merely correct; code must be clearly correct

– Nearly correct is right out.