Copyright © 2012 David Schmidt

Lecture 15:
Miscellaneous topics

15.1 Version control for project management
15.2 Design Patterns: when to use them

Here are some notes that might be useful. I've not had the time to write all the details, but there is enough info to get you going.

15.1 Version control for project management

To survive any large software project, you must manage backups, variations, and extensions of your system. If you work in a team, you must integrate all team members' work in a coherent way. It's best to use a professional version control tool to do this.

What it is

Michael Ernst at the Univ. of Washington has written a nice introductory article about version control and a few popular tools. (I've created a local copy here.)

Git-based version control

Git-based version control adapts well to various development situations. Use Git if possible. There is a nice, free, on-line book, Pro Git, by Scott Chacon.

I like the explanation (and pictures!) in Section 1.3 about how Git differs from Subversion-style version control.

Git supports well branch-merge, feature-based, "agile"-style of software development. This Wikipedia page has a simple example of a branch-merge diagram.

There is even a software development methodology based on Git, called "git flow", where you manage an evolving project with Git as your main tool. See this article. (I've created a local copy here.)

Version control in Visual Studio 2013

Both Subversion- and Git-style version control are now intergrated into Visual Studio 2013. Here are some links to see how to use it:
Here are some of Microsoft's pages, which are drier but cover specifics:

15.2 Design Patterns: when to use them

Almost all presentations of design patterns start by showing you patterns, one after the other, until you have overdosed and can't remember what any of them are good for.

Here is a "reverse" table, that lists the problems you might face when you are designing a system and which patterns might help your situation. I've added a few patterns that weren't introduced in my lecture notes, but you can look them up and learn them easily.

But first,

  1. Use interfaces to define "plug-in" (exit) and "plug-out" (entry) points from/to each subassembly in your system.
  2. When a connection point is just a single method, because there is no need to know if there is even a class/object on the other end, use a delegate.

You've designed a complicated implementation of a data structure/data base, and you don't trust your users to use the structure correctly.
  • If it is complicated to traverse the structure, say to answer a query to it, incorporate iterator objects that collect the results from a traversal and hand them out to the user, one by one.
  • If your data structure is organized in layers or levels, or it contains a mix of various classes of elements that it stores, use the composite layout to define it.
  • Define a facade class that holds the methods for building a starter version of your data structure and methods for querying it and updated it.
  • To handle complex, SQL-like, queries, implement an interpreter.
  • If the structure holds hundreds or thousands of data objects all constructed from the same class, use the flyweight pattern to extract the data common to all the objects and save it in a "static" object that is shared by all the thousands of data objects.
Your system holds a model assembly that is queried a lot, and you are worried that the entry to the model is a "bottleneck".
  • In response to a client's login or access request, use a factory method to generate a helper object to give back to the client.
  • The object that is returned to the client might act as a remote proxy or a virtual proxy to relieve the load on the model.
Your subassembly builds compound objects that are assembled from a mix-and-match kit of "features". (Think of how a car is assembled with features and how a laptop is assembled with features.)
  • For software features that are field-based, use the decorator and composite patterns.
  • For software features that are method-based, use the chain of responsibility or command pattern, which allow you to connect together objects that hold useful command code and to pass around command sequences as objects.
You've written a driver or controller that is meant to work with different families of model objects that "plug into" the driver (e.g., a typesetter into which a font family is plugged or a graphics driver into which a widget family is plugged). Use the abstract factory pattern. It is one of the most important and well known patterns.
Two assemblies need to be connected, and they use different protocols (data args, method names, order of method calls). Use an adaptor.
You've connected multiple assemblies, and the result is a mess.
  • Use observers to decouple the assemblies.
  • Construct a mediator (a controller-for-the-controllers) to enforce a complex protocol between controllers.