Sitemap
Chetan Badgujar

Chetan Badgujar

Technology evangelist and coding enthusiast. Exploring the basic coding concepts and sharing insights on the future of programming.

Design Patterns -Bridge, Facade &Flyweight

5 min readApr 14, 2023

--

Press enter or click to view image in full size

Bridge Design Pattern

The Bridge pattern is a structural design pattern that decouples an abstraction from its implementation, allowing them to vary independently. It is useful when you have multiple implementations of a class, and you want to switch between them dynamically without changing the client code.

In C#, the Bridge pattern can be implemented using interfaces or abstract classes. Let’s take an example of a drawing application that can draw different shapes such as circles, squares, and rectangles. We’ll use the Bridge pattern to separate the abstraction (the shape) from its implementation (the drawing logic).

First, we’ll define the abstraction, which is the shape. We’ll create an interface called IShape that will define the common methods for all shapes:

public interface IShape
{
void Draw();
void Resize(int width, int height);
}

Next, we’ll define the implementation, which is the drawing logic. We’ll create an abstract class called DrawingAPI that will define the methods for drawing shapes using a specific implementation:

public abstract class DrawingAPI
{
public abstract void DrawCircle(int x, int y, int radius);
public abstract void DrawRectangle(int x, int y, int width, int height);
public abstract void DrawSquare(int x, int y, int side);
}

Now, we can create concrete implementations of the DrawingAPI class for different drawing engines, such as DrawingAPI1 and DrawingAPI2, which will provide the actual drawing logic.

public class DrawingAPI1 : DrawingAPI
{
public override void DrawCircle(int x, int y, int radius)
{
Console.WriteLine($"DrawingAPI1: Drawing a circle at ({x}, {y}) with radius {radius}");
}

// Implementations for drawing rectangle and square
}

public class DrawingAPI2 : DrawingAPI
{
public override void DrawCircle(int x, int y, int radius)
{
Console.WriteLine($"DrawingAPI2: Drawing a circle at ({x}, {y}) with radius {radius}");
}

// Implementations for drawing rectangle and square
}

Finally, we can create the concrete shape classes, such as Circle, Rectangle, and Square, that will use the appropriate DrawingAPI implementation to draw themselves:

public class Circle : IShape
{
private DrawingAPI drawingAPI;

public Circle(DrawingAPI drawingAPI)
{
this.drawingAPI = drawingAPI;
}

public void Draw()
{
drawingAPI.DrawCircle(0, 0, 5);
}

public void Resize(int width, int height)
{
// Implement resize logic
}
}

// Implementations for Rectangle and Square classes

Now, the client code can create different shapes and pass the appropriate DrawingAPI implementation to them, allowing them to be drawn using different drawing engines dynamically, without changing the client code:

// Client code
var drawingAPI1 = new DrawingAPI1();
var drawingAPI2 = new DrawingAPI2();

var circle1 = new Circle(drawingAPI1);
circle1.Draw(); // Output: DrawingAPI1: Drawing a circle at (0, 0) with radius 5

var circle2 = new Circle(drawingAPI2);
circle2.Draw(); // Output: DrawingAPI2: Drawing a circle at (0, 0) with radius 5

// Similar code for drawing rectangles and squares with different drawing engines

That’s a simple example of how you can use the Bridge pattern in C# to decouple an abstraction from its implementation, allowing them to vary independently.

Facade Design Pattern

The facade design pattern is a structural design pattern that provides a simplified interface to a complex system of objects. It allows clients to interact with a high-level interface that hides the complexity of the underlying subsystems. The facade pattern promotes loose coupling between clients and subsystems, making it easier to modify or replace subsystems without affecting the clients.

Get Chetan Badgujar’s stories in your inbox

Join Medium for free to get updates from this writer.

Here’s an example of the facade design pattern implemented in C#:

using System;

// Subsystem 1: VideoPlayer
class VideoPlayer
{
public void Play()
{
Console.WriteLine("Playing video");
}

public void Pause()
{
Console.WriteLine("Pausing video");
}

public void Stop()
{
Console.WriteLine("Stopping video");
}
}

// Subsystem 2: AudioPlayer
class AudioPlayer
{
public void Play()
{
Console.WriteLine("Playing audio");
}

public void Pause()
{
Console.WriteLine("Pausing audio");
}

public void Stop()
{
Console.WriteLine("Stopping audio");
}
}

// Subsystem 3: Projector
class Projector
{
public void TurnOn()
{
Console.WriteLine("Turning on projector");
}

public void TurnOff()
{
Console.WriteLine("Turning off projector");
}
}

// Facade: MultimediaFacade
class MultimediaFacade
{
private VideoPlayer videoPlayer;
private AudioPlayer audioPlayer;
private Projector projector;

public MultimediaFacade()
{
videoPlayer = new VideoPlayer();
audioPlayer = new AudioPlayer();
projector = new Projector();
}

// Facade methods that provide a simplified interface to the complex subsystems
public void Play()
{
videoPlayer.Play();
audioPlayer.Play();
projector.TurnOn();
}

public void Pause()
{
videoPlayer.Pause();
audioPlayer.Pause();
}

public void Stop()
{
videoPlayer.Stop();
audioPlayer.Stop();
projector.TurnOff();
}
}

class Program
{
static void Main(string[] args)
{
// Client code
MultimediaFacade facade = new MultimediaFacade();
facade.Play();

Console.WriteLine("-----");

facade.Pause();

Console.WriteLine("-----");

facade.Stop();

Console.ReadKey();
}
}

In this example, the MultimediaFacade class acts as a facade that provides a simplified interface to the complex subsystems of VideoPlayer, AudioPlayer, and Projector. The clients can interact with the MultimediaFacade to play, pause, and stop multimedia playback, and the facade internally coordinates the actions of the subsystems to achieve the desired functionality. The facade pattern helps to abstract the complexity of the subsystems and provides a clean and simplified interface for clients to interact with.

Flyweight Design Pattern

The Flyweight design pattern is a structural design pattern in object-oriented programming that is used to minimize memory usage by sharing objects with similar data rather than creating multiple instances of the same data. This pattern is particularly useful when you need to create a large number of objects that share common properties, and you want to reduce the memory footprint of your application.

In C#, the Flyweight design pattern can be implemented using a combination of classes and interfaces. Here’s an example of how you can implement the Flyweight pattern in C#:

// Flyweight interface that defines the common properties shared by objects
public interface IFlyweight
{
void Operation();
}

// Concrete Flyweight class that implements the IFlyweight interface
public class ConcreteFlyweight : IFlyweight
{
private string sharedData;

public ConcreteFlyweight(string sharedData)
{
this.sharedData = sharedData;
}

public void Operation()
{
// Perform operation using sharedData
Console.WriteLine($"Operation with shared data: {sharedData}");
}
}

// FlyweightFactory class that manages the creation and sharing of flyweight objects
public class FlyweightFactory
{
private Dictionary<string, IFlyweight> flyweights = new Dictionary<string, IFlyweight>();

public IFlyweight GetFlyweight(string key)
{
if (!flyweights.ContainsKey(key))
{
// Create and store a new flyweight object if it doesn't exist
flyweights[key] = new ConcreteFlyweight(key);
}

// Return the existing or newly created flyweight object
return flyweights[key];
}
}

// Client class that uses flyweight objects
public class Client
{
private FlyweightFactory factory = new FlyweightFactory();

public void Operation(string key)
{
IFlyweight flyweight = factory.GetFlyweight(key);
flyweight.Operation();
}
}

In this example, the IFlyweight interface defines the common properties shared by flyweight objects, and the ConcreteFlyweight class implements this interface with a sharedData field. The FlyweightFactory class manages the creation and sharing of flyweight objects using a dictionary to store and retrieve flyweights based on a unique key. The Client class uses the FlyweightFactory to request flyweight objects and perform operations on them.

By using the Flyweight design pattern, you can reduce the memory usage of your application by sharing common data among multiple objects, instead of duplicating the data in each object. This can be especially beneficial when dealing with large numbers of objects with similar properties, such as in graphical applications where many objects share the same graphical resources.

here we have finished structural patterns ….. stay tuned for behavioural pattern…

--

--

Chetan Badgujar
Chetan Badgujar

Technology evangelist and coding enthusiast. Exploring the basic coding concepts and sharing insights on the future of programming.