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
Singleton
class has a private static variableinstance
that holds the single instance of the class. - The constructor is private to prevent instantiation from outside the class.
- The
Instance
property 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:
Product
is an abstract class with a methodGetName
.ConcreteProductA
andConcreteProductB
are concrete implementations ofProduct
.Creator
is an abstract class with a methodFactoryMethod
.ConcreteCreatorA
andConcreteCreatorB
implementFactoryMethod
to return instances ofConcreteProductA
andConcreteProductB
, 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:
ITarget
defines the domain-specific interface.Adaptee
has an existing interface that needs adapting.Adapter
implementsITarget
and translates theRequest
call 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:
IObserver
defines an interface for objects that should be notified of changes.ConcreteObserver
implementsIObserver
and defines theUpdate
method.Subject
maintains 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:
ConfigurationManager
is a Singleton class with a private constructor.- It has a property
ConfigurationSetting
to store a configuration setting. - The
Instance
property ensures only one instance ofConfigurationManager
is 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