.Net, Design Patterns

Abstract Factory Pattern in C#

Introduction

According to Wikipedia:

The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes.

According to Gang Of Four:

Provide an interface for creating families of related or dependent objects without
specifying their concrete classes.

Use of this pattern makes it possible to interchange concrete implementations without changing the code that uses them, even at runtime.

UML:

677px-Abstract_factory_UML.svg

When to use it?

  • When the system is independent on how the objects are created.
  • When the system should work with multiple families of product.
  • When an interface is required to create similar family products, not the implementation.

Why to use it?

  • Provides an interface for creating objects which are related or dependent objects without specifying concrete classes
  • Calling code works with abstract interface and classes and does not need to know the exact types of the objects created by the factory

The main players in the pattern are:

  • AbstractFactory – declares a interface for operations that create abstract products.
  • ConcreteFactory – implements operations to create concrete products.
  • AbstractProduct – declares an interface for a type of product objects.
  • Product – defines a product to be created by the corresponding ConcreteFactory; it implements the AbstractProduct interface.
  • Client – uses the interfaces declared by the AbstractFactory and AbstractProduct classes.

Example in C#

ComputerFactory.cs (Abstract Factory)


namespace DesignPatterns.AbstractPattern {
abstract class ComputerFactory {
public abstract Computer GetComputer();

}//ComputerFactory
}

ConcreteComputerFactory.cs (ConcreteFactory)


namespace DesignPatterns.AbstractPattern {
class ConcreteComputerFactory : ComputerFactory {

public override Computer GetComputer() {

return new ConcreteComputer();

}//GetComputer

}//ConcreteComputerFactory
}

Computer.cs (Abstract Product)


namespace DesignPatterns.AbstractPattern {
abstract class Computer {

public abstract int Mhz { get; }

}//Computer
}

ConcreteComputer.cs (Product)


namespace DesignPatterns.AbstractPattern {
class ConcreteComputer : Computer {

int _mhz = 500;

public override int Mhz{
get { return _mhz; }
}//Mhz

}//ConcreteComputer
}

Client Code


using System;

namespace DesignPatterns.AbstractPattern {
class AbstractFactoryPatternClient : IDesignPattern {
public void Run() {
Console.WriteLine("\n------------------Abstract Factory Pattern------------------");
ComputerFactory factory = new ConcreteComputerFactory();

new ComputerAssembler().AssembleComputer(factory);
}
}
}

ComputerAssembler.cs


using System;

namespace DesignPatterns.AbstractPattern {
class ComputerAssembler {
public void AssembleComputer(ComputerFactory factory) {

Computer computer = factory.GetComputer();
Console.WriteLine("Assembled a {0} running at {1} MHz",
computer.GetType().FullName, computer.Mhz);

}//AssembleComputer
}
}

Output:

Assembled a DesignPatterns.AbstractPattern.ConcreteComputer running at 500 MHz

So whats happening here?

If you see the client code its passing an Abstract factory to the ComputerAssembler class and this class has a public method AssembleComputer which takes the Abstract factory class and calls the exposed public method of that factory.

So good thing here is that the AssembleComputer does not care about any specific object but it can support any type of ComputerFatcory which in this example its the ConcreteComputerFacory which implements ComputerFactory; and then it calls the GetComputerMethod which returns a concrete computer which implements Computer abstract class, which again decouples it from any concrete class.

Basically idea is to decouple the implementation logic to any specific concrete class, in this way every concrete class will have an common interface to implement and the client can call any method of the Product without worrying the exact object type.

Code can be found at GitHub

Reference:

 

 

.Net, Design Patterns

Design Patterns using C#

My notes on various design patterns from the book of Gang of Four (Design Patterns – Elements of Reusable Object Oriented Software). Sharing so that others also could benefit from this.

Creational Patterns

  • Prototype Pattern: The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects. Read the article
  • Abstract Factory Pattern: This pattern is used when creating families of related or dependent objects without specifying their concrete classes by using a common interface. Read the article
.Net, Software Architecture

Prototype Pattern using C#

Introduction

According to Wikipedia:

The prototype pattern is a creational design pattern in software development. It is used when the type of objects to create is determined by a prototypical instance, which is cloned to produce new objects.

According to Gang Of Four:

Specify the kinds of objects to create using a prototypical instance, and create new objects by copying this prototype.

UML:

600px-Prototype_UML.svg

When to use it?

  1. To avoid the inherent cost of creating a new object in the standard way (e.g., using the ‘new’ keyword) when it is prohibitively expensive for a given application.
  2. Hide concrete classes from the client.
  3. Add and remove new classes (via prototypes) at runtime.
  4. Keep the number of classes in the system to a minimum.
  5. Adapt to changing structures of data at runtime.

The main players in the pattern are:

IPrototype
Defines the interface that says prototypes must be cloneable

Prototype
A class with cloning capabilities

PrototypeManager
Maintains a list of clone types and their keys

Client
Adds prototypes to the list and requests clones

Example in C#

PrototypeBase.cs

using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace DesignPatterns.PrototypePattern {
[Serializable]
public abstract class PrototypeBase<T> {

// Shallow clone
public T Clone() {
return (T)this.MemberwiseClone();
}

//Deep Clone
public T DeepClone() {
MemoryStream stream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, this);
stream.Seek(0, SeekOrigin.Begin);
T copy = (T)formatter.Deserialize(stream);
stream.Close();
return copy;
}
}
}

Prototype.cs

using System;

namespace DesignPatterns.PrototypePattern {
[Serializable]
public class Prototype : PrototypeBase<Prototype> {
// Content members
public string Country { get; set; }
public string Capital { get; set; }
public DeeperData Language { get; set; }

public Prototype(string country, string capital, string language) {
Country = country;
Capital = capital;
Language = new DeeperData(language);
}

public override string ToString() {
return Country + "\t\t" + Capital + "\t\t->" + Language;
}
}
}

DeeperData.cs

using System;

namespace DesignPatterns.PrototypePattern {
[Serializable]
public class DeeperData {
public string Data { get; set; }
public DeeperData(string s) {
Data = s;
}
public override string ToString() {
return Data;
}
}
}

PrototypeManager.cs

using System.Collections.Generic;

namespace DesignPatterns.PrototypePattern {
public class PrototypeManager {
public Dictionary<string, Prototype> prototypes
= new Dictionary<string, Prototype> {
{"Germany",
new Prototype("Germany", "Berlin", "German")},
{"Italy",
new Prototype("Italy", "Rome", "Italian")},
{"Australia",
new Prototype("Australia", "Canberra", "English")}
};
}
}

PrototypeClient.cs

using System;

namespace DesignPatterns.PrototypePattern {
public class PrototypeClient : IDesignPattern {

private void Report(string s, Prototype a, Prototype b) {
Console.WriteLine("\n" + s);
Console.WriteLine("Prototype " + a + "\nClone      " + b);
}

public void Run() {
Console.WriteLine("\n------------------Prototype Pattern------------------");
PrototypeManager manager = new PrototypeManager();
Prototype c2, c3;

// Make a copy of Australia's data
c2 = manager.prototypes["Australia"].Clone();
Report("Shallow cloning Australia\n===============",
manager.prototypes["Australia"], c2);

// Change the capital of Australia to Sydney
c2.Capital = "Sydney";
Report("Altered Clone's shallow state, prototype unaffected",
manager.prototypes["Australia"], c2);

// Change the language of Australia (deep data)
c2.Language.Data = "Chinese";
Report("Altering Clone deep state: prototype affected *****",
manager.prototypes["Australia"], c2);

// Make a copy of Germany's data
c3 = manager.prototypes["Germany"].DeepClone();
Report("Deep cloning Germany\n============",
manager.prototypes["Germany"], c3);

// Change the capital of Germany
c3.Capital = "Munich";
Report("Altering Clone shallow state, prototype unaffected",
manager.prototypes["Germany"], c3);

// Change the language of Germany (deep data)
c3.Language.Data = "Turkish";
Report("Altering Clone deep state, prototype unaffected",
manager.prototypes["Germany"], c3);
}
}
}

Output:

Shallow cloning Australia
===============
Prototype Australia             Canberra                ->English
Clone      Australia            Canberra                ->English

Altered Clone’s shallow state, prototype unaffected
Prototype Australia             Canberra                ->English
Clone      Australia            Sydney          ->English

Altering Clone deep state: prototype affected *****
Prototype Australia             Canberra                ->Chinese
Clone      Australia            Sydney          ->Chinese

Deep cloning Germany
============
Prototype Germany               Berlin          ->German
Clone      Germany              Berlin          ->German

Altering Clone shallow state, prototype unaffected
Prototype Germany               Berlin          ->German
Clone      Germany              Munich          ->German

Altering Clone deep state, prototype unaffected
Prototype Germany               Berlin          ->German
Clone      Germany              Munich          ->Turkish

So whats happening here?

Firstly we are using two ways to clone the object

  1. Shallow Clone: In .Net we have a method called MemberwiseClone, its a method that is available on all objects. It copies the values of all fields and any references, and returns a reference to this copy. However, it does not copy what the references in the object point to.Many objects are simple, without references to other objects, and therefore shallow copies are adequate.To preserve the complete value of the object, including all its subobjects use a deep copy.
  2. Deep Clone: An alternative is a deep copy, meaning that fields are dereferenced: rather than references to objects being copied, new copy objects are created for any referenced objects, and references to these placed in B. The result is different from the result a shallow copy gives in that the objects referenced by the copy B are distinct from those referenced by A, and independent. Deep copies are more expensive, due to needing to create additional objects, and can be substantially more complicated, due to references possibly forming a complicated graph. In the .NET Framework they are encapsulated in a process called serialization, which can be defined by using attribute [Serializable] on the class to be serialized. Objects are copied to a given destination and can be brought back again at will. The options for serialization destinations are several, including disks and the Internet, but the easiest one for serializing smallish objects is memory itself. Thus a deep copy consists of serializing and deserializing in one method.

Then we are also using a PrototypeManager, which I mentioned earlier that its used to maintains a list of clone types and their keys so that we can retrieve an already cloned object quickly.

The main program consists of a series of experiments demonstrating the effects of cloning and deep copying.

In the first group, Australia is shallow copied. After changing of Australia’s clone, the capital is Canberra in the prototype and Sydney in the clone. The statement responsible is   c2.Capital = “Sydney”;

however, changing the language to Chinese (  c2.Language.Data = “Chinese”;) also changes the prototype’s language to Chinese. That is not what we wanted. We got the error because we did a shallow copy and the language in the prototype and in the clone reference the same DeeperData object.

In the next experiment, we clone Germany using a deep copy ( c3 = manager.prototypes[“Germany”].DeepClone();).

Now changing the capital ( c3.Capital = “Munich”;) does not affect the prototype but the deep clone is affected. Similarily  altering the deep state-its language to Turkish ( c3.Language.Data = “Turkish”;), shows the prototype after the changes; it is unaffected.

That’s all about Prototype Pattern.

Code can be found at GitHub

Reference: