A pleasant walk through computing

Comment for me? Send an email. I might even update the post!

Who Are You Coding For?

The Lesson

Consider this brief, hypothetical exchange between two developers, maybe in a meeting where others are present.

Developer 1: "...so, I solved the problem this way. This causes e.Data to overwrite the previous line."

bool overwrite = e.Data.Contains(".....");

WriteLog(e.Data, overwrite);

Developer 2: "You know you could just include the condition in the WriteLog call. You don't need to go to the trouble of setting a variable."

WriteLog(e.Data, e.Data.Contains("....."));

Developer 1: "Sure. So, why did I?"

Developer 2: "Huh?"

Developer 1: "Why did I use a variable?"

Developer 2: "I don't know."

Developer 1: "Because I'm not coding for me today. I'm coding for other developers, and my future self. I want it to be as easy for them as possible to understand what this code is doing."

Why I wrote this

My grandfather was once desribed as a bridge-builder. Even if he were (metaphorically) crossing a river just once, he'd build a bridge for whoever came after him.

Think about this as you make coding, architectural, and documentation decisions. What you do will affect someone in the future. Are you focused on finishing now, as fast as possible? Or crafting your code so that someone thanks you later on.

Who are you coding for?

The ViewService Pattern: Especially Good For Windows Forms

Windows Form development is still happening, is fast, and is reasonably easy to understand how to get started with. It's event-driven and hasn't changed much since Visual Basic. You drag controls onto a canvas, double-click the controls to open their event methods, and write what's supposed to happen.

Thus, Frankenstein's monster was born.

I'm taming the monster with a pattern I'm calling the ViewService. I won't create a Gang of Four class diagram, but basically a ViewService is a way of separating the code that manages ViewModels, and is a useful approach in Windows Forms. It's directly analagous to domain services and models.

How often have you written or maintained code that looks like this? (note this is pseudocoding, not actual code.)

class MainForm
{
    void buttonLoadData(object sender, EventArgs e)
    {
        var cn = new Connection(_connString);
        var cmd = new Command(cn, "select a.*, b.* from Customer a join Order b on a.CustomerId = b.CustomerId");
        var reader = cmd.ReadResults();
        until (reader.Eof)
        {
            var co = ConvertReaderToCustomerOrder(reader.Read());
            grid1.Row.Add(new Row(co.Name, co.Zip);
            AddOrdersToGridRow(grid1.Rows[grid1.Rows.Length-1], co)
            if (co.Type = 1) { grid1.Rows[grid1.Rows.Length-1].BackgroundColor = Blue;}
            else if (co.Type = 3) { grid1.Rows[grid1.Rows.Length-1].BackgroundColor = Orange;}
        }
        
    }
    
    void AddOrdersToGridRows(Row row, CustomerOrder order, Connection cn)
    {
        foreach (var orderitem in order.Orders)
        {
            var cmd = new Command();
            cmd.Connection = cn;
            cmd.CommandText = String.Format ("select * from lineitems where OrderId = %1",  orderitem.OrderId);
            var items = ToLineItems(cmd.ExecuteQuery());
            row["Items"] = AddItemsToGrid(items, row);
            if (order.Type = 3 and items.Count() > 15) {row.BackgroundColor = Red;}
        }
        CheckIfMoreOrdersHaveArrivedAndPrintThem();
        
    }
    
    void buttonRefreshData(object sender, EventArgs e)
    {
        buttonLoadData(sender, e);
    }
    
}

The problems with the above code could occupy us for awhile, and they add up to one word: complication.

  • Events are doing too many things
  • Events are called directly
  • Tight coupling
  • Inconsistent naming and coding style

Here's how I recommend clearing up this kind of code by applying the ViewService pattern.

  1. Group form events together
  2. Group together methods that only apply to this form
  3. Group methods that could apply to a replacement form together, potentially into a service
  4. Form events present data, or call a method to preserve data

Let's apply these steps to the above.

The code below is pretty sparse and incomplete. The goal is to give you the idea of what to do, not provide a full-fledged implementation.

Group form events and methods

This is organizational, and clarifies what the user is doing vs what the developer is doing. (Methods collapsed for clarity.)


+ void AddOrdersToGridRows(Row row, CustomerOrder order, Connection cn)...

#region "Control Events"

+ void buttonLoadData(object sender, EventArgs e)...
+ void buttonRefreshData(object sender, EventArgs e)...

#endregion

Better methods and names, and events call custom methods instead of being treated as custom methods

class MainForm
{
    List<CustomerOrderView> _customerOrders = new List<CustomerOrderView>();
    
    + void GetData()...
    + void LoadControls()
    
    void buttonLoadData(object sender, EventArgs e)
    {
        GetData();
        LoadControls();
    }
    void buttonCancel(object sender, EventArgs e)
    {
        LoadControls();
    }
}

Already, we're gaining clarity.

Separate the data calls into a ViewService

Imagine the ViewService is going to be resued in a web application. That means it doesn't accept or process form controls, and is UI agnostic.

This is the same Dependency Injection pattern used in web applications. The difference is that CustomerOrderViewService isn't a domain service, it's specific to this view of the data.


class MainForm 
{
    ICustomerOrderViewService _customerOrderViewService = null;
    List<CustomerOrderView> _customerOrders = new List<CustomerOrderView>();

    
    + void MainForm(ICustomerOrderViewService)...
    
    void GetData()
    {
        _customerOrders = _customerOrderViewService.Get(txtCustomerId.Text);
    }
    
    void LoadControls()
    {
        if (_customerOrders == null) { GetData(); }
        gridOrders.DataSource = _customerOrders;
    }

}

//These two classes would be in separate files, and *could* be in a separate namespace
//to emphasize the decoupling.

class CustomerOrderViewService: ICustomerOrderViewService
{
    ICustomerOrderService _customerOrderService = null;
    
    + public CustomerOrderViewService(ICustomerOrderService _customerOrderService)...
    

    public List<CustomerOrderView> Get(string customerId)
    {
        //This is the call to the *domain service*. It might call the database directly, or might in turn call a web api.
        
        //Returns type CustomerOrder
        var customerOrders = _customerOrderService.GetOrdersByCustomer(string customerId);
        
        //Mapping
        return customerOrders.Select(a => a.ToCustomerOrderView());
    }
    
}

class static CustomerOrderViewServiceHelpers
{
    public static ToCustomerOrderView (this CustomerOrder customerOrder)
    {
        return new CustomerOrderView()
        {
            Name = customerOrder.Name,
            CustomerType = customerOrder.CustomerType,
            etc....
        }
    }
}

The Happy Wrap Up

By applying the ViewService pattern, we can separate Windows Form code into cleaner areas of concern, making our code clear, testable, maintainable and replaceable.

Most of Us Developers Aren't Smart

Most of us developers think we're smart. But we aren't smart. We're clever.

And confused.

You can easily prove this by looking at our code. Specifically, by answering these questions:

  1. How many layers of indirection are there?
  2. How many abstractions of abstractions are there?
  3. How many different things are our methods doing?

Combined, these reveal how complicated a set of code is.

What we do as developers is complex. There are lots of little moving parts that all need to work together.

Complex: composed of many interconnected parts; compound; composite1

Complicated: difficult to analyze, understand, explain, etc.2

Because we're clever, we think we understand how to manage this complexity. But we don't. We're confused by it. So, instead of making the complex clear, we complicate it. We add stuff that makes those moving parts harder to understand (over-use of indirection and abstraction), and harder to work together (method multi-responsibility).

No one likes to hear this or face it in themselves. But we face it too often when supporting others' code. It comes with that little, internal question, "Couldn't this be easier?"

So, what do we do?

First, decide that "we" applies to "you." It's better to start with this assumption because it leads to learning rather than avoidance. If I insist I'm smart, I won't make the effort to learn better--thus, not smart. But if I insist I can be better, no matter how good I am, then the path to improvement and away from stagnation opens long and wide.

Second, find examples of complex code that isn't complicated. Open source projects coming out of Microsoft and Google are good starting points.

Third, Read about and apply the principles behind uncomplicating code. Three of these are:

  1. SOLID
  2. DRY
  3. YAGNI

Finally, be guided by four overarching principles in your architecture and coding. Make your code...

  1. Clear
  2. Maintainable
  3. Testable
  4. Replaceable

We may not be smart, but be can code smarter.


  1. Complex 1645–55; 1905–10 for def 7; (adj.) < Latin complexus, past participle of complectī, complectere to embrace, encompass, include, equivalent to complect- (see complect) + -tus past participle suffix; (noun) < Late Latin complexus totality, complex (Latin: inclusion, grasping, embrace), equivalent to complect(ere) + -tus suffix of v. action; reanalysis of the Latin v. as “to intertwine (completely)” has influenced sense of the adj.

  2. Complicated 1615–25; < Latin complicātus (past participle of complicāre to fold together), equivalent to com- com- + -plic- (combining form of *plecāre to fold, akin to plectī to plait; see complex) + -ātus -ate1

   Older