17 May 2011

Coding 201 – Separating your Code Layers

Above the Post Edit: In response to a request, the source code is available on CodePlex @ http://movielibrarydemo.codeplex.com/

Original Post:
One thing that has come to annoy me recently is the fact that, despite how much we talk about it, we never show new developers how actually to go about separating their code tiers, or layers, from each other. Then they get into some jam where good, object-oriented code would help, and they completely have to refactor.
Something like this came up today on StackOverflow.com (where I ask/answer as AllenG) when someone was building a simple library app to sort and store movies.

As soon as I heard this, my brain said to me, "Self," it said, "If I were doing this, I'd have an object layer with a Movie object and a Library Object, and they'd both be Serializable. Then I could just call an XmlSerializer and let it do the work." Which is, more or less, what I said. Unfortunately, the questioner did not understand how to go about making that happen. Well, out of a sense of fun, I had thrown the same project together anyway, and was going to post something about it (Don't worry, my next installment for my MVC3 series is slated for Thursday, unless something comes up). With his questions, I decided to go a little more into detail.

So, first, even for small projects, I believe that code should be broken into all three tiers: data, logic, UI. In very small projects, you could just manage it with folders, but I tend to use separate projects within a solution just to maintain the habit.

So, I created a solution (sorry, no screen caps- I'm on a different computer at the moment) which I just called MyMovieLibrary. The first project was a Class Library. I used it to create the following classes:
  1. Movie : Serializable
    1. Title
    2. Genre (an enum) //I had to make this an old style field/property to get it serialize with the XmlEnum attribute
    3. MPAA Rating
    4. Running Time
    5. Personal Rating
  2. Library : Serializable
    1. List<Movie>
  3. LibraryManager
    1. Library
    2. Methods to use the Library's List<Movie>.
Then I created the Genre enum.

If I were building it for real, I'd probably move most of the code from LibraryManager just into library, but there would be enough that I don't think the Library should know about that I'd still have all three classes.

Once I had my classes, I created a new project in the same solution that I called WinMovieLibrary. It referenced my class library project, and was a simple form.

The big thing that the OP needed was how to make the list-view work. So here that is, in all its glory (again, sorry for the formatting- I should be back on my main machine for the next MVC post…)

public Form1()
        {
            InitializeComponent();
            manager = new LibraryManager();
            if (File.Exists(ConfigurationManager.AppSettings["SaveLocation"]))
            {
                manager.Load();
            }
            else manager.Library = new Library() { Movies = new List<Movie>() };
            this.movieList.DataSource = manager.Library.Movies;
        }

As you can see, this is the Form initializer. You see that it calls an already saved file (if it exists) and loads the records into the library. Then, even if there are no movies, yet, it sets the DataSource of the ListBox to manager.Library.Movies.

When I update the library (say, by adding or deleting a movie), I then call this code:

        private void UpdateMovieListBox()
        {
            this.SuspendLayout();
            movieList.Invalidate();
            movieList.DataSource = null;
            movieList.DataSource = manager.Library.Movies;
            this.Update();
        }

This suspends the layout of the form, invalidates the movieList control, and then forces the DataSource to update. Finally, I allow the form to update. Which then gets me whatever the current version of the list is.
For serialization (where this started), as I mentioned, I simply marked up the Library and Movie classes as [Serializable], and my LibraryManager actually serializes them with this code (Save first, then Load):

        public void Save()
        {
            using (XmlWriter xWriter = XmlWriter.Create(ConfigurationManager.AppSettings["SaveLocation"]))
            {
                XmlSerializer xSer = new XmlSerializer(typeof(Library));
                xSer.Serialize(xWriter, Library);
            }
        }

        public void Load()
        {
            using (XmlReader xReader = XmlReader.Create(ConfigurationManager.AppSettings["SaveLocation"]))
            {
                XmlSerializer xSer = new XmlSerializer(typeof(Library));
                Library = (Library)xSer.Deserialize(xReader);
            }
        }

Now, back to my original point. Something I've noticed (and I've noticed it in everything from instructional books to tutorials online, to college level classes) is that we do not teach new developers this simple, foundational stuff. Had the OP known, from the beginning, that Movie should have been a class, and that his ListBox should just attach to a list for a DataSource, then his confusion over Serialization would not have required (probably) a question on SO followed by an exchange of comments. Even if he'd had to ask his question, as soon as someone said "Make your object serializable, then call an XmlSerializer" he'd have been all set.

So, for those of you who teach others how to write code, please incorporate this fundamental into your training material. It's not that hard to do, and it can save someone much time when they find they need to add functionality they hadn't thought of.

3 comments:

  1. Hi

    Would you give away the project files for this? I´m stuck with problem like that and not going to use your code just want to see it and try to learn form it. Let me know if you have nothing against sending me the code for this problem.

    ReplyDelete
  2. I'll post them on CodePlex. I'll post an update when it's up.

    ReplyDelete
  3. Thank you very much I will check for you update later today or tomorrow.

    ReplyDelete