Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Structural Patterns

Structural patterns tập trung vào cách kết hợp objects và classes để tạo thành cấu trúc lớn hơn, đảm bảo tính linh hoạt và hiệu quả trong thiết kế hệ thống.

1. Adapter Pattern

Mục đích: Cho phép interface của một class không tương thích hoạt động với interface khác.

C# Implementation

// Target interface
public interface IMediaPlayer
{
    void Play(string filename);
}

// Adaptee - existing class with incompatible interface
public class AdvancedMediaPlayer
{
    public void PlayMp4(string filename)
    {
        Console.WriteLine($"Playing MP4: {filename}");
    }
    
    public void PlayVlc(string filename)
    {
        Console.WriteLine($"Playing VLC: {filename}");
    }
}

// Adapter
public class MediaAdapter : IMediaPlayer
{
    private readonly AdvancedMediaPlayer _advancedPlayer;
    
    public MediaAdapter()
    {
        _advancedPlayer = new AdvancedMediaPlayer();
    }
    
    public void Play(string filename)
    {
        if (filename.EndsWith(".mp4"))
        {
            _advancedPlayer.PlayMp4(filename);
        }
        else if (filename.EndsWith(".vlc"))
        {
            _advancedPlayer.PlayVlc(filename);
        }
    }
}

Use Cases

  • Integrating legacy systems với modern code
  • Working with third-party libraries
  • Converting between different data formats

2. Bridge Pattern

Mục đích: Tách abstraction khỏi implementation để cả hai có thể thay đổi độc lập.

C# Implementation

// Implementation interface
public interface IRenderer
{
    void RenderCircle(float radius);
    void RenderSquare(float side);
}

// Concrete implementations
public class VectorRenderer : IRenderer
{
    public void RenderCircle(float radius) 
        => Console.WriteLine($"Drawing circle as vectors with radius {radius}");
    public void RenderSquare(float side) 
        => Console.WriteLine($"Drawing square as vectors with side {side}");
}

public class RasterRenderer : IRenderer
{
    public void RenderCircle(float radius) 
        => Console.WriteLine($"Drawing circle as pixels with radius {radius}");
    public void RenderSquare(float side) 
        => Console.WriteLine($"Drawing square as pixels with side {side}");
}

// Abstraction
public abstract class Shape
{
    protected IRenderer Renderer;
    
    protected Shape(IRenderer renderer) => Renderer = renderer;
    
    public abstract void Draw();
    public abstract void Resize(float factor);
}

public class Circle : Shape
{
    private float _radius;
    
    public Circle(IRenderer renderer, float radius) : base(renderer)
    {
        _radius = radius;
    }
    
    public override void Draw() => Renderer.RenderCircle(_radius);
    public override void Resize(float factor) => _radius *= factor;
}

Use Cases

  • Cross-platform applications
  • Multiple UI frameworks
  • Database drivers

3. Composite Pattern

Mục đích: Compose objects into tree structures để represent part-whole hierarchies.

C# Implementation

public interface IComponent
{
    void Execute();
    int GetPrice();
}

public class Leaf : IComponent
{
    private readonly int _price;
    private readonly string _name;
    
    public Leaf(string name, int price)
    {
        _name = name;
        _price = price;
    }
    
    public void Execute() => Console.WriteLine($"Leaf: {_name}");
    public int GetPrice() => _price;
}

public class Composite : IComponent
{
    private readonly List<IComponent> _children = new();
    
    public void Add(IComponent component) => _children.Add(component);
    public void Remove(IComponent component) => _children.Remove(component);
    
    public void Execute()
    {
        foreach (var child in _children)
            child.Execute();
    }
    
    public int GetPrice() => _children.Sum(c => c.GetPrice());
}

Use Cases

  • File systems
  • UI hierarchies
  • Organization structures

4. Decorator Pattern

Mục đích: Thêm behavior vào objects dynamically mà không thay đổi class gốc.

C# Implementation

public interface ICoffee
{
    string GetDescription();
    decimal GetCost();
}

public class SimpleCoffee : ICoffee
{
    public string GetDescription() => "Simple Coffee";
    public decimal GetCost() => 2.00m;
}

// Base decorator
public abstract class CoffeeDecorator : ICoffee
{
    protected ICoffee _coffee;
    
    public CoffeeDecorator(ICoffee coffee) => _coffee = coffee;
    
    public virtual string GetDescription() => _coffee.GetDescription();
    public virtual decimal GetCost() => _coffee.GetCost();
}

public class MilkDecorator : CoffeeDecorator
{
    public MilkDecorator(ICoffee coffee) : base(coffee) { }
    
    public override string GetDescription() 
        => _coffee.GetDescription() + ", Milk";
    
    public override decimal GetCost() => _coffee.GetCost() + 0.50m;
}

public class SugarDecorator : CoffeeDecorator
{
    public SugarDecorator(ICoffee coffee) : base(coffee) { }
    
    public override string GetDescription() 
        => _coffee.GetDescription() + ", Sugar";
    
    public override decimal GetCost() => _coffee.GetCost() + 0.25m;
}

// Usage
ICoffee coffee = new SimpleCoffee();
coffee = new MilkDecorator(coffee);
coffee = new SugarDecorator(coffee);

Use Cases

  • Adding features to streams (buffering, compression)
  • UI components với borders, scrollbars
  • Logging, caching decorators

5. Facade Pattern

Mục đích: Cung cấp simplified interface cho complex subsystem.

C# Implementation

public class VideoFile
{
    public string Filename { get; }
    public string CodecType { get; }
    
    public VideoFile(string filename)
    {
        Filename = filename;
        CodecType = filename.EndsWith(".mp4") ? "mp4" : "ogg";
    }
}

public class CodecFactory
{
    public static string Extract(VideoFile file) => file.CodecType;
}

public class BitrateReader
{
    public static string Read(string codec) => "video data";
}

public class AudioMixer
{
    public string Fix(string data) => "fixed audio";
}

public class VideoConverter
{
    public string Convert(string filename, string format)
    {
        var file = new VideoFile(filename);
        var codec = CodecFactory.Extract(file);
        var data = BitrateReader.Read(codec);
        var audio = new AudioMixer().Fix(data);
        
        return "converted file";
    }
}

Use Cases

  • Simplifying library APIs
  • Hiding complexity của third-party systems
  • Providing unified interface to multiple services

6. Flyweight Pattern

Mục đích: Chia sẻ objects để tiết kiệm memory, đặc biệt khi có nhiều objects giống nhau.

C# Implementation

public class TreeType
{
    public string Name { get; }
    public string Color { get; }
    public string Texture { get; }
    
    public TreeType(string name, string color, string texture)
    {
        Name = name;
        Color = color;
        Texture = texture;
    }
    
    public void Draw(int x, int y)
    {
        Console.DrawTree(x, y, Color, Texture);
    }
}

public class TreeFactory
{
    private static readonly Dictionary<string, TreeType> _cache = new();
    
    public static TreeType GetTreeType(string name, string color, string texture)
    {
        var key = $"{name}_{color}_{texture}";
        
        if (!_cache.ContainsKey(key))
        {
            _cache[key] = new TreeType(name, color, texture);
        }
        
        return _cache[key];
    }
}

public class Tree
{
    private readonly int _x, _y;
    private readonly TreeType _type;
    
    public Tree(int x, int y, TreeType type)
    {
        _x = x;
        _y = y;
        _type = type;
    }
    
    public void Draw() => _type.Draw(_x, _y);
}

Use Cases

  • Game development (trees, particles)
  • Caching shared resources
  • Reducing memory usage với large number of similar objects

7. Proxy Pattern

Mục đích: Cung cấp placeholder cho another object để control access to it.

C# Implementation

public interface IImage
{
    void Display();
}

public class RealImage : IImage
{
    private readonly string _filename;
    
    public RealImage(string filename)
    {
        _filename = filename;
        LoadFromDisk();
    }
    
    private void LoadFromDisk()
    {
        Console.WriteLine($"Loading image: {_filename}");
    }
    
    public void Display()
    {
        Console.WriteLine($"Displaying image: {_filename}");
    }
}

public class ProxyImage : IImage
{
    private readonly string _filename;
    private RealImage _realImage;
    
    public ProxyImage(string filename)
    {
        _filename = filename;
    }
    
    public void Display()
    {
        if (_realImage == null)
        {
            _realImage = new RealImage(_filename);
        }
        _realImage.Display();
    }
}

Use Cases

  • Lazy loading
  • Access control
  • Logging và monitoring
  • Caching

Comparison

PatternPurposeUse Case
AdapterConvert interfaceLegacy integration
BridgeSeparate abstraction/implementationCross-platform
CompositeTree structureUI, file systems
DecoratorAdd behavior dynamicallyFeature extension
FacadeSimplify interfaceComplex subsystems
FlyweightShare objectsMemory optimization
ProxyControl accessLazy loading, security

Best Practices

  1. Adapter: Use when integrating incompatible interfaces
  2. Bridge: Use when you have multiple platforms/formats
  3. Composite: Use when representing hierarchical structures
  4. Decorator: Use when you need flexible behavior extension
  5. Facade: Use to simplify complex systems
  6. Flyweight: Use when you have many similar objects
  7. Proxy: Use for lazy loading và access control