Software Abstractions take Skill.

I recently read Adaptive Code via C# and posted a review on Amazon:

This book is a new favorite of mine. I’ve always prided myself on writing clean and concise code. I’ve always been fond of SOLID principles. I wanted to read this book to keep my understanding of SOLID principles fresh. It also covers design patterns, although be aware that it’s not an in-depth design patterns book.

The book is broken into three sections. The first section is an introduction to Scrum and SOLID. Before I had even finished the first section, I was already recommending this book to colleagues. This leading content isn’t necessarily targeted toward developers. I think many managers or team leaders could benefit from reading the basics of Scrum and understanding the terminology for SOLID programming.

I already had a pretty solid (sorry for that) understanding of SOLID principles, so I felt like the second section was more of a refresher. In that vein, I think it’d be hard for me to definitively say how easily digestible this section will be for beginners. I think it will greatly help intermediate or expert engineers to gain a new understanding of software architecture. The book’s audience is meant to be intermediate and expert engineers, but I think beginners could get the content. It’s so well written and clearly explained that I think anyone who might struggle a little with the concepts presented in the book could easily substitute any gaps in understanding with Wikipedia or blogs. Though, I honestly felt like there were no gaps. This section may be boring for non-developers, although I know project managers, program managers, and directors that would find this section interesting. The biggest thing to keep in mind is that SOLID principles are not rules; they’re guidelines.

I thought the last section was excellent. It is split into three chapters in which you’re presented with somewhat realistic dialog (‘fortnight’) that follows a small team through the first two sprints for a chat application. I’ve read a few books on Agile and Scrum methodologies and this section was probably the most fun to read on the topic. It could just be that it’s written as a script with code examples, but it was refreshing and easy to follow.

This book does a great job at explaining technical debt. While reading the book, I realized nobody at my current job has ever said the term ‘technical debt’. I asked around and found that it was a new term to most. The concept of technical debt is one thing I know I had problems understanding as a beginner. As developers become more mature, they begin to understand that the field is usually roughly equal parts business and technology. It’s really important to understand these ‘trade-offs’ and the last section demonstrates pretty well how technical debt occurs.

If you’re on the fence about purchasing this book, you should buy it. It’s a quick read and an understanding of the subject matter will improve your software. I’ve never regretted purchasing a Microsoft Press book.

I gave a short presentation at work recently about SOLID principles, so I was stoked to have a chance to read this book. One of the biggest takeaways I hope anyone has from this book is the ability to abstract software in useful ways.

I recently solved an issue using techniques such as those presented in this book. Specifically, this solution included the Open/Closed Principle, Dependency Injection, and Single Responsibility Principle as well as the Decorator pattern. The issue was simple, one I’m sure many people have encountered in their time with C#; a dataset’s DataRow does not inherent from IDataRecord in the same way something like SqlDataReader does.

Suppose you have a domain model of some kind that you need ‘mapped’ from your database into a single object.

Here’s a demonstration of the problem. I use a csv of cars from Wikipedia and instead of mapping to a domain object, I write out to the console.

First, an example using a DataReader:

using (var connection = new OdbcConnection(connectionString))
{
    connection.Open();
    using (var cmd = connection.CreateCommand())
    {
        cmd.CommandText = "select * from Cars.csv";

        using (var reader = cmd.ExecuteReader())
        while (reader.Read())
        {
            OverloadedDumpRow(reader);
        }
    }
}

Now, an example using a DataRow:

using (var connection = new OdbcConnection(connectionString))
{
    connection.Open();

    var adapter = new OdbcDataAdapter("SELECT * FROM Cars.csv", connection);

    DataSet ds = new DataSet("Temp");
    adapter.Fill(ds);

    foreach (DataRow row in ds.Tables[0].Rows)
    {
        OverloadedDumpRow(row);
    }
}

Aside from the implementation of data retrieval, OverloadedDumpRow should look exactly the same for both examples:

Console.WriteLine("A {0} {1} {2}", 
    row["Year"], row["Make"], row["Model"]);

The problem is that, since these two implementations don’t share a common base type of any sort (DataRow derives from nothing). This isn’t really an issue in a small example like this, but if you have a complex domain and you want to return data from your database and parse that data consistently. Think about what you’d have to do for each and every domain model just to parse resultsets from IDataRecord and DataRow. How do you determine whether or not you’ll even *need* both implementations? To be DRY, you’d need to pull your data from the following methods into variables or directly into your target domain object:

static void OverloadedDumpRow(IDataRecord row)
{
    Console.WriteLine("A {0} {1} {2}",
                    row["Year"], row["Make"], row["Model"]);
}

static void OverloadedDumpRow(DataRow row)
{
    Console.WriteLine("A {0} {1} {2}",
                    row["Year"], row["Make"], row["Model"]);
}

Obviously, this isn’t reusable. What we’d need is an interface. Something like this:

public interface IStringIndexed
{
  object this[string key] { get; }
}

Then, we could implement the redundant methods above in a single method:

static void DumpRow(IStringIndexed record)
{
  Console.WriteLine("A {0} {1} {2}",
    record["Year"], record["Make"], record["Model"]);
}

To get from IDataRecord and DataRow to this target interface, you’ll need an adapter. An adapter decorates a target type and exposes some new interface for that type. This can be a little confusing in our case because IDataRecord and DataRow have the same functionality (returning an object by string index), but they don’t have a consistent interface allowing us to write abstractions on top of these two types.

Our simple interface follows the single responsibility principle (implementations can only get an object by key) as well as the open/closed principle (you can now write extension methods against IStringIndexed).

Writing an adapter to use the interface from above is ridiculously easy. Here’s one of them:

internal class DataRowStringIndexedWrapper : IStringIndexed
{
    private readonly DataRow _row;

    public DataRowStringIndexedWrapper(DataRow row)
    {
        _row = row;
    }

    #region IStringIndexed Members

    object IStringIndexed.this[string key]
    {
        get { return _row[key]; }
    }

    #endregion
}

You would wrap an IDataRecord in exactly the same way.

Here are the two updated examples (notice both of these dump the record to console using the same method):

using (var connection = new OdbcConnection(connectionString))
{
    connection.Open();
    using (var cmd = connection.CreateCommand())
    {
        cmd.CommandText = "select * from Cars.csv";

        using (var reader = cmd.ExecuteReader())
        while (reader.Read())
        {
            DumpRow(new DataRecordStringIndexedWrapper(reader));
        }
    }
}

using (var connection = new OdbcConnection(connectionString))
{
    connection.Open();

    var adapter = new OdbcDataAdapter("SELECT * FROM Cars.csv", connection);

    DataSet ds = new DataSet("Temp");
    adapter.Fill(ds);

    foreach (DataRow row in ds.Tables[0].Rows)
    {
        DumpRow(new DataRowStringIndexedWrapper(row));
    }
}

This is the power of following SOLID principles and studying design patterns. This example isn’t taken from the book, but you’ll learn these skills and more in the book.

Code

A console application of this example is available on github.

You can purchase Adaptive Code via C# from informIT or Amazon.

Related Articles