Friday, December 9, 2011

The Facade Pattern

The Facade pattern is an incredibly simple pattern. In fact, there's a good chance you've implemented it in your projects before without being aware that you were using a design pattern.
This is a short and simple article, but it is a very useful pattern. The Facade pattern simplifies an interface.
A system can be made up of many classes, with complicated interdependencies, each with their own unique interface.
The Facade pattern is a way to simplify how the client interacts with these objects.
Let's say we have a bunch a classes that control various appliances in the house.
class KitchenLight
{
    public void On()
    {
        Console.WriteLine("Kitchen light on.");
    }
    
    public void Off()
    {
        Console.WriteLine("Kitchen light off.");
    }
}

class LivingRoomLight
{
    public void On()
    {
        Console.WriteLine("Living room light on.");
    }

    public void Off()
    {
        Console.WriteLine("Living room light off.");
    }
}

class TV
{
    int _channel;

    public int Channel
    {
        get { return _channel; }
    }

    public void On()
    {
        Console.WriteLine("TV on.");
    }

    public void Off()
    {
        Console.WriteLine("TV off.");
    }

    public void ChangeChannel(int channel)
    {
        _channel = channel;
    }
}

class DVD
{
    string _name;
    
    public string Name
    {
        get { return _name; }
    }

    public DVD(string name)
    {
        _name = name;
    }
}

class DVDPlayer
{
    TV _tv;
    DVD _dvd;

    public DVDPlayer(TV tv)
    {
        _tv = tv;
    }

    public void On()
    {
        Console.WriteLine("DVD on.");
    }

    public void Off()
    {
        Console.WriteLine("DVD off.");
    }

    public void PlayMovie()
    {
        if (_tv.Channel == 4)
        {
            if (_dvd != null)
            {
                Console.WriteLine("Playing " + _dvd.Name);
            }
            else
            {
                Console.WriteLine("No disc in player.");
            }
        }
        else
        {
            Console.WriteLine("TV must be on channel 4.");
        }
    }

    public void InsertDVD(DVD dvd)
    {
        Console.WriteLine("Inserting " + _dvd.Name);
        _dvd = dvd;
    }    
}
Seems simple but now what if we want to watch a movie? From our client class we'd have to....
class Program
{
    static void Main(string[] args)
    {
        KitchenLight kitchenLight = new KitchenLight();
        LivingRoomLight livingRoomLight = new LivingRoomLight();
        TV tv = new TV();
        DVDPlayer dvdPlayer = new DVDPlayer(tv);
        DVD dvd = new DVD("Memento");

        //We'd have to do all this!
        kitchenLight.Off();
        tv.On();
        tv.ChangeChannel(4);
        dvdPlayer.On();
        dvdPlayer.InsertDVD(dvd);
        livingRoomLight.Off();
        dvdPlayer.PlayMovie();
    }
}
That's a lot just to watch a dvd. And when we're done, we'll have to do the whole thing again in reverse.
And, oh no! We just got an XBox and a new game!
class XBoxGame
{
    string _name;

    public string Name
    {
        get { return _name; }
    }

    public XBoxGame(string name)
    {
        _name = name;
    }
}

class XBox
{
    TV _tv;
    XBoxGame _game;

    public DVDPlayer(TV tv)
    {
        _tv = tv;
    }

    public void On()
    {
        Console.WriteLine("XBox on.");
    }

    public void Off()
    {
        Console.WriteLine("XBox off.");
    }

    public void PlayGame()
    {
        if (_tv.Channel == 4)
        {
            if (_game != null)
            {
                Console.WriteLine("Playing " + _game.Name);
            }
            else
            {
                Console.WriteLine("No game in console.");
            }
        }
        else
        {
            Console.WriteLine("TV must be on channel 4.");
        }
    }

    public void InsertGame(XBoxGame game)
    {
        Console.WriteLine("Inserting " + _game.Name);
        _game = game;
    }    
}
This is getting ridiculous.
Luckily the facade pattern can help. A facade is basically a class that holds all these interconnected classes and knows how to interact with them.
Let's see how.
class HomeApplianceFacade
{
    TV _tv;
    DVDPlayer _dvdPlayer;
    KitchenLight _kitchenLight;
    LivingRoomLight _livingRoomLight;
    XBox _xbox;

    public HomeApplianceFacade(
        TV tv,
        DVDPlayer dvdPlayer,
        KitchenLight kitchenLight,
        LivingRoomLight livingRoomLight,
        XBox xbox)
    {
        _tv = tv;
        _dvdPlayer = dvdPlayer;
        _kitchenLight = kitchenLight;
        _livingRoomLight = livingRoomLight;
        _xbox = xbox;
    }

    public void WatchMovie(DVD dvd)
    {
        kitchenLight.Off();
        tv.On();
        tv.ChangeChannel(4);
        dvdPlayer.On();
        dvdPlayer.InsertDVD(dvd);
        livingRoomLight.Off();
        dvdPlayer.PlayMovie();
    }

    public void FinishedMovie()
    {
        _livingRoomLight.On();
        _dvdPlayer.Off();
        _tv.Off();
        _kitchenLight.On();
    }

    public void PlayGame(XBoxGame game)
    {
        _kitchenLight.Off();
        _tv.On();
        _tv.ChangeChannel(4);
        _xBox.On();
        _xBox.InsertGame(game);
        _livingRoomLight.Off();
        _xBox.PlayGame();
    }

    public void FinishedPlayingGame()
    {
        _livingRoomLight.On();
        _xBox.Off();
        _tv.Off();
        _kitchenLight.On();

    }
}
Now we have a few methods that deal with all the low level classes correctly so we don't have to deal with it every time.
When we want to watch a movie or something we deal directly with the Facade we've created. We can still access the low level stuff if we want to, but the simplified interface is there if we need it.
class Program
{
    static void Main(string[] args)
    {
        //We still have to 'install' our system.
        KitchenLight kitchenLight = new KitchenLight();
        LivingRoomLight livingRoomLight = new LivingRoomLight();
        TV tv = new TV();
        DVDPlayer dvdPlayer = new DVDPlayer(tv);

        //But now we have a facade to handle all the complicated stuff.
        HomeApplianceFacade applianceFacade =
            new HomeApplianceFacade(tv, dvdPlayer, kitchenLight, livingRoomLight, xBox);

        //Our simplified interface.
        applianceFacade.WatchMovie(new DVD("Memento"));
        applianceFacade.FinishedMovie();
        applianceFacade.PlayGame(new XBoxGame("Skyrim"));
        applianceFacade.FinishedPlayingGame();
    }
}
And that's pretty much it! The Facade pattern; a great way to create a unified interface to simplify a complicated subsystem.

Thanks, as always for reading and I hope you enjoyed the article.

No comments :

Post a Comment