Design patterns are typical solutions to common problems in software design. They are like blueprints that you can customize to solve a particular design problem in your code. Understanding design patterns is crucial for writing clean, maintainable, and scalable code.
Key Concepts
-
What are Design Patterns?
- Reusable solutions to common problems in software design.
- Not code, but templates for how to solve a problem in various situations.
- Help in improving code readability and reusability.
-
Types of Design Patterns:
- Creational Patterns: Deal with object creation mechanisms.
- Structural Patterns: Deal with object composition or structure.
- Behavioral Patterns: Deal with object interaction and responsibility.
Creational Patterns
Singleton Pattern
Ensures a class has only one instance and provides a global point of access to it.
Example:
public class Singleton
{
private static Singleton instance = null;
private static readonly object padlock = new object();
Singleton()
{
}
public static Singleton Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new Singleton();
}
return instance;
}
}
}
}Explanation:
- The
Singletonclass has a private static variableinstancethat holds the single instance of the class. - The constructor is private to prevent instantiation from outside the class.
- The
Instanceproperty checks if the instance is null and creates it if necessary, ensuring thread safety with a lock.
Factory Method Pattern
Defines an interface for creating an object but lets subclasses alter the type of objects that will be created.
Example:
public abstract class Product
{
public abstract string GetName();
}
public class ConcreteProductA : Product
{
public override string GetName() => "Product A";
}
public class ConcreteProductB : Product
{
public override string GetName() => "Product B";
}
public abstract class Creator
{
public abstract Product FactoryMethod();
}
public class ConcreteCreatorA : Creator
{
public override Product FactoryMethod() => new ConcreteProductA();
}
public class ConcreteCreatorB : Creator
{
public override Product FactoryMethod() => new ConcreteProductB();
}Explanation:
Productis an abstract class with a methodGetName.ConcreteProductAandConcreteProductBare concrete implementations ofProduct.Creatoris an abstract class with a methodFactoryMethod.ConcreteCreatorAandConcreteCreatorBimplementFactoryMethodto return instances ofConcreteProductAandConcreteProductB, respectively.
Structural Patterns
Adapter Pattern
Allows incompatible interfaces to work together by converting the interface of a class into another interface that a client expects.
Example:
public interface ITarget
{
void Request();
}
public class Adaptee
{
public void SpecificRequest()
{
Console.WriteLine("Called SpecificRequest()");
}
}
public class Adapter : ITarget
{
private readonly Adaptee _adaptee;
public Adapter(Adaptee adaptee)
{
_adaptee = adaptee;
}
public void Request()
{
_adaptee.SpecificRequest();
}
}Explanation:
ITargetdefines the domain-specific interface.Adapteehas an existing interface that needs adapting.AdapterimplementsITargetand translates theRequestcall toSpecificRequest.
Behavioral Patterns
Observer Pattern
Defines a one-to-many dependency between objects so that when one object changes state, all its dependents are notified and updated automatically.
Example:
public interface IObserver
{
void Update();
}
public class ConcreteObserver : IObserver
{
public void Update()
{
Console.WriteLine("Observer has been updated.");
}
}
public class Subject
{
private List<IObserver> observers = new List<IObserver>();
public void Attach(IObserver observer)
{
observers.Add(observer);
}
public void Detach(IObserver observer)
{
observers.Remove(observer);
}
public void Notify()
{
foreach (var observer in observers)
{
observer.Update();
}
}
}Explanation:
IObserverdefines an interface for objects that should be notified of changes.ConcreteObserverimplementsIObserverand defines theUpdatemethod.Subjectmaintains a list of observers and notifies them of any state changes.
Practical Exercise
Exercise: Implementing the Singleton Pattern
Task: Create a Singleton class that manages a configuration setting for an application.
Solution:
public class ConfigurationManager
{
private static ConfigurationManager instance = null;
private static readonly object padlock = new object();
public string ConfigurationSetting { get; set; }
ConfigurationManager()
{
ConfigurationSetting = "Default Setting";
}
public static ConfigurationManager Instance
{
get
{
lock (padlock)
{
if (instance == null)
{
instance = new ConfigurationManager();
}
return instance;
}
}
}
}Explanation:
ConfigurationManageris a Singleton class with a private constructor.- It has a property
ConfigurationSettingto store a configuration setting. - The
Instanceproperty ensures only one instance ofConfigurationManageris created.
Summary
In this section, we covered the basics of design patterns, including their importance and different types. We explored examples of creational, structural, and behavioral patterns, and provided a practical exercise to implement the Singleton pattern. Understanding and applying design patterns will help you write more efficient, maintainable, and scalable code.
C# Programming Course
Module 1: Introduction to C#
- Introduction to C#
- Setting Up the Development Environment
- Hello World Program
- Basic Syntax and Structure
- Variables and Data Types
Module 2: Control Structures
Module 3: Object-Oriented Programming
- Classes and Objects
- Methods
- Constructors and Destructors
- Inheritance
- Polymorphism
- Encapsulation
- Abstraction
Module 4: Advanced C# Concepts
- Interfaces
- Delegates and Events
- Generics
- Collections
- LINQ (Language Integrated Query)
- Asynchronous Programming
Module 5: Working with Data
Module 6: Advanced Topics
- Reflection
- Attributes
- Dynamic Programming
- Memory Management and Garbage Collection
- Multithreading and Parallel Programming
