Editor Project Review/Discussion

Lecture Notes for CS 190
Spring 2016
John Ousterhout

  • Biggest overall issue: classes aware of too much information
    • Combine unrelated things
    • Specialized for particular uses
  • Examples:
    • Text management also implements undo mechanism
    • Single object represents both selection and cursor
      • Cursor is one end of selection, or
      • If selection empty, then it's a cursor
    • A selection class that also handles insertions, deletions, notifications
    • APIs for text are modeled on GUI operations:
      • Text modifications done via a "selection"
      • Text has "delete 1 char forward" and "delete 1 char backward"
    • A "range" class that is named "Selection": over-specialized
  • Problems this creates:
    • Complexity: have to keep more information in your mind at once
    • Dependencies: can't understand one piece of code without another
  • Key idea: make classes focused and generic
    • If it's natural to think about 2 ideas independently, then separate them.
      • Text is only text, not text+undo or text+selection
      • Keep the selection and cursor separate
    • Try to keep each class as generic as possible:
      • Don't incorporate your knowledge of how it will be used
      • Imagine a broad range of usage
      • Define general-purpose operations that many uses will need: "delete range of characters" rather than "delete 1 character backwards"
      • Specialize at a higher level (e.g. GUI maps selection to positions, Delete to "delete 1 character backwards"
    • Apply these same ideas even within a class:
      • Minimize the amount of information that must be known at each point in the code
  • For Project 2: separate text management and undo, implement each in a generic way.
    • Basic undo mechanism must be totally generic: can undo and redo anything
    • But, it must be extensible, so higher levels can implement specific kinds of undoable operations.
    • Implement undoing for text and for the selection separately.
    • Simple and generic text: knows nothing about selections, GUI operations, etc.
  • Second problem: trying to separate things that are intimately related
    • See Example 1: Scrollbar AdjustmentListener
      • User interface divided into 2 classes: TextWindow, TextController
      • But TextController knows almost everything about TextWindow's internals
    • See Example 2: undo actions
      • Thin classes (InsertCommand, DeleteCommand, etc.) just hold data
      • Code associated with actions is in a separate class
  • Pass-through methods are a red flag
    • See Example 3: TextDocument class acts as intermediary between RWTextArea and TextEditor
    • One method just calls another with similar name and arguments
    • Results in thin class: lots of interface, not much functionality
  • Dependencies make code hard to read
    • Can I look at a piece of code in isolation and see easily that it works?
    • Or, do I need to look at lots of other code also (e.g. this code only solves part of the problem)?
    • See Example 4: event handler for Delete
  • Tactical vs. strategic programming:
    • Tactical: "I just need to make this work"
      • Don't worry about architecture
      • A little bit of incremental complexity isn't that bad?
    • Strategic: "How can I get the best architecture?"
      • Each bug and problem is a potential red flag: "does this bug indicate a problem with the APIs?"
      • Fix architecture problems when they are encountered
      • Don't accept incremental complexity.
  • Challenge for undo: how to manage event grouping?
    • Collect related actions (e.g. consecutive backspaces) into a single undoable action
      • Complex: lots of dependencies
    • Separate actions; single undo command may undo many actions. How to decide when to stop undoing?
      • Ask action: each action knows which other actions it should be grouped with.
        • Creates dependencies between actions: to add new action, may need to find and modify many existing actions.
        • Requires more action types (e.g. "insert single character" vs. "insert string")
      • Centralized code that knows about all actions and which can be grouped
        • Probably more manageable than having each action decide.
      • Introduce fences in the undo mechanism; undo/redo all actions up to the next fence.
        • Who adds fences and when? Requires extra code in event handlers.
        • But, code is locally understandable: no dependencies.
  • Design challenge: how to refer to the position of a character in the text?
    • Use Java Point object
    • Separate coordinates x and y
    • Separate line and character variables
    • Position class
  • How do you handle nonexistent positions?
    • Can they arise?
    • Who fixes them? When?
  • Documentation issues:
    • Example 5: documentation duplicates code
    • Example 6
    • Example 7: documentation to rewrite
  • Exception handling: generally sloppy
    • Doing something expedient, vs. figuring out how a problem eventually needs to be handled.
    • See Example 8.
    • Sometimes OK to swallow errors (Example 9)
  • How did you arrange for all windows to be redisplayed after text has changed?
  • Example 11: class definitions embedded in methods are hard to read. Only use for trivial cases.