TodoApp – Part 2: Single responsibility is what we need.

In our journey towards hardcore we need to refactor the shitcore into a bit more softcore =) I’m not going to show all source code in this blog entry. You can look at the code here.

It will take some iterations before the code is quite usable. In this iteration we will focus giving each method a single responsibility, which means that each method should only contain the logic for one thing. The constructor is a kick ass example of that. Lets look at it.

First of all, it’s responsible of connecting to the database.

[sourcecode language=”csharp”]
public MainWindow()
{
InitializeComponent();
_db = new SQLiteConnection("Data Source=todomachine.db;Version=3;DateTimeFormat=Ticks;");
_db.Open();
[/sourcecode]

then, it’s also verifying that the table exists in the database.

[sourcecode language=”csharp” firstline=”6″]
//create table if it do not exist.
if (!DoesTableExist(_db, "todo_items"))
{
SQLiteCommand command = new SQLiteCommand(_db);
command.CommandText =
@"CREATE TABLE todo_items(
id INTEGER PRIMARY KEY AUTOINCREMENT,
title VARCHAR(40) NOT NULL,
description text NOT NULL
);";
command.ExecuteNonQuery();
}
[/sourcecode]

and finally it’s loading stuff into the list box.
[sourcecode language=”csharp” firstline=”18″]

// load all todo items.
SQLiteCommand mycmd = new SQLiteCommand(_db);
mycmd.CommandText = "SELECT * FROM todo_items";
var reader = mycmd.ExecuteReader();
while (reader.Read())
{
lbTodos.Items.Add(new Item { Id = (long)reader["id"], Title = (string)reader["title"] });
}
}
[/sourcecode]

Let’s fix that and write a little bit of documentation for each method.

btnNew_Click

Next we have btnNew_Click which is used to save a new item:

[sourcecode language=”csharp”]
private void btnNew_Click(object sender, EventArgs e)
{
var frm = new TodoItemForm();
frm.ShowDialog(this);

var cmd =
new SQLiteCommand("INSERT INTO todo_items (title, description) values(@title, @description)", _db);
cmd.Parameters.Add(new SQLiteParameter("title", frm.tbTitle.Text));
cmd.Parameters.Add(new SQLiteParameter("description", frm.tbDescription.Text));
cmd.ExecuteNonQuery();

lbTodos.Items.Clear();
// load all todo items.
SQLiteCommand mycmd = new SQLiteCommand(_db);
mycmd.CommandText = "SELECT * FROM todo_items";
var reader = mycmd.ExecuteReader();
while (reader.Read())
{
lbTodos.Items.Add(new Item { Id = (long)reader["id"], Title = (string)reader["title"] });
}
}
[/sourcecode]

It has two responsibilities: Saving an item into the database and refreshing the listbox.
Fortunately, we have already created a method for the listbox in the constructor refactoring. Let’s just add that the listbox should be emptied before adding all items.

lbTodos_MouseDoubleClick

lbTodos_MouseDoubleClick is used to edit an item.

[sourcecode language=”csharp”]
private void lbTodos_MouseDoubleClick(object sender, MouseEventArgs e)
{
Item item = (Item)lbTodos.SelectedItem;

SQLiteCommand mycmd = new SQLiteCommand(_db);
mycmd.CommandText = "SELECT * FROM todo_items WHERE id=" + item.Id;
var reader = mycmd.ExecuteReader();
reader.Read();

var frm = new TodoItemForm();
frm.tbDescription.Text = (string)reader["description"];
frm.tbTitle.Text = (string) reader["title"];
frm.ShowDialog(this);

var cmd =
new SQLiteCommand("UPDATE todo_items set title=@name, description=@description WHERE id = @id", _db);
cmd.Parameters.Add(new SQLiteParameter("name", frm.tbTitle.Text));
cmd.Parameters.Add(new SQLiteParameter("description", frm.tbDescription.Text));
cmd.Parameters.Add(new SQLiteParameter("id", item.Id));
cmd.ExecuteNonQuery();

lbTodos.Items.Clear();
// load all todo items.
SQLiteCommand cmd3 = new SQLiteCommand(_db);
cmd3.CommandText = "SELECT * FROM todo_items";
reader = cmd3.ExecuteReader();
while (reader.Read())
{
lbTodos.Items.Add(new Item { Id = (long)reader["id"], Title = (string)reader["title"] });
}

}
[/sourcecode]

Responsibilities:

  1. Load item from database
  2. Populate item form
  3. Save stuff from the item form into the database
  4. Populate list box

Here we’ll refactor the item form to use the Item class, and we’ll therefore make the item class standalone (instead of being a private class inside MainWindow). That should take care of #2.

#4 have already been fixed thanks to the previous method refactorings.

We’ll move the item loading into a separate method.

Done

The first refactoring is now done. The goal of this post was to help you understand what Single responsibility really means, although we are not quite done with the guideline yet. The MainWindow form still have many different responsibilities. But first let’s explore exception handling in the next post.