@rob_rich

Don't write legacy code:

best practices and
design patterns for
maximum legibility

by Rob Richardson

@rob_rich

https://robrich.org/

December 9, 2015

About Me

Rob Richardson is a software craftsman building web properties in ASP.NET, Node, and Angular. He's a Microsoft MVP, published author, frequent speaker at conferences, user groups, and community events, and a diligent teacher and student of high quality software development. You can find this and other talks on https://robrich.org/presentations and follow him on twitter at @rob_rich.

Car Blinkers

What's the purpose?

To tell others of your intentions

Agile: Element of least surprise

Programming: Discoverable

Easy (not simple)

"Legacy"

means I don't understand it

Developer's Responsibility

To provide the best information for those that follow us

Design Patterns

What is a Design Pattern?

A generic solution to a common problem

Pattern

What is the problem we're trying to solve?

What is the technique?

What are the pros & cons

Singleton

A single instance

Problem: expensive initialization

Problem: common state

Solution: private constructor,
public static get instance method

Singleton


public class TheSingleton {
    private static TheSingleton instance = null;

    public static TheSingleton GetInstance() {
        if (instance == null) {
            instance = new TheSingleton();
        }
        return instance;
    }

    private TheSingleton() {
    }

    // ...
}
					

Singleton

Pros Cons
  • Solves common state
  • Expensive initialization only happens once
  • Less garbage collection churn
  • Difficult to reset during testing

Factory

A helper class instantiates and configures the real class

Problem: group common initialization

Problem: instantiate derived class, return interface

Solution: method that returns a new instance

Factory


public class TheFactory {

    public static TheClass GetTheClass() {
        TheClass theclass = new TheClass();
        // initialize theclass
        return theclass;
    }

}

public class TheClass {
    // ...
}
					

Factory

Pros Cons
  • Moves common initialization to a central place
  • Levels of indirection can be difficult to follow
  • Can you use a constructor instead?

Builder

A smarter class configures a simpler class

Problem: avoid overloaded constructors

Solution: method that configures the class's property and returns itself

Builder


public class TheBuilder {
    private TheClass theclass;

    public TheBuilder() {
        this.theclass = new TheClass();
    }

    public TheBuilder SetColor(string Color) {
        this.theclass.Color = Color;
        return this;
    }

    public TheClass Build() {
        return this.theclass;
    }

}

public class TheClass {
    // ...
}

public void Main() {
    TheClass theclass = new TheBuilder().SetColor("green").SetSize(5).Build();
}
					

Builder

Pros Cons
  • Removes the target class's constructor
  • A fluent interface can be very legible
  • The consuming code is quite legible, but the builder is weird

MVC

Model - View - Controller

Problem: UI code is hard to test

Solution: Separate routing, computation, and presentation

MVC

source: http://www.tonymarston.net/php-mysql/model-view-controller-01.png

MVC

Pros Cons
  • Easy to test each piece
  • Need a framework to wire up the pieces
  • Novices need some time to get comfortable with the pattern
  • Are you testing the pieces?

Publish / Subscribe

Decouple creating and handling events

Problem: avoid tight coupling between disconnected pieces

Solution: publish events or messages from one class to another

Publish / Subscribe


public class ThePublisher {
    public event InvoiceData InvoiceShipped;

    private ShipInvoice(InvoiceData Data) {
        InvoiceShipped(this, Data);
    }

}

public class TheConsumer {
    private ThePublisher publisher

    public void Setup() {
        publisher.InvoiceShipped += this.OnInvoiceShipped;
    }

    public void OnInvoiceShipped(object Sender, InvoiceData Data) {
        // ...
    }
}
					

Publish / Subscribe

Pros Cons
  • Separates concerns
  • Smaller, single-purpose classes
  • Race conditions between components
  • Subscribers that fail to unsubscribe: memory leak

Interface

Generic interface implemented by concrete class

Problem: concrete dependencies make testing hard

Solution: swap in fake dependencies during tests

Interface


public interface ITheClass {
    public int Add(int X, int Y);
}

public class TheClass : ITheClass {

    public int Add(int X, int Y) {
        return X + Y;
    }

}

// ...

public class MockClass : ITheClass {
    public int Add(int X, int Y) {
        return 5;
    }
}
					

Interface

Pros Cons
  • Can swap in different dependencies during testing
  • Can adjust implementation without breaking consumers
  • Gateway to Inversion of Control
  • Layers of indirection can confuse
  • Are you testing?

Repository

Thin veiner around expensive external systems

Problem: Need to contain change around external dependencies

Solution: A gateway class that separates the the external dependency from the rest of the application

Repository

source: http://fredwu.me/post/54009567748/datamappify-a-new-take-on-decoupling-domain

Repository


public class CustomerRepository {

    public Customer GetById(int Id) {
        using (var db = new DataContext()) {
            return db.Find(Id);
        }
    }

    public void Save(Customer Customer) {
        Customer.Modified = DateTime.Now;
        using (var db = new DataContext()) {
            db.Attach(Customer);
            db.SaveChanges();
        }
    }

}
					

Repository

Pros Cons
  • Easy to audit external system use
  • Can easily evolve with external system change
  • Layers of indirection can confuse
  • Is the complexity necessary?
  • How often will you change your database?

Comments

Problem: What was this doing?

Solution: Add notes for future developers about why these choices were made

Comments


public class ConfusingClass {

    public void Iduno(int inData) {
        int mutex = new Mutex(101,504,inData);
        for(int i = 0; i < 200; i++) {
            mutex.Next(i);
        }
    }

    // Count the salamander's weight relative to its height
    public void IKnow(int SalamanderSize) {
        // 101 is the weight of the object
        // 504 is the number of seconds it will run
        int mutex = new Mutex(101,504,SalamanderSize);
        // 200 is the max speed
        for(int i = 0; i < 200; i++) {
            mutex.Next(i);
        }
    }

}
					

Comments

Pros Cons
  • Provide clues to future developers
  • Explain why decisions were made
  • Explain confusing parts of the code
  • Easy to get code and comments out of sync
  • Easy to explain irrelevant things -- like what instead of why
  • "The code is the documentation"

This is only the beginning

Gang of Four

wikipedia.org/wiki/Design_Patterns