Understanding Structural Design Patterns in C
Structural design patterns in C# are a set of software design patterns that focus on the composition of classes and objects to create larger structures and provide new functionality. These patterns help in building flexible and reusable software components.
In this article, we will explore some common structural design patterns in C# and discuss their implementation and usage.
1. Adapter Pattern
The Adapter pattern allows incompatible interfaces to work together by creating a common interface that both can work with. This pattern is useful when you want to use an existing class with an incompatible interface or when you want to hide the complexities of a subsystem.
Example:
// Existing class with incompatible interface
public class LegacyRectangle
{
public void Draw(int x, int y, int width, int height)
{
// Draw legacy rectangle
}
}
// Adapter class
public class RectangleAdapter : IShape
{
private LegacyRectangle _legacyRectangle;
public RectangleAdapter(LegacyRectangle legacyRectangle)
{
_legacyRectangle = legacyRectangle;
}
public void Draw()
{
// Convert new interface to the legacy interface
_legacyRectangle.Draw(0, 0, 100, 50);
}
}
2. Decorator Pattern
The Decorator pattern allows you to add additional behavior to an object dynamically. It provides a flexible alternative to subclassing for extending functionality.
Example:
// Component interface
public interface ICar
{
string Assemble();
}
// Concrete component
public class BasicCar : ICar
{
public string Assemble()
{
return "Basic car assembled.";
}
}
// Decorator base class
public abstract class CarDecorator : ICar
{
protected ICar _car;
public CarDecorator(ICar car)
{
_car = car;
}
public virtual string Assemble()
{
return _car.Assemble();
}
}
// Concrete decorators
public class LuxuryCar : CarDecorator
{
public LuxuryCar(ICar car) : base(car)
{
}
public override string Assemble()
{
return base.Assemble() + " Luxury features added.";
}
}
public class SportsCar : CarDecorator
{
public SportsCar(ICar car) : base(car)
{
}
public override string Assemble()
{
return base.Assemble() + " Sports features added.";
}
}
3. Proxy Pattern
The Proxy pattern provides a surrogate or placeholder for another object to control access to it. It can be used to add additional functionality, such as lazy loading or access control, without altering the original object’s interface.
Example:
// Subject interface
public interface IImage
{
void Display();
}
// Real subject
public class RealImage : IImage
{
private string _filename;
public RealImage(string filename)
{
_filename = filename;
LoadImageFromDisk();
}
private void LoadImageFromDisk()
{
// Load image from disk
}
public void Display()
{
// Display image
}
}
// Proxy class
public class ImageProxy : IImage
{
private string _filename;
private RealImage _realImage;
public ImageProxy(string filename)
{
_filename = filename;
}
public void Display()
{
if (_realImage == null)
{
_realImage = new RealImage(_filename);
}
_realImage.Display();
}
}
These are just a few examples of structural design patterns in C#. By understanding and implementing these patterns, you can improve the organization, flexibility, and reusability of your code.
Remember to consider the specific requirements of your project before implementing any design pattern. Each pattern has its strengths and weaknesses, and choosing the right pattern for the job is crucial.
Happy coding!