import java.util.*; // for List and ArrayList // stubs: Color and Dictionary don't work; we just want the rest to type-check class Color { public Color(String s) {} } class Dictionary { public static Dictionary findDictionary(String language) { return null; /*...*/ } public boolean contains(String s) { return true; /*...*/ } /*...*/ } /* As a final but separate style issue, it was wrong from the start to mix specific appearance choices in with spell-checking (poor cohesion). So while this still is not ideal, we abstract out the notion of color choices, which creates a separate concept that is broadly useful since a color for errors applies to more than spell-checking (wrong quiz answers, wrong phone-number format, ...) */ interface ColorChoices { public Color defaultColor(); public Color errorColor(); /* ... */ } class StandardColors implements ColorChoices { public Color defaultColor() { return new Color("black"); } public Color errorColor() { return new Color("red"); } /* ... */ } interface WordChangeListener { public void onWordChange(StyledWord w); } class StyledWord { private StringBuffer text = new StringBuffer(); private Color color = new Color("black"); private List listeners = new ArrayList(); public StyledWord() { } public void addListener(WordChangeListener l) { listeners.add(l); } public void removeListener(WordChangeListener l) { listeners.remove(l); } private void afterWordChange() { for(WordChangeListener listener : listeners) { listener.onWordChange(this); } } public void addLetter(char c, int position) { text.insert(position,c); afterWordChange(); } public void deleteLetter(int position) { text.delete(position,position+1); afterWordChange(); } public String getText() { return new String(text); } public Color getColor() { return color; } public void setColor(Color c) { color = c; } /*...*/ } class Spellchecker implements WordChangeListener { private Dictionary dictionary; private ColorChoices colors; public Spellchecker(String language, ColorChoices cs) { dictionary = Dictionary.findDictionary(language); colors = cs; } public boolean performSpellcheck(StyledWord word) { return dictionary.contains(word.getText()); } public void onWordChange(StyledWord word) { if(performSpellcheck(word)) { word.setColor(colors.defaultColor()); } else { word.setColor(colors.errorColor()); } } } class QRemover implements WordChangeListener { public QRemover() { } public void removeQs(StyledWord word) { // subtle that we can delete only one Q but we'll get // called-back again after changing the text int i = word.getText().indexOf('Q'); if(i != -1) { word.deleteLetter(i); } } public void onWordChange(StyledWord word) { removeQs(word); } } class ChangeCounter implements WordChangeListener { private int count = 0; public ChangeCounter() { } public void onWordChange(StyledWord word) { count++; } public int getCount() { return count; } } class Main { public static void main(String[] args) { StyledWord w = new StyledWord(); w.addListener(new Spellchecker("English", new StandardColors())); // ... use w in various methods in the application } }