Chapter 8. Adapter

Nikola Tesla invented the AC electric power system back in the 19th century. He probably couldn't imagine that we would have the annoying problem of plugging an electric device made for the United States into a wall outlet in Europe without any adapter. Picture me holding an electric razor and looking at a wall outlet in the bathroom of my hotel room in Europe but missing an adapter. "What now?" "Should I buy a new razor here?" "But then when I bring it back to the United States, I will need an adapter for it too."

In object-oriented software design, sometimes you want to reuse some useful and well-tested classes in other new areas of an application. But it's quite common that new features require new interfaces and they don't fit the existing classes that can be reused. You may ask yourself: "What now?" "Should I rewrite the same classes to fit the new interfaces?" "But then when I need to add new features again, will I need to rewrite them again?" People are smart; many years since the introduction of AC electric power, there is still a huge need for power adapters fitting US plugs in European wall outlets or the other way around. No one wants to buy a new electric razor every time they travel abroad, just like we don't want to rewrite our faithful classes for new interfaces.

Since the incompatibility issue of old classes vs. new interfaces is quite common, people came up with a solution to solve that problem. That solution was used many times until it got cataloged as a design pattern called Adapter.

What Is the Adapter Pattern?

The Adapter pattern is, so to speak, to fit two different types of objects together and make them work with each other without any problem. Sometimes it's also called "Wrapper." The idea is quite straightforward. There is an adapter that implements behavior of some sort of interface that a client is expecting. It also connects to another object that has a (totally) different interface and behavior. The adapter is standing between a target interface that a client that knows how to use and an adaptee that is (totally) foreign to it. The main role of an adapter is to bring the behavior from the adaptee to the other end of the pipe to the client.

There are basically two ways to implement the Adapter pattern. The first one is by inheritance to adapt one interface to another. It's called a class adapter. In the original Design Patterns book, a class adapter is achieved by multiple inheritance. C++, which is one of the main languages used in the book, doesn't have a language feature like Java's interface or Objective-C's protocol. Everything is a class. A class without actual implementation is called an abstract class. That will be a type of thing that will be used as an "Abstract" type, unlike Objective-C; it has protocol as a form of pure abstraction. In Objective-C, we can have a class that implements a protocol and at the same time inherits a superclass to achieve something similar to the multiple inheritance in C++. There are a lot of books out there that discuss language features and such. Let's not worry about them here. To implement a class adapter in Objective-C, first we need a protocol that defines a set of behaviors that a client will use, and then we need a concrete adapter class that implements the protocol. The adapter class also inherits an adaptee at the same time. Their relationships are illustrated in Figure 8-1.

A class diagram of a class adapter

Figure 8-1. A class diagram of a class adapter

The Adapter is a type of Target as well as a type of Adaptee at the same time. The Adapter overrides the request method of the Target. However, the Adapter doesn't override the specificRequest method of Adaptee, but the method is used as a message call to the superclass in the Adapter's implementation of the request method. The request method sends a [super specificRequest] message to the superclass at runtime. super, being an Adaptee, executes the method with its own behavior within the scope of the request method in the Adapter. The class adapter can be implemented in Objective-C only when the Target is a protocol but not a class.

The second way to implement the Adapter pattern is called an object adapter. Unlike a class adapter, an object adapter doesn't inherit an adaptee class but composes a reference to it. Their new relationships implemented as an object adapter are illustrated in Figure 8-2.

A class diagram of an object adapter

Figure 8-2. A class diagram of an object adapter

The relationship between the Target and Adapter is the same as in a class adapter in Figure 8-1, except that the relationship between Adapter and Adaptee has changed from a "is a" relationship to a "has a" relationship. In this relationship, Adapter needs to keep a reference to Adaptee. In the request method, it sends a [adapteespecificRequest] message to the reference, adaptee, to access its behavior indirectly and then fulfill the rest of the request by a client. Since it's a "has a" relationship between Adapter and Adaptee, Adapter can adapt subclasses of Adaptee without much problem.

Note

THE ADAPTER PATTERN: Converts the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces.[6]

When Would You Use the Adapter Pattern?

You'd naturally think about using the pattern when

  • The interface of an existing class does not match the one you need.

  • You want to have a reusable class that can cooperate with other classes with possibly incompatible interfaces.

  • You need to adapt several different subclasses of a class, but it's impractical to let each of them subclass a class adapter. So you can use an object adapter (a.k.a. delegate) to adapt their parent's interface.

So You Know Delegation

In earlier chapters, we have talked a lot about delegation in the Cocoa Touch framework. A lot of iOS developers have already seen "delegate" here and there in the framework SDK. According to Apple's documentation, "delegate" is a form of the Delegation pattern that the Cocoa Touch framework has adopted. What's the Delegation pattern, by the way?

The Delegation pattern was once one of the inspirations for cataloging the Adapter pattern in the "Gang of Four" book. So what's the connection between them? Think again about what the Adapter pattern does: to convert the interface of a class into another interface clients expect. Adapter lets classes work together that couldn't otherwise because of incompatible interfaces. What could be the clients here? They will be Cocoa Touch framework classes. So what is the "Target" in this case? It is a delegation protocol. A concrete class that implements the protocol will be an adapter in that case. Then what would be the classes that wouldn't fit to the framework and need to be adapted? Other classes in our apps! So now you understand why the Delegation pattern is, in fact, the Adapter pattern.

The reason I said the Delegation pattern is mainly the Adapter pattern is that the Delegation mechanism can also fulfill the intents of some other design patterns, such as the Decorator pattern (Chapter 16). The implementation of the Delegation pattern in the Cocoa Touch framework can sometimes mix with other design patterns. For example, some framework classes that implement the Delegation pattern are also part of the Template Method pattern (Chapter 18). A template method contains a set of predefined, parameterized algorithms with some specific behavior left out for subclasses to provide, but in this case it is a delegate. During an operation of a template method, a message will be sent to a delegate (adapter) for some specific behavior when it's required. Then any specific information can be obtained from any adaptee in an application through the adapter's (delegate's) interface. It's common to see the Delegate pattern interrelated and used in a mixture of more than one design pattern.

Implementing the Adapter Pattern with Objective-C Protocol

We have seen a lot of framework classes in Cocoa Touch that are implemented with some form of delegation defined in protocols. We can implement our own delegation as an adapter.

In the discussion on designing the TouchPainter app in Chapter 2, we understood that there is a view where it allows the user to change stroke color and size for the next stroke drawn on the CanvasView. An instance of PaletteViewController presents that view when requested. On that view, there are three sliders for changing color components for the stroke color. That operation involves several components, CanvasViewController, PaletteViewController, and its sliders. You may think that we can probably do it in a few lines of code. But the catch is that everything will be tightly coupled and hard to reuse. We need a better solution so we can reuse the color changing part in other areas of the application. For example, if we change the interface for picking colors to a color wheel instead of using three sliders for different color components, we can still reuse the same object to accomplish the same thing. Likewise, if we go for implementing an iPad version of TouchPainter, we can also put the same object for changing colors in a pop-up menu that iPhone doesn't support.

When we want to reuse something, in terms of object-oriented programming, that something should be put in an object. If we want to put the color changing action into an object, a natural way to do so is to put it in a command object (see the Command pattern, Chapter 20). A command object can be queued and reused and can have undo and redo encapsulated operations. In the Command pattern chapter, we explain Cocoa implementation of the Command pattern with NSInvocation. An NSInvocation object can be generic enough to encapsulate and invoke a target-action unit for any Objective-C object as a target and any message as a selector. One of the limitations with NSInvocation, though, is that you cannot put multiple operations in its objects to invoke. Multiple operations can be as simple as checking password input by the user; if the check doesn't pass, then show an alert box to warn the user. Those operations don't need to be jammed in a particular controller, especially when they are so common that they can be reused in many places. So sometimes we still need our own command objects for that reason. You can either subclass NSInvocation or create your own from scratch. Chapter 20 has detailed explanations for the latter.

Designing and Implementing a Stroke Color Changing Mechanism

SetStrokeColorCommand is an implementation of the Command pattern. What it does is to set a stroke color value to the CanvasViewController, so the next stroke drawn on the CanvasView will be using a set color. You can go back to Chapter 2 and the other chapters for detailed discussions about CanvasViewController and CanvasView. When an instance of SetStrokeColorCommand is executed, it needs values of RGB to construct a color value as part of its operations. The SetStrokeColorCommand object doesn't care who will provide these values. The values should be obtained from any adapter that adapts a delegate protocol, SetStrokeColorCommandDelegate. Their static relationships are illustrated in Figure 8-3.

PaletteViewController acts as an adapter for SetStrokeColorCommandDelegate, which is a target for SetStrokeColorCommand as a client by adapting values obtained from CommandSlider instances.

Figure 8-3. PaletteViewController acts as an adapter for SetStrokeColorCommandDelegate, which is a target for SetStrokeColorCommand as a client by adapting values obtained from CommandSlider instances.

SetStrokeColorCommand is a client here because it is the one that requests the data it needs to get its jobs done from whatever can fulfill its needs. SetStrokeColorCommand is a subclass of Command as defined in Chapter 20, as shown in Listing 8-1.

Example 8-1. Command.h

@interface Command : NSObject
{
  @protected
  // other private member variables...
}

// other properties...

- (void) execute;

@end

We are not going to discuss all the methods and properties declared in the Command class but instead focus on the execute method when we get to the implementation of SetStrokeColorCommand class. SetStrokeColorCommand needs a target to tell adapters what to provide.

Defining SetStrokeColorCommandDelegate As a Target

Like other delegates you've seen in the Cocoa Touch framework, SetStrokeColorCommandDelegate is defined as a protocol. It acts as a contract between a client and an adapter, in this case, SetStrokeColorCommand and PaletteViewController objects. A protocol declaration for SetStrokeColorCommandDelegate is shown in Listing 8-2.

Example 8-2. SetStrokeColorCommandDelegate Protocol As a Target in the Adapter Pattern Defined in SetStrokeColorCommand.h

#import <Foundation/Foundation.h>
#import "Command.h"

@class SetStrokeColorCommand;

@protocol SetStrokeColorCommandDelegate

- (void) command:(SetStrokeColorCommand *) command
                didRequestColorComponentsForRed:(CGFloat *) red
                                          green:(CGFloat *) green
                                           blue:(CGFloat *) blue;

- (void) command:(SetStrokeColorCommand *) command
                didFinishColorUpdateWithColor:(UIColor *) color;

@end

The command:didRequestColorComponentsForRed:green:blue: method returns separate RGB values by passing red, green, and blue parameters as reference. On the other hand, command:didFinishColorUpdateWithColor: will be invoked when the color updating process is finished. The command object will pass itself and the updated color object to the adapter, so it can take that chance to do anything else with the new color.

Implementing SetStrokeCommand As a Client

Here it comes with our client SetStrokeColorCommand. It keeps a reference of SetStrokeColorCommandDelegate as delegate_, as shown in its class declaration in Listing 8-3.

Example 8-3. A Class Declaration of SetStrokeColorCommand in SetStrokeColorCommand.h

@interface SetStrokeColorCommand : Command
{
  @private
  id <SetStrokeColorCommandDelegate> delegate_;
}

@property (nonatomic, assign) id <SetStrokeColorCommandDelegate> delegate;

- (void) execute;

@end

Since this is a command object, you might wonder whether the delegate should be a receiver that receives any action message from the command object as defined in the Command pattern in Chapter 20. In fact, the real command's receiver is not defined here as a property, per se, but should be obtained in the execute method, as shown in Listing 8-4.

Example 8-4. An Implementation of SetStrokeColorCommand in SetStrokeColorCommand.m

#import "SetStrokeColorCommand.h"
#import "CoordinatingController.h"
#import "CanvasViewController.h"

@implementation SetStrokeColorCommand

@synthesize delegate=delegate_;


- (void) execute
{
  CGFloat redValue = 0.0;
  CGFloat greenValue = 0.0;
  CGFloat blueValue = 0.0;

  // Retrieve RGB values from a delegate
  [delegate_ command:self didRequestColorComponentsForRed:&redValue
                                                    green:&greenValue
                                                     blue:&blueValue];

  // Create a color object based on the RGB values
  UIColor *color = [UIColor colorWithRed:redValue
                                   green:greenValue
                                    blue:blueValue
                                   alpha:1.0];

  // Assign it to the current canvasViewController
  CoordinatingController *coordinator = [CoordinatingController sharedInstance];
  CanvasViewController *controller = [coordinator canvasViewController];
[controller setStrokeColor:color];

  // Forward a post update message
  [delegate_ command:self didFinishColorUpdateWithColor:color];
}

- (void) dealloc
{
  [super dealloc];
}

@end

delegate_ is sent off a command:didRequestColorComponentsForRed:green:blue: message to return individual RGB values. Then a color object is created from the obtained values. We then obtain a sole reference to CanvasViewController from CoordinatingController. A singleton instance of CoordinatingController is acting as a mediator to coordinate different view controllers in the application (see the Mediator pattern, Chapter 11). We assign the color that we've just put together in the previous step to the CanvasViewController as its new stroke color. After that, delegate_ is fired again with another delegate method with a newly set color object. Whatever that delegate_ object is will take it from there and use the color object for other things.

Now we know about what our client and target will be. Let's see what object and its values we should adapt for the client.

Creating CommandSlider As an Adaptee

As we've mentioned many times now, there are three sliders for adjusting color components and whatnot. Every minute move on the slider handle kicks off an update to a receiver with its new value. This scenario reminds us of an invoker as defined in the Command pattern (Chapter 20). An invoker keeps a reference of a command object and executes it when the invoker is invoked (e.g., a button click by the user). So our sliders also need to keep a reference to a command object that can do something for them (i.e., color updating). However, UISlider didn't expect that we are going to use our command object with it, so we need to make our own by subclassing UISlider. We call it CommandSlider, as shown in Listing 8-5.

Example 8-5. CommandSlider.h

#import <Foundation/Foundation.h>
#import "Command.h"

@interface CommandSlider : UISlider
{
  @protected
  Command *command_;
}

@property (nonatomic, retain) IBOutlet Command *command;

@end

A custom slider that takes and executes a Command object is not enough to solve our problem. We need to link it up to a SetStrokeColorCommand object. As we can see, the command property is an IBOutlet, which means we can accomplish that in the Interface Builder. Chapter 11 has a detailed explanation on linking UI elements with custom objects.

We let the sliders listen to a Value Changed event. When there is a change on the slider's handle, the runtime will fire off the event with a target-action that should be invoked. The slider will then execute the stored command object. We will get to the details of that part in the next section.

Using PaletteViewController As an Adapter

Finally, we are getting to our adapter for the design. PaletteViewController is a natural choice to adapt values from the RGB sliders because the controller owns and manipulates them. The PaletteViewController class is required to conform to the SetStrokeColorCommandDelegate in order to adapt anything for a client (i.e., a SetStrokeColorCommand object). Let's take a look at its adopted delegate methods in Listing 8-6.

Example 8-6. The SetStrokeColorCommandDelegate Methods Implemented by PaletteViewController

#pragma mark -
#pragma mark SetStrokeColorCommandDelegate methods

- (void) command:(SetStrokeColorCommand *) command
                didRequestColorComponentsForRed:(CGFloat *) red
                                          green:(CGFloat *) green
                                           blue:(CGFloat *) blue
{
  *red = [redSlider_ value];
  *green = [greenSlider_ value];
  *blue = [blueSlider_ value];
}

- (void) command:(SetStrokeColorCommand *) command
                didFinishColorUpdateWithColor:(UIColor *) color
{
  [paletteView_ setBackgroundColor:color];
}

When the client (SetStrokeColorCommand object) asks for new RGB values and fires off command:didRequestColorComponentsForRed:green:blue: to its delegate or adapter, then PaletteViewController responds by assigning each color component value with the corresponding slider's value. Likewise, when the SetStrokeColorCommand object is done with its business updating a new stroke color, it will fire off another method for its adapter to perform any extra operations with the new color object. PaletteViewController responds to that message by updating paletteView_'s background color with the new color, where paletteView_ is the small color palette view sitting right below the color sliders.

At this point, we have a pretty good picture of how things are put together. But how are we going to use the CommandSlider instances in code? The answer is we are not going to use it directly, but we will use the help from the Interface Builder by hooking up the color sliders with PaletteViewController in a .xib file.

When there is a change to the value of any one of the color sliders, the event will be captured by invoking an onCommandSliderValueChanged: method defined in PaletteViewController, as shown in Listing 8-7.

Example 8-7. onCommandSliderValueChanged: Event Handler for Any Instance of CommandSlider in PaletteViewController.m

#pragma mark -
#pragma mark Slider event handler

- (IBAction) onCommandSliderValueChanged:(CommandSlider *)slider
{
  [[slider command] execute];
}

This little event handling method in the PaletteViewController does the trick to let all types of CommandSlider objects run the same way with their own embedded Command object. Each slider returned by that method will invoke the execute method of its Command object. In this case, the execute method of the SetStrokeColorCommand will in turn try to get RGB values from the adapter (PaletteViewController). Once the delegate returns some RGB values, then the SetStrokeColorCommand will continue to update CanvasViewController's stroke color, as discussed in Listing 8-4.

Voilà! Everything is now connected in the same pipeline. Every object has its own particular duty and is highly reusable. If later we decided to change the way of adjusting the stroke size, we can still reuse the same SetStrokeColorCommand to serve the same purpose without rewriting that part again. The onCommandSliderValueChanged: event handler can also be used with other sliders that have different command objects. Do you see how they are connected but not dependent on each other?

Implementing the Adapter Pattern with Objective-C Blocks in iOS 4

We have seen that the Adapter pattern can be implemented with a protocol as a target interface that defines some standard behavior that clients expect and adapters can commit to. A protocol is a language-level feature in Objective-C that makes it possible to define interfaces that are instances of the Adapter pattern. The term "interface" is synonymous with "protocol" in other object-oriented languages like Java. Interfaces used in the Adapter pattern are essentially a series of method declarations that a client object knows how to communicate with. But with a protocol, both an adapter and a client need to know about a target as a protocol in order to make everything well defined and run correctly. A language feature called blocks is a powerful feature in Objective-C that can allow us to implement the Adapter pattern without using a protocol. Blocks are available since the introduction of iOS 4.0.

Blocks are like functions, but written inline with the rest of your code, inside other functions. Blocks have been around for some time now. They have been part of scripting and programming languages such as Ruby, Python, Smalltalk, and Lisp. Sometimes blocks can go by the names of "closures" and "lambdas." They are called "closures" because they close around variables in your scope. We will not go through any specific language details of blocks in this section, but we will briefly cover some basic syntaxes and how we can use them to implement the Adapter pattern.

Declaring a Block Reference

A block variable holds a reference to a "block" of code that can be passed around in an application. It's similar to a function pointer in C language, as follows:

int (^ObjectiveCblock)(int);
int (*CFunctionPointer)(float);

The first one is an Objective-C block variable that takes an integer as an argument and returns an integer. The syntax is very similar to the C version in the second one. The C function pointer variable takes a floating-point number as an argument and returns an integer. The main difference in terms of syntax between the two is that a C function pointer uses the * character while an Objective-C block uses the ^ character.

If you use the same block in multiple places, instead of typing the same cryptic signature every time you want to define it, it's better to create a type for it with typedef:

typedef int (^ObjectiveCblock)(int);

So you can define it later as follows:

ObjectiveCblock firstBlock = ...;
ObjectiveCblock secondBlock = ...;

Now it's much easier to read, with a simpler type name compared to a complete block signature. We're going to go over how to define a block literal in a little bit.

Creating a Block

In the previous section, we've defined a block variable, ObjectiveCblock, and declared it as a type. In order to define an actual block literal (the meat of a block as a function) in it, we can do the following:

ObjectiveCBlock aBlock = ^(int anInt) {

        return anInt++;
}

Then it can be used as a function in other parts of an application, like this:

int result = aBlock(5);

An amazing thing about blocks is that they are actually Objective-C objects. They are regarded as anonymous objects that live on the stack and will be destroyed once they fall out of scope. You can copy, retain, release, and autorelease them just like other regular Objective-C objects. But retain doesn't seem to have any real effects on a block. So if you want to "retain" a block, you need to make a "copy" of it by moving it to the heap. Like a regular Objective-C object, you need to balance retain/copy with release/autorelease. For the block just defined, we can copy and release it in this way:

id aBlockCopy = [ ^(int anInt) { return anInt++; } copy];

// do something with aBlockCopy...

[ aBlockCopy release];

Or in this way:

id aBlockCopy = [aBlock copy];

// do something with aBlockCopy...

[aBlockCopy release];

Or in thiseven simpler way, especially when you want to return it from another method or function:

id aBlockCopy = [[aBlock copy] release];

// do something with aBlockCopy...

Any one of the styles will achieve the same result. You get the idea.

One of the main differences between a C function pointer and an Objective-C block is that a block literal can be defined inline with the same message call:

[anObject doSomethingWithBlock: ^(int anInt) {

        return anInt++;
}];

It's something that you can't do with a C function pointer, though. Now you should be curious about how we can use a block as a "building block" for implementing the Adapter pattern.

Using a Block As an Adapter

We remember on the setting view of the PaletteViewController in our TouchPainter app, there are three sliders that allow the user to change the stroke color. Each slider corresponds to red, green, and blue values respectively in the RGB value of the color object that's used in iOS. One may want to, let's say, add more red to the current color, and then he or she can move the slider that corresponds to the red value to increase the red value, and the same for other sliders for other color components.

In the last part, we used a protocol to define a target for an adapter to adopt. This time, we will use a block to implement an adapter for the same purpose. A new class diagram for our new adapter is shown in Figure 8-4.

A class diagram of a realization of an RGBValuesProvider block as an object adapter for StrokeColorCommand by adapting UISlider with object composition

Figure 8-4. A class diagram of a realization of an RGBValuesProvider block as an object adapter for StrokeColorCommand by adapting UISlider with object composition

From the figure, we define a block called RGBValuesProvider that has a signature of CGFloat ^(CGFloat *red, CGFloat *green, CGFloat *blue). Each parameter in the signature is a pointer to a CGFloat value. It is just a type until we assign an actual block literal to it. A block literal should conform to the block signature and provide implementation that does something. In the foregoing diagram, we have a block literal RGBValuesProvider that contains the same old redSlider_, greenSlider_, and blueSlider_. When the block is called in an operation, it will retrieve values from the sliders and assign the values to the pointer values defined in the block signature to pass them back to a caller. Let's see how they could look in code (Listing 8-8).

Example 8-8. SetStrokeColorCommand.h

#import <Foundation/Foundation.h>
#import "Command.h"

typedef void (^RGBValuesProvider)(CGFloat *red, CGFloat *green, CGFloat *blue);

@interface StrokeColorCommand : Command
{
  @private
  RGBValuesProvider RGBValuesProvider_;
}

@property (nonatomic, copy) RGBValuesProvider RGBValuesProvider;

- (void) execute;

@end

We typedef the RGBValuesProvider block in the header file of SetStrokeColorCommand, so we can refer the same block in other places just by its name as if it were an Objective-C object type. SetStrokeColorCommand has a private member variable of the block type we have just defined, and that variable is associated with a property that has the copy attribute turned on. We need to copy a block as it's assigned to a SetStrokeColorCommand object, as most of the time a block literal is defined and used within the scope of a method. When a method goes out of scope, so does the block literal. For more detailed discussion about memory management related to blocks, please see Apple's documentation regarding block programming.

SetStrokeColorCommand is a Command object, so it's got a definition of the execute method that it's inheriting from. We are going to see what it does in its execute method in Listing 8-9.

Example 8-9. StrokeColorCommand.m

#import "StrokeColorCommand.h"
#import "CoordinatingController.h"
#import "CanvasViewController.h"

@implementation StrokeColorCommand

@synthesize RGBValuesProvider=RGBValuesProvider_;


- (void) execute
{
  CGFloat redValue = 0.0;
  CGFloat greenValue = 0.0;
  CGFloat blueValue = 0.0;

  // Retrieve RGB values from a block
  if (RGBValuesProvider_ != nil)
  {
    RGBValuesProvider_(&redValue, &greenValue, &blueValue);
  }

  // Create a color object based on the RGB values
  UIColor *color = [UIColor colorWithRed:redValue
                                   green:greenValue
                                    blue:blueValue
                                   alpha:1.0];

  // Assign it to the current canvasViewController
  CoordinatingController *coordinator = [CoordinatingController sharedInstance];
  CanvasViewController *controller = [coordinator canvasViewController];
  [controller setStrokeColor:color];
}

- (void) dealloc
{
  [RGBValuesProvider_ release];
  [super dealloc];
}

@end

When an instance of the SetStrokeColorCommand is being executed, it is trying to get values for the color components by calling its block RGBValuesProvider_(&redValue, &greenValue, &blueValue). A new UIColor object is created with the returned values, and then the SetStrokeColorCommand sets that color object to an instance of CanvasViewController.

So how are we going to use the SetStrokeColorCommand in code? Let's find out in the PaletteViewController's code in Listing 8-10.

Example 8-10. viewDidLoad Method in PaletteViewController.m

- (void)viewDidLoad
{
  [super viewDidLoad];

  // initialize the RGB sliders with
  // a StrokeColorCommand
  StrokeColorCommand *colorCommand;

  // retrieve a reference to the command
  // being used for changing colors...

  // set each color component provider
  // to the color command
  [colorCommand setRGBValuesProvider: ^(CGFloat *red, CGFloat *green, CGFloat *blue)
   {
     *red = [redSlider_ value];
     *green = [greenSlider_ value];
     *blue = [blueSlider_ value];
   }];
}

We have defined a member variable for each color slider, redSlider_, greenSlider_, and blueSlider_. They are all connected and set up in the Interface Builder. Finally, we define a block literal that returns values from those sliders as color component values and assign the literal to the colorCommand object that is being referenced in those sliders.

It seems that we haven't defined any event handling method for our color sliders. But, in fact, we are going to reuse the one we have defined in the last section, as they are all the same CommandSlider type and used the same way. So we just need to simply hook up those color sliders to the same onCommandSliderValueChanged: method in the PaletteViewController in the Interface Builder. You can, of course, add another block for letting an adapter do an extra duty with the new color object, like we have in the delegation approach in the preceding section.

Done! What do you think about using a block as an adapter compared to a relatively more traditional way of using a protocol? A block allows you to define the definition of a block virtually anywhere and let a receiver (SetStrokeColorCommand) use it later. It doesn't affect any inheritance of the classes involved as well, because an adapter is defined within the viewDidLoad method. This approach offers much cleaner code and architecture compared to more formal protocol and classes involved. The adapter is more direct and casual, yet it doesn't lose the original flavor of the Adapter pattern.

Summary

This is a relatively long chapter. Now we understand how to use an Objective-C protocol to implement the Adapter pattern. Though the Delegation pattern itself can serve multiple intents other than just for the Adapter pattern, it was one of the major inspirations for the Adapter pattern in the first place! We have also explored how to take advantage of the power of Objective-C blocks to implement the Adapter pattern with a block serving as an adapter. Blocks are new to iOS development, but they are a powerful language feature. We should tap into them for more possibilities in the future.

The Adapter pattern is one of most commonly used design patterns. You can see it appear in code from time to time. The examples we used for illustrating the pattern are just a couple of them in the TouchPainter app. In real life, it's not uncommon to see design patterns used together for particular solutions, just like the examples of using an adapter with a command object. Try to check out a copy of the source code and see how many more adapters you can find in the app.

In the next chapter, we will see another pattern that helps decouple the abstraction interfaces from implementation so they can vary independently.



[6] 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.118.20.231