Summary: in this tutorial, you’ll learn how to use the C# Facade pattern to make a simple and clean interface to a larger and more complex subsystem.
Introduction to the C# Facade pattern
The Facade pattern is a structural design pattern that allows you to create a simplified interface to a complex subsystem.
The Facade pattern exposes only the necessary functionality of the underlying subsystem while hiding the implementation details and complexities of the subsystem.
The following diagram illustrates the Facade pattern:
In this diagram, the client classes interact with a simple interface (Facade) instead of working directly with subsystem classes. This makes the system more flexible so that when the subsystem classes change, you don’t need to modify the client classes.
To use the Facade pattern in practice, you create a new class or set of classes that serve as an interface to the existing subsystem.
This new interface provides a simplified set of methods that the client classes use to access the functionality of the subsystem. The client classes don’t need to know the implementation details or interact directly with the subsystem’s classes.
C# Facade pattern example
The following program returns the current temperature in Celsius based on a city. It uses three classes GeoLookupService
, WeatherService
, and ConverterService
that provide geolocation lookup, weather, and converter services:
namespace FacacdePattern;
public class Location
{
public double Latitude
{
get; set;
}
public double Longitude
{
get; set;
}
public Location(double latitude, double longitude)
{
Latitude = latitude;
Longitude = longitude;
}
}
public class GeoLookupService
{
public Location GetLocationByCity(string city)
{
return new Location(15, 20);
}
}
public class WeatherService
{
public double GetCurrentTemperature(Location location) => new Random().Next(32, 115);
}
public class ConverterService
{
public double ConvertFToC(double f) => (f - 32) * 5 / 9;
}
public class Program
{
public static void Main(string[] args)
{
var geoLookupService = new GeoLookupService();
var location = geoLookupService.GetLocationByCity("San Jose");
var weatherService = new WeatherService();
var temperature = weatherService.GetCurrentTemperature(location);
var converterService = new ConverterService();
var temperatureInC = converterService.ConvertFToC(temperature);
Console.WriteLine($"The current temperature is {temperatureInC:F1}°C");
}
}
Code language: C# (cs)
Output:
The current temperature is 30.0°C
Code language: C# (cs)
How it works.
First, define the Location
class that represents a geographical location with a latitude and longitude:
public class Location
{
public double Latitude
{
get; set;
}
public double Longitude
{
get; set;
}
public Location(double latitude, double longitude)
{
Latitude = latitude;
Longitude = longitude;
}
}
Code language: C# (cs)
Second, define the GeoLookupService
class with a method GetLocationByCity()
that returns a Location object for a given city:
public class GeoLookupService
{
public Location GetLocationByCity(string city)
{
return new Location(15, 20);
}
}
Code language: C# (cs)
Third, define the WeatherService
class that returns the current temperature in Fahrenheit of a location:
public class WeatherService
{
public double GetCurrentTemperature(Location location) => new Random().Next(32, 115);
}
Code language: C# (cs)
The GetCurrentTemperature()
method returns a random temperature in Fahrenheit between 32 and 115. In practice, you may call an API to get the current temperature based on the current location of the user.
Fourth, define the ConverterService
class that converts Fahrenheit to Celsius:
public class ConverterService
{
public double ConvertFToC(double f) => (f - 32) * 5 / 9;
}
Code language: C# (cs)
Finally, use the above classes in the Client class to display the current temperature in Celsius to the console by using the above service classes:
public class Client
{
public static void Main(string[] args)
{
var geoLookupService = new GeoLookupService();
var location = geoLookupService.GetLocationByCity("San Jose");
var weatherService = new WeatherService();
var temperature = weatherService.GetCurrentTemperature(location);
var converterService = new ConverterService();
var temperatureInC = converterService.ConvertFToC(temperature);
Console.WriteLine($"The current temperature is {temperatureInC:F1}°C");
}
}
Code language: C# (cs)
The program works fine except that it depends on many services of the subsystem. If one of the services changes, you need to modify the Client class accordingly. To make the Client class more flexible, you can apply the Facade pattern.
Using the Facade pattern
First, define an interface IWeatherServiceFacade
:
public interface IWeatherServiceFacade
{
double GetCurrentTemperatureByCity(string city);
}
Code language: C# (cs)
The IWeatherServiceFacade
interface has one method GetCurrentTemperatureByCity()
that returns the current temperature in Celsius of a city.
Second, define the WeatherServiceFacade
class that implements the IWeatherServiceFacade
interface:
public class WeatherServiceFacade : IWeatherServiceFacade
{
private readonly GeoLookupService _geoLookupService;
private readonly WeatherService _weatherService;
private readonly ConverterService _converterService;
public WeatherServiceFacade()
{
_geoLookupService = new GeoLookupService();
_weatherService = new WeatherService();
_converterService = new ConverterService();
}
public double GetCurrentTemperatureByCity(string city)
{
var location = _geoLookupService.GetLocationByCity(city);
var temperature = _weatherService.GetCurrentTemperature(location);
return _converterService.ConvertFToC(temperature);
}
}
Code language: C# (cs)
The GetCurrentTemperatureByCity()
method of the
first uses the WeatherService
FacadeGeoLookupService
object to get the location of a city. Then, it uses the WeatherService
object to get the current temperature in Fahrenheit by location. And finally, it uses the UnitConvertService
to convert the temperature from Fahrenheit to Celsius.
Now, the Client
class needs to use the IWeatherServiceFacade
object to get the current temperature of a city by calling the GetCurrentTemperatureByCity()
method. It doesn’t need to know the underlying services:
public class Client
{
public static void Main(string[] args)
{
var weatherServiceFacade = new WeatherServiceFacade();
var temperatureInC = weatherServiceFacade.GetCurrentTemperatureByCity("San Jose");
Console.WriteLine($"The current temperature is {temperatureInC:F1}°C");
}
}
Code language: C# (cs)
Putting it all together.
namespace FacacdePattern;
public class Location
{
public double Latitude
{
get; set;
}
public double Longitude
{
get; set;
}
public Location(double latitude, double longitude)
{
Latitude = latitude;
Longitude = longitude;
}
}
public class GeoLookupService
{
public Location GetLocationByCity(string city)
{
return new Location(15, 20);
}
}
public class WeatherService
{
public double GetCurrentTemperature(Location location) => new Random().Next(32, 115);
}
public class ConverterService
{
public double ConvertFToC(double f) => (f - 32) * 5 / 9;
}
public interface IWeatherServiceFacade
{
double GetCurrentTemperatureByCity(string city);
}
public class WeatherServiceFacade : IWeatherServiceFacade
{
private readonly GeoLookupService _geoLookupService;
private readonly WeatherService _weatherService;
private readonly ConverterService _converterService;
public WeatherServiceFacade()
{
_geoLookupService = new GeoLookupService();
_weatherService = new WeatherService();
_converterService = new ConverterService();
}
public double GetCurrentTemperatureByCity(string city)
{
var location = _geoLookupService.GetLocationByCity(city);
var temperature = _weatherService.GetCurrentTemperature(location);
return _converterService.ConvertFToC(temperature);
}
}
public class Client
{
public static void Main(string[] args)
{
var weatherServiceFacade = new WeatherServiceFacade();
var temperatureInC = weatherServiceFacade.GetCurrentTemperatureByCity("San Jose");
Console.WriteLine($"The current temperature is {temperatureInC:F1}°C");
}
}
Code language: C# (cs)
Summary
- Use the Facade pattern to create a simple and clean interface to complex subsystems to make your applications more maintainable and flexible.