Software Design Patterns
Florian Rappl, Fakultät für Physik, Universität Regensburg
Software Design Patterns
Introduction to modern software architecture
Creational patterns
Introduction
- Deal with object creation
- Try to control the object creation process
- Basically two intentions are often seen here:
- Making systems independent of specific classes
- Hiding how classes are created and combined
- Goal: Minimizing dependencies and complexities while maximizing flexibility
The singleton pattern
- Restricts the instantiation of a class to one object
- Useful if only one instance is needed and this instance is used by a lot of objects
- Much better controlled than global variables
- Encapsulates the given set of variables
- Can be resource friendly by allocating memory when required, not before
- Required: Private or protected constructor and static readonly property to access instance
Singleton diagram
Remarks
- There are multiple variants - the shown one is called lazy initialization and represents the most common approach
- By using a static constructor we could have an eager initialization and avoid the unnecessary condition in the property
- Therefore there is a way that uses both approaches to have the advantages of lazy loading without any repeating condition testing
- Another advantage of this solution is that it would be thread-safe (the original lazy is not thread-safe)
Lazy loading singleton
public class Singleton
{
private static Singleton instance;
private Singleton() {}
public static Singleton Instance
{
get
{
if (instance == null)
instance = new Singleton();
return instance;
}
}
}
public class Singleton {
private static Singleton instance;
private Singleton() { }
public static Singleton getInstance() {
if(instance == null)
instance = new Singleton();
return instance;
}
}
class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance;
return instance;
}
private:
Singleton() {}
Singleton(Singleton const&);
void operator=(Singleton const&);
};
Practical considerations
- Quite often used together with other patterns
- Central location for variables (like a namespace), but more clean and encapsulated
- Can be controlled quite easily (
getInstance
could have more logic) - Configuration of application stored in a Singleton allows easy and automated serialization and deserialization of stored values
- APIs make great use of singletons to provide a single entry point
- Enhance a resource friendly way of using particular objects
Global application state
public sealed class ApplicationState
{
private static ApplicationState _instance;
private static object _lockThis = new object();
private ApplicationState() { }
public static ApplicationState Instance
{
get
{
if (_instance == null)
{
lock (_lockThis)
{
if (_instance == null)
_instance = new ApplicationState();
}
}
return _instance;
}
}
public string LoginId { get; set; }
public int MaxSize { get; set; }
}
The prototype pattern
- Use exchangeable objects as parts of a class, instead of deriving from another class
- Big advantage: We do not have to destroy the complete object to change the parent (which is now the prototype)
- Some languages are actually using this pattern implicitly
- The creation of the prototype is usually done by calling a cloning method on an already existing instance
- The cloning process should result in a deep copy of the prototype
Prototype diagram
Remarks
- Cloning is an important part, but it should not be the only reason to follow the prototype pattern
- .NET and Java have already a single purpose interface for cloning alone
- The main reason for this pattern is flexibility
- Prototype doesn't require subclassing, but an initialize operation
- One instance of a class is used as a breeder of all future instances
- Prototype is unique among the other creational patterns since it only requires an object, not a class
Base class prototype
abstract class Prototype
{
private readonly string _id;
protected Prototype(string id)
{
_id = id;
}
public string Id
{
get { return _id; }
}
public abstract Prototype Clone();
}
class ConcretePrototype : Prototype
{
public ConcretePrototype(string id)
: base(id)
{
}
public override Prototype Clone()
{
return (Prototype)MemberwiseClone();
}
}
abstract class Prototype {
private final string _id;
protected Prototype(string id) {
_id = id;
}
public string getId() {
return _id;
}
public abstract Prototype clone();
}
class ConcretePrototype extends Prototype {
public ConcretePrototype(string id) {
super(id);
}
@Override
public Prototype clone() {
return (Prototype)Object.clone(this);
}
}
class Prototype {
private:
const string _id;
protected:
Prototype(string id) : _id(id) {
}
public:
const string& getId() const {
return _id;
}
virtual Prototype* Clone() = 0;
};
class ConcretePrototype : public Prototype {
public:
ConcretePrototype(string id) : Prototype(id) {
}
virtual Prototype* Clone() {
return new ConcretePrototype(*this);
}
};
Practical considerations
- The prototype pattern can save resources by cloning only certain parts and omitting some initialization
- Usually we will get a general object back, so casting might be required
- Designs that make heavy use of the other patterns (e.g. composite, decorator, ...) often can benefit from prototype as well
- A Singleton that contains all prototype objects is quite useful
- The singleton would then have a method to create (clone) a specific prototype
Changing a template
public abstract class Template
{
public abstract Template Copy();
public Color Foreground { get; set; }
public Color Background { get; set; }
public string FontFamily { get; set; }
public double FontSize { get; set; }
/* ... */
protected static void CopyBasicFields(Template original, Template copy)
{
copy.Foreground = original.Foreground;
/* ... */
}
}
public class DefaultTemplate : Template
{
public DefaultTemplate()
{
Foreground = Color.FromRgb(0, 0, 0);
Background = Color.FromRgb(255, 255, 255);
FontFamily = "Arial";
FontSize = 12.0;
}
public override Template Copy()
{
DefaultTemplate copy = new DefaultTemplate();
CopyBasicFields(this, copy);
/* ... */
return copy;
}
}
public class Document
{
Template _template;
public Document(Template template)
{
ChangeTemplate(template);
}
public void ChangeTemplate(Template template)
{
_template = template.Copy();
}
}
The factory method pattern
- Create specific classes without using them
- Idea: Tell a central factory which class to create (e.g. by using a string)
- The factory then knows which class to use and how to create
- Goal: Maintainability and extendability gain
- Using DRY: Creation code on a single central place
- Information hiding is possible if special information is required that only the factory needs to know about
Factory method diagram
Remarks
- The factory method exists in a lot of variations and makes most sense with otherwise complicated APIs or library extensions
- Generally one might think of two more methods in the factory to register and unregister new products with instructions on how to create
- Languages like C# or Java have Reflection built-in, which can enable automatically constructed factories
- Quite often the factory method pattern is displayed with an abstract class for the factory, but actually this is not required
String based factory
public abstract class SmartPhone
{
public abstract double Price { get; }
/* ... */
}
class GalaxyS3 : SmartPhone
{
public override double Price
{
get { return 599.99; }
}
/* ... */
}
public class Factory
{
public SmartPhone Produce(String type)
{
switch (type)
{
case "GalaxyS3":
return new GalaxyS3();
/* ... */
default:
return null;
}
}
}
class Consumer
{
private SmartPhone phone;
private double money;
public void BuyNewSmartPhone(Factory factory, String type)
{
phone = factory.Produce(type);
money -= phone.Price;
}
}
public abstract class SmartPhone {
public abstract double getPrice();
/* ... */
}
class GalaxyS3 extends SmartPhone {
@Override
public double getPrice() {
return 599.99;
}
/* ... */
}
public class Factory {
public SmartPhone produce(String type) {
switch (type) {
case "GalaxyS3":
return new GalaxyS3();
/* ... */
default:
return null;
}
}
}
class Consumer {
private SmartPhone phone;
private double money;
public void buyNewSmartPhone(Factory factory, String type) {
phone = factory.produce(type);
money -= phone.getPrice();
}
}
class SmartPhone {
public:
virtual double getPrice() = 0;
/* ... */
};
class GalaxyS3 : public SmartPhone {
public:
double getPrice() {
return 599.99;
}
/* ... */
};
class Factory {
public:
SmartPhone* Produce(string type) {
if (type == "GalaxyS3")
return new GalaxyS3();
return NULL;
}
};
class Consumer {
private:
SmartPhone* phone;
double money;
public:
void BuyNewSmartPhone(Factory* factory, string type)
{
phone = factory->Produce(type);
money -= phone->getPrice();
}
};
Practical considerations
- Using the singleton pattern for the factory is often useful
- If the products follow the prototype pattern then object creation (and registration, if provided) is really simple and straight forward
- One of the most famous factories can be found in the browser: the DOM only provides access for creating elements over a factory
- Actually we use already a factory by typing in addresses (scheme)
- Games quite often make use of factories to produce items
- The provided abstraction of factories help to reduce dependencies
A factory for commands
interface ICommand
{
string Info { get; }
string Help { get; }
bool CanUndo { get; }
string[] Calls { get; }
string FlushOutput();
bool Invoke(string command, string[] arguments);
void Undo();
}
class Commands
{
static Commands instance;
ICommand[] commands;
Stack<ICommand> undoList;
private Commands()
{
undoList = new Stack<ICommand>();
undoCallings = new List<string>();
commands = Assembly.GetExecutingAssembly()
.GetTypes()
.Where(m => !m.IsInterface && typeof(ICommand).IsAssignableFrom(m))
.Select(m => m.GetConstructor(System.Type.EmptyTypes).Invoke(null) as ICommand)
.ToArray();
}
public static Commands Instance
{
get { return instance ?? (instance = new Commands()); }
}
public ICommand Create(string command, params string[] args)
{
foreach (ICommand cmd in commands)
{
if (CheckForCall(cmd, command))
return cmd;
}
return null;
}
public bool Invoke(string command, params string[] args)
{
ICommand cmd = Create(command, args);
if (cmd != null && cmd.Invoke(command, args))
{
if (cmd.CanUndo)
undoList.Push(cmd);
return true;
}
return false;
}
}
Discussion
- Singletons are sometimes overused, but can be used in many situations
- The factory method is particularly useful to decouple dependencies on children of classes
- The ability to clone itself is interesting especially if construction would be impossible otherwise (due to dependencies or language constraints)
- Decoupling the factory can also be very beneficial, which results in the next pattern: The abstract factory pattern
The abstract factory pattern
- What if we need specializations of outcoming products?
- Goal: Provide an interface for creating families of related or dependent objects without specifying their concrete classes
- Instead of wanting Product instances we want ProductVariant objects, with specialized implementations
- However, we do not need to know which factory is producing it
- This decouples a concrete factory from our code and enables application aware productions
Abstract factory diagram
Remarks
- Abstract factories allow cross-platform development
- Trick: Extract concrete factory and products into adapter library
- Exchange adapter library depending on platform
- There could also be a factory of factories, using both, the Factory method and the abstract factory pattern
- Abstract factories in this form are not as popular as a plain factory
- Most popular is a hybrid approach, that takes an abstract factory (e.g. defined in an interface), to produce a single type of product
Method based abstract factory
public abstract class Control
{
protected abstract void PaintControl();
}
public abstract class Button : Control
{
}
public abstract class TextBox : Control
{
}
class WinButton : Button
{
protected override void PaintControl()
{
/* Implementation */
}
}
class WinTextBox : TextBox
{
protected override void PaintControl()
{
/* Implementation */
}
}
class OsxButton : Button
{
protected override void PaintControl()
{
/* Implementation */
}
}
class OsxTextBox : TextBox
{
protected override void PaintControl()
{
/* Implementation */
}
}
public interface IFactory
{
Button CreateButton();
TextBox CreateTextBox();
}
public class WinFactory : IFactory
{
public Button CreateButton()
{
return new WinButton();
}
public TextBox CreateTextBox()
{
return new WinTextBox();
}
}
public class OsxFactory : IFactory
{
public Button CreateButton()
{
return new OsxButton();
}
public TextBox CreateTextBox()
{
return new OsxTextBox();
}
}
public abstract class Control {
protected abstract void paintControl();
}
public abstract class Button extends Control {
}
public abstract class TextBox extends Control {
}
class WinButton extends Button {
@Override
protected void paintControl() {
/* Implementation */
}
}
class WinTextBox extends TextBox {
@Override
protected void paintControl() {
/* Implementation */
}
}
class OsxButton extends Button {
@Override
protected void paintControl() {
/* Implementation */
}
}
class OsxTextBox extends TextBox {
@Override
protected void paintControl() {
/* Implementation */
}
}
public interface Factory {
Button createButton();
TextBox createTextBox();
}
public class WinFactory implements Factory {
public Button createButton() {
return new WinButton();
}
public TextBox createTextBox() {
return new WinTextBox();
}
}
public class OsxFactory implements Factory {
public Button CreateButton() {
return new OsxButton();
}
public TextBox createTextBox() {
return new OsxTextBox();
}
}
class Control {
protected:
virtual void PaintControl() = 0;
};
class Button : public Control {
};
class TextBox : public Control {
};
class WinButton : public Button {
protected:
void PaintControl() {
/* Implementation */
}
};
class WinTextBox : public TextBox {
protected:
void PaintControl() {
/* Implementation */
}
};
class OsxButton : public Button {
protected:
void PaintControl() {
/* Implementation */
}
};
class OsxTextBox : public TextBox {
protected:
void PaintControl() {
/* Implementation */
}
};
class Factory {
public:
virtual Button* CreateButton() = 0;
virtual TextBox* CreateTextBox() = 0;
};
class WinFactory : public Factory {
public:
Button* CreateButton() {
return new WinButton();
}
TextBox* CreateTextBox() {
return new WinTextBox();
}
};
class OsxFactory : public Factory {
public:
Button* CreateButton()
{
return new OsxButton();
}
TextBox* CreateTextBox() {
return new OsxTextBox();
}
};
Practical considerations
- The abstract factory pattern is quite often used with dependency injection (DI), which will be introduced later
- Here the concrete products will be resolved automatically
- Also the factory can then be resolved automatically
- This yields a maximum an flexibility and minimizes the coupling
- There are complete frameworks like ADO.NET build upon this
- Idea: Abstract the interface to the database and hide concrete implementations for specific databases (like MySQL, MariaDB, ...)
ADO.NET abstract factory
The builder pattern
- Quite often a defined sequence of method calls is required for correctly initializing objects
- This scenario goes far beyond what a constructor should deliver
- Idea: Create a pattern, which follows the right sequence by design
- The builder pattern enables the step-by-step creation of complex objects using the correct sequence of actions
- Here the construction is controlled by a director object that only needs to know the type of object it is to create
Builder diagram
Remarks
- The flow requires that a calling object needs to know the concrete builder and the director
- In general the directory does not require to get the builder over the constructor, it could also be set by using a property
- However, the director requires a builder, instance which is reflected by such a design
- The director handles the whole construction process
- In the end one just has to call
getResult()
on the chosen builder
Fluent interface
- The builder pattern is a solution to the telescoping constructor problem
- Instead of using numerous constructors, the builder pattern uses another object, that receives each initialization parameter step by step
- Builders are good candidates for a fluent API using the fluent interface
- This can build upon chaining, i.e. we always return something useful (if nothing would be returned, then the current instance should be returned)
Simple builder
public interface IBuilder
{
void BuildPartA();
void BuildPartB();
Product Result() { get; }
}
static class Director
{
public static void Construct(IBuilder builder)
{
builder.BuildPartA();
builder.BuildPartB();
}
}
class ConcreteBuilder : IBuilder
{
private Product _product;
public ConcreteBuilder
{
_product = new Product();
}
public void BuildPartA()
{
/* ... */
}
public void BuildPartB()
{
/* ... */
}
public Product Result
{
get { return _product; }
}
}
class Product
{
/* ... */
}
public interface Builder {
void buildPartA();
void buildPartB();
Product getResult();
}
static class Director {
public static void construct(Builder builder) {
builder.buildPartA();
builder.buildPartB();
}
}
class ConcreteBuilder implements Builder {
private Product _product;
public ConcreteBuilder {
_product = new Product();
}
public void buildPartA() {
/* ... */
}
public void buildPartB() {
/* ... */
}
public Product getResult() {
return _product;
}
}
class Product {
/* ... */
}
class Builder {
public:
virtual void BuildPartA() = 0;
virtual void BuildPartB() = 0;
virtual Product* GetResult() = 0;
};
class Director {
public:
static void Construct(Builder builder) {
builder.BuildPartA();
builder.BuildPartB();
}
};
class ConcreteBuilder : public Builder {
private:
Product* _product;
public ConcreteBuilder {
_product = new Product();
}
public void BuildPartA() {
/* ... */
}
public void BuildPartB() {
/* ... */
}
public Product* GetResult() {
return _product;
}
};
class Product {
/* ... */
};
Practical considerations
- Quite often a static class is enough, with the builder dependency being shifted to the construction function
- The director might also create intermediate objects, which can then be implicitly buffered or destroyed
- Sometimes there is no specific director class, but a director method
- This could be done to centralize the director and minimize dependencies
- An example would be the construction of database commands
Database command builder
public abstract class DbCommandBuilder
{
public abstract DbCommand GetDeleteCommand();
/* ... */
}
public class SqlCommandBuilder : DbCommandBuilder
{
private DbCommand BuildDeleteCommand(DataTableMapping mappings, DataRow dataRow)
{
DbCommand command = this.InitializeCommand(this.DeleteCommand);
StringBuilder builder = new StringBuilder();
int parameterCount = 0;
builder.Append("DELETE FROM ");
builder.Append(this.QuotedBaseTableName);
parameterCount = this.BuildWhereClause(
mappings, dataRow, builder, command, parameterCount, false);
command.CommandText = builder.ToString();
RemoveExtraParameters(command, parameterCount);
this.DeleteCommand = command;
return command;
}
public SqlCommandBuilder(SqlDataAdapter da)
{
/* set some properties */
}
public override DbCommand GetDeleteCommand()
{
DataTableMapping mappings = ComputeMappings();
DataRow row = ComputeRow();
return BuildDeleteCommand(mappings, row);
}
}
Literature
- Gamma, Erich; Helm, Richard; Johnson, Ralph; Vlissides, John (1995). Design Patterns: Elements of Reusable Object Oriented Software.
- Freeman, Eric; Freeman, Elisabeth; Kathy, Sierra; Bert, Bates (2004). Head First Design Patterns.
- Fowler, Martin; Beck, Kent; Brant, John; Opdyke, William; Roberts (1999). Refactoring: Improving the Design of Existing Code.
- McConnell, Steve (2004). "Design in Construction". Code Complete.