Chapter 10. Façade

You don't feel you want to drive today, so you call up a taxicab. You don't really care what the make or model of the cab is as long as it can take you to the destination you want to go to. The first thing you'd say to the cab driver is "Take me to X," where X is the place where you wanted to go. Then the cab driver begins with a sequence of actions to "execute" the "command" (releases the brakes, changes gears, presses the accelerator, etc.). The cab driver abstracts away all the bells and whistles of the underlying complexity of operating a car. He decouples you from the originally complicated interfaces to operate the vehicle by offering the driving service (a simplified interface). The interface between the cab driver and you is just a simple "Take me to X" command. And that command is not tied to any specific make/model of a vehicle, either.

In many legacy object-oriented applications, there might be a lot of classes scattered around the system with different functionalities. To use them for a particular function, you need to know all the bits and pieces in order to use them in a single group of algorithms. If some of them can be logically grouped into a simplified interface, it can make them easier to use. A way to provide a unified interface to a set of different interfaces in a subsystem is called the Façade pattern.

We are going to discuss what the Façade pattern is and how to implement the pattern in Objective-C.

What Is the Façade Pattern?

The Façade pattern helps provide a unified interface to a set of different interfaces in a subsystem. Façade defines a higher-level interface that makes the subsystem easier to use by reducing complexity and hiding the communication and dependencies among subsystems.

Let's say there is a cluster of different classes within this subsystem, as shown in Figure 10-1. Some of them are dependent on each other. It makes it difficult for clients to use the classes in the subsystem, as they need to know each one of them. Sometimes it could be unnecessarily cumbersome if the clients just need their default behavior without customizing them. Façade acts as a gateway for the whole subsystem. It provides a simplified interface for clients that need somewhat default behavior without much customizability on the subsystem classes. A façade is like the taxicab driver in the previous example. Only clients that need more customized behavior from some of the subsystem classes will look beyond the façade. A taxicab scenario can provide a driving service with "default" behavior on the road. But if you want to have a "customized" trip (e.g., taking a few pit stops or speeding on highways, etc.), then you'd be better off driving by yourself.

A sample structural diagram of a Façade implementation

Figure 10-1. A sample structural diagram of a Façade implementation

Note

FAÇADE PATTERN: Provides a unified interface to a set of interfaces in a system. Façade defines a higher-level interface that makes the subsystem easier to use.[8]

When Would You Use the Façade Pattern?

There are three common situations in which you would consider using this pattern:

  • Your subsystem is getting complex. A lot of classes are evolved from applying patterns. You can use a façade to provide a simpler interface for the subsystem classes.

  • You can use façades to layer your subsystems. Each subsystem level has a façade as an entry point. You can simplify their dependencies by making them communicate through their façades.

In the coming sections, we are going to implement the Façade pattern with the taxicab driver example to illustrate the basic concepts. Also, later on, we will use the pattern to simplify the scribble saving processes of the TouchPainter app.

Providing a Simplified Interface to a Set of Interfaces in the Subsystem

Back to the taxicab example in the introduction of this chapter—we have a passenger and a taxicab that provides a simplified interface to a set of complex interfaces of driving a cab. If we put them in a class diagram, then they'd look something like Figure 10-2.

A class diagram for the taxicab example—the CabDriver class is a façade of the Taxicab subsystem.

Figure 10-2. A class diagram for the taxicab example—the CabDriver class is a façade of the Taxicab subsystem.

You can see the whole taxicab service as a closed system that includes a cab driver, a car, and a taximeter. The only way you can interface with the system is to go through the interface defined in CabDriver—that's driveToLocation:x. Once you send the cab driver a message of driveToLocation:x, then CabDriver will take it from there. There are two subsystems that he or she needs to operate with, Taximeter and Car. CabDriver will first set off the Taximeter to get it ticking, and then with the Car he or she will releaseBrakes, changeGears, and pressAccelerator to start moving it. Until you've arrived at location x, the CabDriver will releaseAccelerator, pressBrakes, and stop the Taximeter to complete the trip. Everything happens within a simple driveToLocation:x command to the CabDriver. No matter how complex those two internal subsystems are, they are hidden from the view of you (or a passenger). So the CabDriver is providing a simplified interface for other complex interfaces within the subsystem of the cab. The CabDriver is standing in between a passenger and the complex system like a façade. Let's take a look at Listing 10-1 and see what they look like in code.

Example 10-1. Car.h

@interface Car : NSObject
{

}

// ...

- (void) releaseBrakes;
- (void) changeGears;
- (void) pressAccelerator;
- (void) pressBrakes;
- (void) releaseAccelerator;

// ...

@end

In Listing 10-1, Car defines several methods for operating its other internal objects, things like releaseBrakes, changeGears, pressAccelerator, pressBrakes, and releaseAccelerator. In order for a client to use Car's internal objects, it needs to know all the bells and whistles about how to use those methods to operate it properly. Besides Car, let's also take a look at the code for Taximeter in Listing 10-2.

Example 10-2. Taximeter.h

@interface Taximeter : NSObject
{

}

- (void) start;
- (void) stop;

@end

Although a Taximeter is a complex system by itself, it provides two methods to let clients operate on its objects. The start and stop methods simply tell the Taximeter to either start or stop. We are not getting into the details of the Taximeter. So far, there are two complex subsystems sitting in the taxicab service system. We need a CabDriver to serve as a façade to simplify the interfaces. Its code snippets are in Listing 10-3.

Example 10-3. CabDriver.h

#import "Car.h"
#import "Taximeter.h"

@interface CabDriver : NSObject
{

}

- (void) driveToLocation:(CGPoint) x;

@end

The CabDriver's façade method defines how simple clients can use the whole taxicab service system. As we have mentioned before, a client just needs to call driveToLocation:x, where x is the destination a client wants to go to, and then the rest of the operation will take place within the message call. The client doesn't need to know all the bits and pieces of what's going on under the hood. Let's see what the method actually does in Listing 10-4.

Example 10-4. CabDriver.m

#import "CabDriver.h"


@implementation CabDriver

- (void) driveToLocation:(CGPoint) x
{
  // ...

  // set off the taximeter
  Taximeter *meter = [[Taximeter alloc] init];
  [meter start];

  // operate the vehicle
  // until location x is reached
  Car *car = [[Car alloc] init];
  [car releaseBrakes];
  [car changeGears];
  [car pressAccelerator];

  // ...

  // when it's reached location x
  // then stop the car and taximeter
  [car releaseAccelerator];
  [car pressBrakes];
  [meter stop];

  // ...
}

@end

Inside the driveToLocation: method, it will first start an object of Taximeter so it can start ticking from that moment. Then it will move to an object of Car and start operating on it. It sends a releaseBrakes message to the Car object to release the brakes, then changeGears, and finally pressAccelerator to make it start moving. When we arrive at the destination x, it will tell the Car to releaseAccelerator, then pressBrakes, and finally tell the Taximeter object to stop ticking. Then the service is completed.

It doesn't look complicated, does it? With our CabDriver in place as a façade, we can simplify the whole service system. Are you ready to see how we can put a façade in a real app, our TouchPainter app? Let's move on to the next section.

Using the Façade Pattern in the TouchPainter App

In the original design of the TouchPainter app described in Chapter 2, one of the requirements is saving what is drawn on the CanvasView as a form of Scribble and putting that data structure in the file system. An archive of a scribble can be retrieved later and resurrected to a real Scribble object back to the state right before it's saved. That operation involves different objects put together to make the process happen. Based on the original design, part of the saving process is involved with a data structure that contains the current state of the strokes onscreen as well as a screenshot of it that will be used as a thumbnail later for browsing. Another part of the process is to use an instance of NSFileManager to actually save the data structure in the file system. The data structure is created as a memento (see the Memento pattern, Chapter 23) by a Scribble object. A memento object should be saved independently from a corresponding thumbnail image. The whole process involves a lot of steps, operations, and different objects. Without simplifying the interfaces, it would easily go out of control, especially when you need to reuse the same operations later in other areas of the application. So we need a ScribbleManager to handle all the complex operations under the hood with some simplified interfaces. A class diagram illustrates their relationships in Figure 10-3.

A class diagram of ScribbleManager and other classes in its subsystem

Figure 10-3. A class diagram of ScribbleManager and other classes in its subsystem

There are a few public interfaces in ScribbleManager, but we will discuss just the saveScribble:scribble thumbnail:image method. You can checkout a copy of the source code on the book's website and take a look at the actual implementation of ScribbleManager. The general idea of ScribbleManager is to simplify any operation related to saving and restoring Scribble objects. A Scribble is a model (in the sense of Model-View-Controller) that contains an internal data structure for all the current strokes drawn on the CanvasView by the user. An instance of Scribble is sitting inside in an instance of CanvasViewController (see the Observer pattern, Chapter 12) as part of its Model-View-Controller structure.

When a request is initiated to save the current Scribble (what's on the CanvasView), a reference to the current Scribble object as well as a screenshot of the scribble are passed to the saveScribble:scribble thumbnail:image method of a ScribbleManager object as part of a message. Code snippets for the ScribbleManager are shown in Listings 10-5 and 10-6.

Example 10-5. ScribbleManager.h

#import <Foundation/Foundation.h>
#import "Scribble.h"
#import "ScribbleThumbnail.h"
#import "ScribbleThumbnailProxy.h"

@interface ScribbleManager : NSObject
{

}

- (void) saveScribble:(Scribble*)scribble thumbnail:(UIImage*)image;
- (NSInteger) numberOfScribbles;
- (Scribble*) scribbleAtIndex:(NSInteger)index;
- (ScribbleThumbnail*) scribbleThumbnailAtIndex:(NSInteger)index;

@end

Example 10-6. The Definition of the saveScribble: Method in ScribbleManager.m

- (void) saveScribble:(Scribble*)scribble thumbnail:(UIImage*)image
{
  // get a new index for the new scribble data and its thumbnail
  NSInteger newIndex = [self numberOfScribbles] + 1;

  // use the index as part of the name for each of them
  NSString *scribbleDataName = [NSString stringWithFormat:@"data_%d", newIndex];
  NSString *scribbleThumbnailName = [NSString stringWithFormat:@"thumbnail_%d.png",
                                                                      newIndex];

  // get a memento from the scribble
  // then save the memento in the file system
  ScribbleMemento *scribbleMemento = [scribble scribbleMemento];
  NSData *mementoData = [scribbleMemento data];
  NSString *mementoPath = [[self scribbleDataPath]
                                 stringByAppendingPathComponent:scribbleDataName];
  [mementoData writeToFile:mementoPath atomically:YES];

  // save the thumbnail directly in
// the file system
  NSData *imageData = [NSData dataWithData:UIImagePNGRepresentation(image)];
  NSString *imagePath = [[self scribbleThumbnailPath]
                                  stringByAppendingPathComponent:scribbleThumbnailName];
  [imageData writeToFile:imagePath atomically:YES];
}

In the saveScribble:scribble thumbnail:image method, it runs the whole show of saving a scribble and its screenshot as an image in the file system. It first creates paths in the file system for both the scribble data and screenshot image. Then it sends a memento message to scribble for an instance of ScribbleMemento to save the data with (see the Memento pattern, Chapter 23). The ScribbleMemento reference contains the current state of scribble. Only a Scribble object will be able to retrieve what's stored inside a ScribbleMemento object. Then it will ask the ScribbleMemento object to create an instance of NSData based on its internal structure. Once a complete path for the newly returned data is constructed, then ScribbleManager will go ahead and save that data object in the file system by sending a writeToFile:mementoPath message to the ScribbleMemento object.

Summary

When your application is getting larger and more complex, more and more small classes are evolved from the design and applying patterns to it. If there is not a simplified way to use those classes, your client code will end up getting larger and harder to understand. What's more, maintaining them will just be a chore. A façade can help provide a much cleaner way to use those classes in a subsystem. It could just be a simple method defined in a façade that handles the default behavior of the subsystem classes, instead of using them individually.

In this part of the book, we have covered design patterns that primarily focus on adapting different interfaces with simpler or different ones. Design patterns in the next part are used for dealing with decoupling many objects that work together with common or different interfaces.



[8] The original definition appeared in Design Patterns, by the "Gang of Four" (Addison-Wesley, 1994).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.224.44.53