Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
CSharpNotesForProfessionals.pdf
Скачиваний:
57
Добавлен:
20.05.2023
Размер:
6.12 Mб
Скачать

Chapter 134: Creational Design Patterns

Section 134.1: Singleton Pattern

The Singleton pattern is designed to restrict creation of a class to exactly one single instance.

This pattern is used in a scenario where it makes sense to have only one of something, such as:

a single class that orchestrates other objects' interactions, ex. Manager class or one class that represents a unique, single resource, ex. Logging component

One of the most common ways to implement the Singleton pattern is via a static factory method such as a CreateInstance() or GetInstance() (or a static property in C#, Instance), which is then designed to always return the same instance.

The first call to the method or property creates and returns the Singleton instance. Thereafter, the method always returns the same instance. This way, there is only ever one instance of the singleton object.

Preventing creation of instances via new can be accomplished by making the class constructor(s) private.

Here is a typical code example for implementing a Singleton pattern in C#:

class Singleton

{

//Because the _instance member is made private, the only way to get the single

//instance is via the static Instance property below. This can also be similarly

//achieved with a GetInstance() method instead of the property.

private static Singleton _instance = null;

//Making the constructor private prevents other instances from being

//created via something like Singleton s = new Singleton(), protecting

//against unintentional misuse.

private Singleton()

{

}

public static Singleton Instance

{

get

{

//The first call will create the one and only instance. if (_instance == null)

{

_instance = new Singleton();

}

//Every call afterwards will return the single instance created above. return _instance;

}

}

}

To illustrate this pattern further, the code below checks whether an identical instance of the Singleton is returned when the Instance property is called more than once.

class Program

{

GoalKicker.com – C# Notes for Professionals

663

static void Main(string[] args)

{

Singleton s1 = Singleton.Instance; Singleton s2 = Singleton.Instance;

// Both Singleton objects above should now reference the same Singleton instance. if (Object.ReferenceEquals(s1, s2))

{

Console.WriteLine("Singleton is working");

}

else

{

//Otherwise, the Singleton Instance property is returning something

//other than the unique, single instance when called.

Console.WriteLine("Singleton is broken");

}

}

}

Note: this implementation is not thread safe.

To see more examples, including how to make this thread-safe, visit: Singleton Implementation

Singletons are conceptually similar to a global value, and cause similar design flaws and concerns. Because of this, the Singleton pattern is widely regarded as an anti-pattern.

Visit "What is so bad about Singletons?" for more information on the problems that arise with their use.

In C#, you have the ability to make a class static, which makes all members static, and the class cannot be instantiated. Given this, it is common to see static classes used in place of the Singleton pattern.

For key di erences between the two, visit C# Singleton Pattern Versus Static Class.

Section 134.2: Factory Method pattern

Factory Method is one of creational design patterns. It is used to deal with the problem of creating objects without specifying exact result type. This document will teach you how to use Factory Method DP properly.

Let me explain the idea of it to you on a simple example. Imagine you're working in a factory that produces three types of devices - Ammeter, Voltmeter and resistance meter. You are writing a program for a central computer that will create selected device, but you don't know final decision of your boss on what to produce.

Let's create an interface IDevice with some common functions that all devices have:

public interface IDevice

{

int Measure(); void TurnOff(); void TurnOn();

}

Now, we can create classes that represent our devices. Those classes must implement IDevice interface:

public class AmMeter : IDevice

{

private Random r = null; public AmMeter()

{

GoalKicker.com – C# Notes for Professionals

664

r = new Random();

}

public int Measure() { return r.Next(-25, 60); }

public void TurnOff() { Console.WriteLine("AmMeter flashes lights saying good bye!"); } public void TurnOn() { Console.WriteLine("AmMeter turns on..."); }

}

public class OhmMeter : IDevice

{

private Random r = null; public OhmMeter()

{

r = new Random();

}

public int Measure() { return r.Next(0, 1000000); }

public void TurnOff() { Console.WriteLine("OhmMeter flashes lights saying good bye!"); } public void TurnOn() { Console.WriteLine("OhmMeter turns on..."); }

}

public class VoltMeter : IDevice

{

private Random r = null; public VoltMeter()

{

r = new Random();

}

public int Measure() { return r.Next(-230, 230); }

public void TurnOff() { Console.WriteLine("VoltMeter flashes lights saying good bye!"); } public void TurnOn() { Console.WriteLine("VoltMeter turns on..."); }

}

Now we have to define factory method. Let's create DeviceFactory class with static method inside:

public enum Device

{

AM,

VOLT,

OHM

}

public class DeviceFactory

{

public static IDevice CreateDevice(Device d)

{

switch(d)

{

case Device.AM: return new AmMeter(); case Device.VOLT: return new VoltMeter(); case Device.OHM: return new OhmMeter(); default: return new AmMeter();

}

}

}

Great! Let's test our code:

public class Program

{

static void Main(string[] args)

{

IDevice device = DeviceFactory.CreateDevice(Device.AM); device.TurnOn();

Console.WriteLine(device.Measure());

Console.WriteLine(device.Measure());

GoalKicker.com – C# Notes for Professionals

665

Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); device.TurnOff(); Console.WriteLine();

device = DeviceFactory.CreateDevice(Device.VOLT); device.TurnOn(); Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); device.TurnOff();

Console.WriteLine();

device = DeviceFactory.CreateDevice(Device.OHM); device.TurnOn(); Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); Console.WriteLine(device.Measure()); device.TurnOff();

Console.WriteLine();

}

}

This is the example output you might see after running this code:

 

AmMeter turns on...

 

36

 

6

 

33

 

43

 

24

 

AmMeter flashes lights saying good bye!

 

VoltMeter turns on...

 

102

 

-61

 

85

 

138

 

36

 

VoltMeter flashes lights saying good bye!

 

OhmMeter turns on...

 

GoalKicker.com – C# Notes for Professionals

666