Moving Responsibility


Right now, the NotepadMenuItem is translating between the Form and the TextModel. The translation of the command Tags into string arrays is inside the menu item, and it should be inside the TextModel. The next step, therefore, is to stop translating the strings, send the command over to the TextModel, and let it do the translation. This should be easy. First, we ll change the NotepadMenuItem just to store the command and return it:

 class NotepadMenuItem : MenuItem { 
private TextModel.Tags command;
public NotepadMenuItem (String menuString, EventHandler handler,
TextModel.Tags tag)
:base(menuString, handler){
command = tag;
}
public TextModel.Tags Command {
get { return command; }
}
}

The removal of the Inserts and Skips method, of course, breaks the following code:

 void MenuInsertTags(object obj, EventArgs ea) { 
NotepadMenuItem item = (NotepadMenuItem) obj;
GetText();
model.InsertTags(item.Inserts, item.Skips);
PutText(textbox, model.LinesArray(), model.SelectionStart);
}

We change it to

 void MenuInsertTags(object obj, EventArgs ea) { 
NotepadMenuItem item = (NotepadMenuItem) obj;
GetText();
model.InsertTags(item.Command);
PutText(textbox, model.LinesArray(), model.SelectionStart);
}

But there is no InsertTags() method in TextModel that takes a command. So we ll write it:

  public void InsertTags(Tags command) {  
InsertTags(InsertStrings(command), SkipStrings(command));
}
public void InsertTags(string[] tagsToInsert, string[] tagsPrecedingCursor) {
int cursorLine = LineContainingCursor();
lines.InsertRange(cursorLine+1, tagsToInsert);
selectionStart = NewSelectionStart(cursorLine + 1, tagsPrecedingCursor);
}

Note that I just called the old InsertTags method with the converted strings. That compiles, and I think it should run. Let s see...indeed, it does. Now we can declare the InsertStrings() and SkipsStrings() methods private:

  private  static string[] InsertStrings(Tags tag) { 
if (tag == Tags.Pre) return newPre;
else if (tag == Tags.Section) return newSection;
else if (tag == Tags.UnorderedList) return newUnorderedList;
else return newParagraph;
}
private static string[] SkipStrings(Tags tag) {
if (tag == Tags.Pre) return preSkip;
else if (tag == Tags.Section) return sectionSkip;
else if (tag == Tags.UnorderedList) return unorderedListSkip;
else return paragraphSkip;
}

We can remove the second InsertTags() method, folding its behavior into the first one. This should break the unit tests that are relying on knowing those strings directly. They do not compile. This

 [Test] public void AltS() { 
model.Lines = new ArrayList(new String[0]);
model.InsertTags(
new string[] {"<sect1><title></title>","</sect1>" },
new string[] { "<sect1><title>" });
AssertEquals("<sect1><title></title>", model.Lines[0]);
AssertEquals("</sect1>", model.Lines[1]);
AssertEquals(14, model.SelectionStart);
}

becomes

 [Test] public void AltS() { 
model.Lines = new ArrayList(new String[0]);
model.InsertTags(TextModel.Tags.Section);
AssertEquals("<sect1><title></title>", model.Lines[0]);
AssertEquals("</sect1>", model.Lines[1]);
AssertEquals(14, model.SelectionStart);
}

And so on. There are also a few helper methods inside TextModel that fail to compile. I had forgotten them:

 public void InsertPreTag() { 
InsertTags(newPre, preSkip);
}
public void InsertUnorderedList() {
InsertTags(newUnorderedList, unorderedListSkip);
}
public void InsertListItemTag() {
InsertTags(newListItem, listItemSkip);
}
public void InsertParagraphTag() {
InsertTags(newParagraph, paragraphSkip);
}

They become

 public void InsertPreTag() { 
InsertTags(Tags.Pre);
}
public void InsertUnorderedList() {
InsertTags(Tags.UnorderedList);
}
public void InsertListItemTag() {
InsertTags(Tags.ListItem);
}
public void InsertParagraphTag() {
InsertTags(Tags.Paragraph);
}

Here, I m inventing new tags for these methods. This is a good discovery: our new approach is actually picking up and improving functionality that we didn t foresee. It s common that a new capability like this one can improve code elsewhere in the system. Changing the methods to private, which we could have skipped , caused us to find these opportunities. It s a good thing. The code changes include these:

 public enum Tags { 
Pre = 1,
Section = 2,
UnorderedList = 3,
ListItem = 4,
Paragraph = 5
}
private static string[] InsertStrings(Tags tag) {
if (tag == Tags.Pre) return newPre;
else if (tag == Tags.Section) return newSection;
else if (tag == Tags.UnorderedList) return newUnorderedList;
else if (tag == Tags.ListItem) return newListItem;
else return newParagraph;
}
private static string[] SkipStrings(Tags tag) {
if (tag == Tags.Pre) return preSkip;
else if (tag == Tags.Section) return sectionSkip;
else if (tag == Tags.UnorderedList) return unorderedListSkip;
else if (tag == Tags.ListItem) return listItemSkip;
else return paragraphSkip;
}

There s still one compiler message, for this code:

 [Test] public void AltSWithText() { 
model.SetLines (new String[1] {"<P></P>"});
model.SelectionStart = 7;
model.InsertTags(
new string[] {"<sect1><title></title>","</sect1>" },
new string[] { "<sect1><title>" });
AssertEquals("<sect1><title></title>", model.Lines[1]);
AssertEquals("</sect1>", model.Lines[2]);
AssertEquals(23, model.SelectionStart);
}

You ll recall me mentioning earlier that more than one test had broken when I commented out all that material in TextModel. This was the other one, and you can see that I changed it to use the strings. Now, of course, the change is similar to all the others:

 [Test] public void AltSWithText() { 
model.SetLines (new String[1] {"<P></P>"});
model.SelectionStart = 7;
model.InsertTags(TextModel.Tags.Section);
AssertEquals("<sect1><title></title>", model.Lines[1]);
AssertEquals("</sect1>", model.Lines[2]);
AssertEquals(23, model.SelectionStart);
}

Everything compiles now...and the tests all run!




Extreme Programming Adventures in C#
Javaв„ў EE 5 Tutorial, The (3rd Edition)
ISBN: 735619492
EAN: 2147483647
Year: 2006
Pages: 291

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net