Amr Elroumy's Blog

Science, Programming & Personal Encounters

Singleton Pattern

August 01, 2011


Pattern Name

Singleton Pattern

Classification

Creational Pattern

Intent

Ensure a class only has one instance, and offer a global point of access to that instance.

Motivation

In many situations you’d find yourself in need of ensuring that a certain class can only be instantiated once and is accessible in various parts of a system.
Example for such situations is:

  • Database access
  • Logging
  • Communication
  • Printer spooling
  • File systems

The singleton pattern provides us with two properties:

  • It ensures a class is only instantiated once.
  • It provides a global access point to that class.

Note Singletons are meant to be used sparingly, so if you find yourself using them everywhere you might want to take another look at your design.

Applicability

Use The Singleton pattern when you want your classes to provide you with only one instance that is globally accessible to the code from an access point.

Structure

Singleton

Participants

SingletonClass: This class provide global access to only a single instance of it by doing the following:

  • It hides its constructor to prevent any external classes from creating new instances.
  • It declares the static method GetInstance() that provides access to the single instance that is held in the private variable: instance.

Consequences

Like any pattern, the singleton pattern has its tradeoffs (according to its implementation) and if it is used while being unaware of these tradeoffs can produce unwanted results.

Criticisms

First criticism: Using it is considered a breach to one of the OOD Principles: The Single Responsibility Principle, which states that:

Every object should have a single responsibility, and that this responsibility should be entirely encapsulated by the class.

Thus, the class shouldn’t care less for being a Singleton or not, as it’s only concern should be doing its business responsibility. But when using Singleton, the class is responsible for two things here:

  • Its original business responsibility.
  • Managing the instantiation of its objects.

Second criticism: We all know that having global variables in our code, produces code smells. But, isn’t this what the Singleton Pattern does? It makes an instance of the class accessible globally. There disadvantages of using global variables that apply when we use Singletons without proper care because of the similarity in their nature.

Some of these disadvantages are:

  • Implicit coupling - Many programmers unconsciously would tightly couple the class that uses the Singleton Pattern with the rest of their code, because it’s globally accessible. When this is done, the overuse of this pattern could converge into becoming an anti-pattern.
  • Namespace pollution - Namespace pollution can occur when we abuse Singleton Pattern, though it is a bit unlikely if the pattern is used wisely.
  • Multithreading issues can occur when more than one thread the GetInstance() method is invoked for the first time. This will result in instantiating many instance at the same time. That’s why it is important to make sure that our code is thread safe by using Synchronization (sometimes discouraged because of its performance footprint), Lazy Instantiation using double-checked locking, or Early Instantiation. Choosing the implementation to use depends mainly on your needs and the available resources.
  • Unit-testing programs that contain singleton objects can become annoying because the Singleton objects have a persistent state throughout the program. Leading to the following:
  • It makes it hard to replicate the tests for later testing or even having a clean environment for testing.
  • It takes away one thing that makes unit testing effective which is having tests that are independent on each other.
  • Also many developers prefer making dependencies between classes obvious through method signatures, rather than having the Singleton object hidden in the code, lurking for the right moment to come and bite you in the back. Some might argue that passing that object through all this methods induces tight coupling, but on the contrary this way of exchanging objects allows you to write to an interface that can be easily extended without having to change a single line in your old code. Thus you’re coupling to an interface not to an implementation.

If we want to overcome the mentioned shortcomings, we could separate the singleton part from the actual object, allowing for the object itself to be used normally if needed. Perhaps we can use a Factory or a builder that would encapsulate the object creation and maintain having only one instance created of this class, while leaving the class responsible only for its business responsibility.

J. B. Rainsberger argued in his article “Use your Singletons wisely” that classes should not be responsible for the singleton part of the code:

Suppose an application needs only one instance of a class and the application configures that class at startup: Why should the class itself be responsible for being a singleton? It seems quite logical for the application to take on this responsibility, since the application requires this kind of behavior. The application, not the component, should be the singleton. The application then makes an instance of the component available for any application-specific code to use. When an application uses several such components, it can aggregate them into what we have called a toolbox.

Implementation

Naïve implementation

public class Singleton
{
    private static Singleton _instance;

    private Singleton()
    { }

    public Singleton GetInstance()
    {
        if (_instance == null)
            _instance = new Singleton();
        return _instance;
    }
}
Disadvantages of this implementation:

It is not thread safe as in some situations, separate threads can invoke the GetInstance for the first time simultaneously. So when both of them reaches the statement if (_instance == null) it will be evaluated as true, thus both threads will see that it is valid to instantiate a new object. To avoid this issue, we have to write thread-safe code .

Thread-safe Singleton using a simple Lazy Instantiation

public sealed class Singleton
{
    private static readonly Singleton instance = new Singleton();

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            return instance;
        }
    }
}

Having the Singleton instance referenced by a private static member variable, gives us an advantage: the actual instantiation of the Singleton object doesn’t occur until the class is referenced by a call to the Instance property. Thus, this solution provides us with a form of Lazy Instantiation.

Thread-safe Singleton using Double-Checked Locking

using System;

public sealed class Singleton
{
    private static volatile Singleton _instance;
    private static readonly object Lock = new Object();

    private Singleton() { }

    public static Singleton Instance
    {
        get
        {
            if (_instance == null)
            {
                lock (Lock)
                {
                    if (_instance == null)
                        _instance = new Singleton();
                }
            }
            return _instance;
        }
    }
}
This specific implementation of Double-Checked locking can be factored into:

When working in a multi-threaded environment, we risk running into issues that arise from resource-sharing between multiple threads, like deadlocks and race conditions (What is a race condition?). This implementation of the Singleton Pattern however saves us from all this drama.

  • Using volatile keyword.
  • Using the lock statement to accomplish thread synchronization.
  • Using double-checked locking.

The use of the volatile keyword on the instance member variable allows us to avoid the unexpected and unpredictable results in multi-threaded programs that access fields without synchronization and using the lock statement to synchronize access. The volatile keyword indicates that a certain field can be modified by multiple threads, avoiding the compiler optimizations that assume access by a single thread. This ensures that the most up-to-date value is present in the field at all times.

For more details on the volatile keyword and volatile fields check the following references: volatile (C# Reference) [MSDN Library] Volatile fields [MSDN Library]


Having data shared between threads is the best recipe for obscure errors and complexity when working in a multithreaded environment. A way of solving this problem is using locks in order to stop more than one thread from working on the same data at the same time.

The lock keyword marks a statement block as a critical section by obtaining the mutual-exclusion lock for a given object, executing a statement, and then releasing the lock (source: lock Statement [MSDN Library]). The idea is to ensure that a thread can’t enter a critical area in the code, when another thread is still executing it. If two threads encounter a lock, one thread waits(blocks) until the lock becomes available.

So, in layman’s terms we can say that the mutual-exclusion lock acts as a simple door lock. You enter a room, you lock the door behind you and when you finish your business, you unlock the door and come out of the room, only then can somebody else enter. If anybody tries to enter the room while you are still in there, he has to wait.

Important Notes

  • It’s a matter of good practice to avoid locking on a public type or an object that is accessible to other classes, because this risks running into performance issues and even deadlocks. Instead, we should use values of objects that are specifically made for the sole purpose of locking. These objects are commonly declared as private or private static.
  • It’s also worth knowing that when using mutual-exclusion locks, the blocked thread doesn’t consume any CPU resources unlike other locks like the Spinlock, nevertheless each has its advantages and usages.

You might ask yourself why can’t I just simply use the lock this way:

get
{
    lock (Lock)
    {
        if (_instance == null)
            _instance = new Singleton();
    }
    return _instance;
}

instead of the implemented one:

get
{
    if (_instance == null)
    {
        lock (Lock)
        {
            if (_instance == null)
                _instance = new Singleton();
        }
    }
    return _instance;
}

The problem with the first implementation is that it hinders the performance of the application because of the constant locking every time an instance is requested. Unlike the double-checked locking implementation, which makes sure that a lock is actually necessary and that there is no available instance.

Related Patterns

Abstract Factory , Builder , and Prototype.

References

Comments

Factory Method

July 20, 2011


Pattern Name

Factory Method

Classification

Creational Pattern

Intent

Define an interface for creating an object, but let subclasses decide which class to instantiate. Factory Method lets a class defer instantiation to subclasses.

Also known as

Virtual Constructor

Motivation

When we program to an implementation we risk making our code more fragile and less flexible. When we handle the instantiation of concrete classes explicitly, this is exactly what we are doing… By doing this, we subject our code to the severe impacts of change. One of these consequences is that we force ourselves to modify already existing code when we want to change the object creation routines or add new concrete classes, thus, breaching one of the OOD principles:

“Your code should be open for extension but closed for modifications.”

A common thing to do when we have a part of the code that is likely to change is to:

“Identify the aspects that vary and separate them from what stays the same.”

This is why we are needed to encapsulate the object creation and isolate it from the rest of the code, ensuring that any change in the object creation will remain transparent to the rest of the code. And here comes the role of the Factory Method pattern, as it handles the object creation and encapsulates it in a subclass. Thus, the client depends on the subclasses to handle all the object creation, so it doesn’t really know the kind of concrete product that is actually created and its code depends on an abstract product.

Notes:

  • Factory Patterns in general focus on encapsulating the instantiation of concrete types; abstracting the product creation process, so that the type of the concrete product can be determined in the runtime.
  • There are two types of Factory Method:
    • Parameterized: This can make more than one object based on the parameters passed to it.
    • Not parameterized: This can make only one object
  • When using parameterized Factory Methods it is important to make sure that the parameters are “type safe” and ensure that errors in parameters are caught at compile time; to prevent runtime errors. E.g. we could use enumerators.
  • Factory Method is similar to the Abstract Factory in that the methods of the Abstract factory can be implemented as Factory Methods. The main difference is that while abstract factory deals with an entire family of products, the Factory Method is only worried about a single product. Also Factory Methods use inheritance to get the concrete object as they delegate object creation to subclasses which implement the Factory Method, while Abstract Factory uses object composition.

Applicability

Use the Factory Method pattern when:

  • a class can't anticipate the class of objects it must create.
  • a class wants its subclasses to specify the objects it creates.
  • classes delegate responsibility to one of several helper subclasses, and you want to localize the knowledge of which helper subclass is the delegate. (source: GoF’s Book)

Important Note:

Factories can only be used with a family of classes. If these classes doesn’t extend the same base or interface, then we can’t use factories with them.

Structure

Factory_Method

Participants

  • CreatorBase: If we don’t need to extend the creator and have different creators the whole factory can be implemented here. Otherwise, this base is either an interface or an abstract class; depending on the situation and if we want to add specify some simple functionality that the concrete creators inherit.
  • ConcreteCreator: It inherits the CreatorBase class. It either uses the FactoryMethod as it is if it is implemented fully in the base class, or it overrides it with the suitable object creation code.
  • ProductBase: This abstract class defines the type of products that the FactoryMethod can create.
  • ConcreteProduct: The actual object that is created by the FactoryMethod.

Consequences

Advantages:
  • It enforces loose coupling between the Creator and the product. This is good, because it’ll allow us to add new products or change old ones without affecting our code or having to change it to fit into the new design as the code only deals with the product’s interface.
  • It allows us to encapsulate all the object creation procedures and use of constructors, leaving the object type and creation to be determined at the runtime instead of being hardwired in the code.
  • Using this pattern reinforces the principle of programming to an interface instead of an implementation, as we separate the object instantiation from the creators and are putting them in separate objects.
  • Provides hooks for subclasses. Creating objects inside a class with a factory method is always more flexible than creating an object directly. Factory Method gives subclasses a hook for providing an extended version of an object. (Source: GoF’s book)

Implementation

abstract class ProductBase
{
}

class ConcreteProductA : ProductBase
{
}

class ConcreteProductB : ProductBase
{
}

abstract class CreatorBase
{
    public enum ProductType
    {
        TypeA,
        TypeB
    }

    public abstract ProductBase FactoryMethod(ProductType type);
}

class ConcreteCreator : CreatorBase
{
    public override ProductBase FactoryMethod(ProductType type)
    {
        if (type == ProductType.TypeA)
            return new ConcreteProductA();
        if(type == ProductType.TypeB)
            return new ConcreteProductB();
        throw new ArgumentException(Invalid argument, type.ToString());
    }
}

References

Comments

Decorator Pattern

July 09, 2011


Pattern Name

Decorator Pattern

Classification

Structural Pattern

Intent

The Decorator Pattern attaches additional responsibilities to an object dynamically. Decorators provide a flexible alternative to subclassing for extending functionality.

Also known as

Wrapper

Motivation

You might find yourself while working on a project and in the need of adding/modifying responsibilities or behaviors to objects of a certain type. One way of approaching this problem is that we extend this type using inheritance and add the new behavior to it in its children. But what would happen when we want to add more than one responsibility for some objects while keeping others with only a single new responsibility? Then, we would have to implement all the combinations of these responsibilities and ending up with what can be called as a class explosion; as you’ll end up with a lot of classes, more time to debug and a lot of those long & lonely nights of maintenance.

An answer to this problem would be to extend the behavior without using inheritance. This can be achieved by using composition to wrap the components by the decorator and add new behavior/properties or modify already existing ones in the runtime. Using this approach we can avoid having to use inheritance and constrain ourselves with static (compile-time) extending to the objects and use instead dynamic (runtime) extending of those specific objects. Also it allows us to add the needed behavior according to the specific conditions that occur during the runtime; meaning that objects of the same type can end up being decorated differently and that the same object can be wrapped in more than one decorator.

Design principles behind this pattern:

  • Open-Closed Principle from SOLID Principles Classes should be open for extension, but closed for modification. One reason why this principle is important is that it prevents us from introducing bugs into code that has already been tested and debugged.

Important Notes:

  • The decorator pattern is used to extend behavior of objects during the runtime not classes.
  • You can't apply the Open-Closed Principle to every part of your design, because it'll add complexity to the code and it'll take time and effort. You need to identify—comes with experience—the parts of the code that are most likely to change and apply the principle on them.
  • Using decorators with code that relies on the component's concrete type will break that code. But if we only use them with code that depends on the abstract component's type, then the decorators will remain transparent to the client of the component.

Applicability (from GoF’s Book)

  • To add responsibilities to individual objects dynamically and transparently, that is, without affecting other objects.
  • For responsibilities that can be withdrawn.
  • When extension by subclassing is impractical. Sometimes a large number of independent extensions are possible and would produce an explosion of subclasses to support every combination. Or a class definition may be hidden or otherwise unavailable for subclassing.

Structure

Decorator Pattern Structure

You might ask yourself if the decorator pattern teaches us to favor composition over inheritance, then why does the DecoratorBase class extends the Object's super class? The answer is that the decorator is extending this class to gain the same interface as the object it will wrap, not to gain the behavior. Thus we extend the base class to get the interface but we're getting the behavior through composition of decorators with the base components as other decorators.

Participants

  • ComponentBase: Defines the basic behaviors/properties that would be implemented by the components that will be decorated. If we don’t want to define any actual functionality in it, you can use an interface instead of an abstract class.
  • DecoratorBase: It is the abstract class for all the decorators. It inherits the basic interface of the components from the ConcreteBase, to allow the decorators to be used in place of the components. You’ll notice that it adds a constructor that accepts a Component of type ComponentBase; this is the Component to be wrapped.
  • ConcreteComponent: The actual components that will be wrapped.
  • ConcreteDecoratorA/ConcreteDecoratorA: The decorators that wrap Components. They can introduce new behavior/properties like ConcreteDecoratorA or modify already existing ones like ConcretDecoratorB.

Properties of decorators:

  • They have the same supertype as the objects they decorate.
  • You can use one or more decorators to wrap an object.
  • The decorator adds its own behavior either before and/or after delegating to the object it decorates to do the rest of the job.
  • Objects can be decorated at runtime.

Consequences

Advantages of using the decorator pattern:

  • It allows us to add new behavior to existing code without the need to modify it (Following the Open-Closed Principle) or without having to use inheritance, thus avoiding inheritance’s rigidity and adding more flexibility to the code.
  • Avoids having classes with complicated and complex responsibilities high up in the class hierarchy. Instead we use simple classes that are customizable and whose responsibilities can be added during the runtime.

One of the shortcomings of using the Decorator Pattern:

  • Using this pattern in your design, you'll end up with a relatively large number of small objects that differ in the way they are assembled. This can be overwhelming to a developer trying to use the Decorator-based API, but is easy to those who understand them and are familiar with this design pattern.
  • Using this pattern can complicate the process of instantiating objects, that's why this pattern is used alongside the Factory and Builder patterns

Implementation

public abstract class ComponentBase
{
    public abstract void Operation();
}

public class ConcreteComponent : ComponentBase
{
    public override void Operation()
    {
        // Implementation of component's behavior
    }
}

public abstract class DecoratorBase : ComponentBase
{
    private ComponentBase _componentObject; // The one to be extended

    public DecoratorBase(ComponentBase componentObject)
    {
       _componentObject = componentObject;
    }

    public override void Operation()
    {
       _componentObject.Operation();
    }
}

public class ConcreteDecorator : DecoratorBase
{
    public ConcreteDecorator(ComponentBase componentObject) : base(componentObject)
    { }

    public override void Operation()
    {
       // Implementation of new behavior
       base.Operation();
       // Implementation of new behavior
    }
}

Related Patterns (from GoF’s book)

  • Adapter : A decorator is different from an adapter in that a decorator only changes an object's responsibilities, not its interface; an adapter will give an object a completely new interface.
  • Composite : A decorator can be viewed as a degenerate composite with only one component. However, a decorator adds additional responsibilities—it isn't intended for object aggregation.
  • Strategy : A decorator lets you change the skin of an object; a strategy lets you change the guts. These are two alternative ways of changing an object.

Comments

Strategy Pattern

July 01, 2011


Pattern Name

Strategy Pattern

Classification

Behavioral Pattern

Intent

Define a family of algorithms/behaviors, encapsulate each one and make them interchangeable. The Strategy pattern lets the algorithm vary independently from the clients that use it.

Also known as

Policy pattern

Motivation

In some situations, we might find ourselves working with classes that only differ in their behavior. If we use inheritance, we’ll have to override the behavior every time we want to derive a class and want to change the algorithm, but when we want to remove special behaviors we would override them with empty methods because the algorithms are hardwired in the classes. Such rigidity in the implementation breaks an important OO principle, which is flexibility.

Also another approach would be providing an interface for each algorithm, this way only classes that should have this algorithm can implement it; thus, solving some of the first approach's shortcomings. But it will have its own disadvantages like duplicate code . Also, by applying this approach, we can kiss code reuse goodbye since we'll have to write the algorithm in each class. Consequently, the slightest change in the implementation will require us to go over all the classes and change them too. These shortcomings would create and ideal "Maintenance Nightmare".

We can conclude some of the restrictions that prevent us from using any of the previously mentioned approaches, include:

  • Clients vary in complexity; some can be very simple to the point they are have one behavior only, while others might be complicated to support multiple behaviors.

  • If the algorithms are hardwired in the clients, it will be hard to implement new ones or modify the ones that already exist.

The design principles behind the pattern

  • Identify the aspects of our application that might vary and is suspected to change in the future and separate (encapsulate) them from the rest of the code. This way when they are changed, the rest of the code won't be affected.
  • Program To an interface not to an implementation; the idea here is to exploit polymorphism by programming to a supertype so that the actual runtime object isn't hardwired into the code. This way, behaviors and clients are loosely coupled and clients can use behaviors interchangeably.

Applicability (from GoF's book)

Use the Strategy pattern when:

  • Many related classes differ only in their behavior. Strategies provide a way to configure a class with one of many behaviors.
  • You need different variants of an algorithm. For example, you might define algorithms reflecting different space/time trade-offs. Strategies can be used when these variants are implemented as a class hierarchy of algorithms [HO87].
  • An algorithm uses data that clients shouldn't know about. Use the Strategy pattern to avoid exposing complex, algorithm-specific data structures.
  • A class defines many behaviors, and these appear as multiple conditional statements in its operations. Instead of many conditionals, move related conditional branches into their own Strategy class.

Structure

Strategy_Pattern

Participants

  • IStrategy: The interface common to all algorithms that the client uses to deal with the Strategies (Concrete Algorithms). This interface represents the family of Strategies that share something in common. This interface can be implemented as an abstract class if it has functionality to provide to its children.
  • Context/Client: Uses the interchangeable algorithms through the IStrategy interface. It declares a property of the IStrategy type that holds one of the Strategies. This property can be set to the required algorithm in the runtime.
  • Strategy A / Strategy B: They implement the IStrategy interface and represent the different implementations of the algorithm.

Consequences

The strategy design pattern splits the behaviors of a class from the class itself, resulting in the following advantages:

  • The algorithms/behaviors produced are reusable by other clients since they are no longer hardwired in our classes.
  • Clients can chose from various implementations of the same behavior.
  • New algorithms/behaviors can be added easily without the need to modify any existing code.

Yet, the strategy design pattern has some draw backs (from GoF's book):

  • Clients must be aware of different Strategies. The pattern has a potential drawback in that a client must understand how Strategies differ before it can select the appropriate one. Clients might be exposed to implementation issues. Therefore you should use the Strategy pattern only when the variation in behavior is relevant to clients.
  • Communication overhead between Strategy and Context. The Strategy interface is shared by all ConcreteStrategy classes whether the algorithms they implement are trivial or complex. Hence it's likely that some ConcreteStrategies won't use all the information passed to them through this interface; simple ConcreteStrategies may use none of it! That means there will be times when the context creates and initializes parameters that never get used. If this is an issue, then you'll need tighter coupling between Strategy and Context.
  • Increased number of objects. Strategies increase the number of objects in an application.

Implementation

public interface IStrategy 
{ 
    string ExecuteAlgorithm(); 
} 

public class ConcreteStrategyA : IStrategy 
{ 
    public string ExecuteAlgorithm() 
    {
         return "This is Concrete Strategy A"; 
    } 
} 

public class ConcreteStrategyB : IStrategy 
{ 
    public string ExecuteAlgorithm(); 
    { 
        return "This is Concrete Strategy B"; 
    } 
} 

public class Client 
{ 
    public IStrategy Strategy { get; set; } 
    public void CallAlgorithm() 
    { 
        Console.WriteLine(Strategy.ExecuteAlgorithm()); 
    } 
}

Sample Code

We are writing a game that contains different types of terrains(e.g. Marshes, Dessert, City & Coasts) and different types of characters (e.g. Warrior, Mage & Rogue). We want every character to pass the terrain with a relative speed to their type and difficulty of the terrain.

We’ll refer to the terrains difficulty with integers to keep things simple. Also, we’ll add algorithms to calculate time taken which take in consideration the distance covered, terrain difficulty and character type.

Class Diagram

Strategy_Pattern_Example

Timer’s implementations

public interface ITimer 
{ 
    double CalculateTimeTaken(double distance, int terrainRate); 
} 

public class RogueTimer: ITimer 
{ 
    double CalculateTimeTaken(double distance, int terrainRate) 
    {
        double timeTaken; 
        //Implementation of Timer 
        return timeTaken; 
    } 
} 

public class MageTimer: ITimer 
{ 
    double CalculateTimeTaken(double distance, int terrainRate) 
    {
        double timeTaken; 
        //Implementation of Timer 
        return timeTaken; 
    } 
} 

public class WarriorTimer: ITimer 
{ 
    double CalculateTimeTaken(double distance, int terrainRate) 
    { 
        double timeTaken; 
        //Implementation of Timer 
        return timeTaken; 
    } 
}

Characters’ implementations

public abstract class Character 
{ 
    public ITimer Timer { get; set; } 
    private Health; public abstract double CalculateTimeTaken(double distance, int terrainRate); 
} 

public class Warrior : Character 
{ 
    private Stamina; 
    private Strength; 
    public Warrior() 
    {
        // Constructor code here 
    } 

    public override double CalculateTimeTaken(double distance, int terrainRate) 
    {
        return Timer.CalculateTimeTaken(distance, terrainRate); 
    } 
} 

public class Mage : Character 
{ 
    private Mana;     
    private WillPower; 
    public Mage() 
    {
         // Constructor code here 
    } 

    public override double CalculateTimeTaken(double distance, int terrainRate) 
    { 
        return Timer.CalculateTimeTaken(distance, terrainRate); 
    } 
} 

public class Rogue: Character 
{ 
    private Dexterity; 
    private Constitution; 
    public Rogue() 
    {
         // Constructor code here 
    } 

    public override double CalculateTimeTaken(double distance, int terrainRate) 
    { 
        return Timer.CalculateTimeTaken(distance, terrainRate); 
    } 
}

Testing Code

Warrior warrior = new Warrior(); 
Mage mage = new Mage(); 
Rogue rogue = new Rogue(); 

warrior.Timer = new WarriorTimer(); 
mage.Timer = new MageTimer(); 
rogue.Timer = new RogueTimer(); 

Console.Write("Warrior's Time: "); 
Console.WriteLine(warrior.CalculateTimeTaken(50, 3); 

Console.WriteLine("Mage's Time: "); 
Console.WriteLine(mage.CalculateTimeTaken(50, 3); 

Console.WriteLine("Rogue's Time: "); 
Console.WriteLine(rogue.CalculateTimeTaken(50, 3);

Related Patterns

Flyweight Pattern

Comments


What are Design Patterns?

“Each pattern describes a problem which occurs over and over in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice.”Christopher Alexander

So we can simply say, that some of the design related problems that we encounter while developing software are solved by someone else. These solutions allow us to solve our own problems and take advantage of other developer’s experience and wisdom.Design patterns are descriptions of communicating objects and classes that are customized to solve a general design problem in a particular context.

Important note: Design patterns are considered guidelines; they are powerful tools that aid developers, but they should never be regarded as rules/specifications that software should follow. That’s why it is important to understand these patterns well and know the proper situations where we could use them. Otherwise, the usage of incorrect patterns might result in unnecessary complications or maintainability issues. To avoid such inconveniences, we should always go with the simplest solution that does the job and introduce patterns only when needed.

What are Anti-Patterns?

In a way we could say that anti-patterns tell us how to go from a problem to a bad solution. Using anti-patterns in practice, leads to counterproductive results or ineffective solutions to the problems that might face us.

Describing Design Patterns

A pattern has four essential elements:

  • A unique pattern name to help us in identifying and referring to the pattern.
  • Problem scenario.
  • The proposed solution.
  • Consequences/tradeoffs of applying this pattern.

Documentation (Design Patterns Catalog)

The documentation for a design pattern describes the context, where the pattern is used, the forces within the context that the pattern seeks to resolve, and the suggested solution. There is no single, standard format for documenting design patterns. Rather, many different formats have been used by different pattern authors. One example of a commonly used documentation format is the one used by "Gang of Four" in their book Design Patterns

It is as follows:

  • Pattern Name and Classification: A descriptive and unique name that helps in identifying and referring to the pattern.
  • Intent: A description of the goal behind the pattern and the reason for using it.
  • Also Known As: Other names for the pattern.
  • Motivation (Forces): A scenario consisting of a problem and a context in which this pattern can be used.
  • Applicability: Situations in which this pattern is usable; the context for the pattern.
  • Structure: A graphical representation of the pattern. Class diagrams and Interaction diagrams may be used for this purpose.
  • Participants: A listing of the classes and objects used in the pattern and their roles in the design.
  • Collaboration: A description of how classes and objects used in the pattern interact with each other.
  • Consequences: A description of the results, side effects, and tradeoffs caused by using the pattern.
  • Implementation: A description of an implementation of the pattern; the solution part of the pattern.
  • Sample Code: An illustration of how the pattern can be used in a programming language.
  • Known Uses: Examples of real usages of the pattern.
  • Related Patterns: Other patterns that have some relationship with the pattern; discussion of the differences between the pattern and similar patterns.

Design Patterns' Classifications

  • Creational Patterns
  • Structural Patterns
  • Behavioral Patterns

Comments