320 likes | 508 Views
Adapter. Explained. Intent. Convert the interface of a class into another interface clients expect Adapter lets classes work together that couldn‘t otherwise because of incompatible interfaces A.K.A Wrapper. Motivation.
E N D
Adapter Explained
Intent • Convert the interface of a class into another interface clients expect • Adapter lets classes work together that couldn‘t otherwise because of incompatible interfaces • A.K.A Wrapper
Motivation • Sometimes a toolkit class that's designed for reuse isn't reusable only because its interface doesn't match the domain-specific interface an application requires • Consider for example a drawing editor that lets users draw and arrange graphical elements (lines, polygons, text, etc.) into pictures and diagrams • The drawing editor's key abstraction is the graphical object, which has an editable shape and can draw itself • The interface for graphical objects is defined by an abstract class called Shape • The editor defines a subclass of Shape for each kind of graphical object: a LineShape class for lines, a PolygonShape class for polygons, and so forth
Motivation • Classes for elementary geometric shapes like LineShape and PolygonShape are rather easy to implement, because their drawing and editing capabilities are inherently limited • But a TextShape subclass that can display and edit text is considerably more difficult to implement, since even basic text editing involves complicated screen update and buffer management • Meanwhile, an off-the-shelf user interface toolkit might already provide a sophisticated TextView class for displaying and editing text • Ideally we'd like to reuse TextView to implement TextShape, but the toolkit wasn't designed with Shape classes in mind. So we can't use TextView and Shape objects interchangeably.
Motivation • How can existing and unrelated classes like TextView work in an application that expects classes with a different and incompatible interface? • We could change the TextView class so that it conforms to the Shape interface, but that isn't an option unless we have the toolkit's source code • Even if we did, it wouldn't make sense to change TextView; the toolkit shouldn't have to adopt domain-specific interfaces just to make one application work • Instead, we could define TextShape so that it adapts the TextView interface to Shape's
Motivation • We can do this in one of two ways: • By inheriting Shape's interface and TextView's implementation or • By composing a TextView instance within a TextShape and implementing TextShape in terms of TextView's interface • These two approaches correspond to the class and object versions of the Adapter pattern • We call TextShape an adapter
Motivation 1. This diagram illustrates the object adapter case. 2. It shows how BoundingBox requests, declared in class Shape, are converted to GetExtent requests defined in TextView. Since TextShape adapts TextView to the Shape interface, the drawing editor can reuse the otherwise incompatible TextView class
Motivation • Often the adapter is responsible for functionality the adapted class doesn’t provide. The diagram shows how an adapter can fulfill such responsibilities • The user should be able to "drag" every Shape object to a new location interactively, but TextView isn't designed to do that. TextShape can add this missing functionality by implementing Shape's CreateManipulator operation, which returns an instance of the appropriate Manipulator subclass. • Manipulator is an abstract class for objects that know how to animate a Shape in response to user input, like dragging the shape to a new location. There are subclasses of Manipulator for different shapes; TextManipulator, for example, is the corresponding subclass for TextShape. By returning a TextManipulator instance, TextShape adds the functionality that TextView lacks but Shape requires.
Applicability • Use the Adapter pattern when • you want to use an existing class, and its interface does not match the one you need • you want to create a reusable class that cooperates with unrelated or unforeseen classes, that is, classes that don't necessarily have compatible interfaces • (object adapter only) you need to use several existing sub-classes, but it’s impractical to adapt their interface by sub-classing every one. An object adapter can adapt the interface of its parent class
Structure A class adapter uses multiple inheritance to adapt one interface to another
Structure An object adapter relies on object composition
Participants • Target (Shape) • defines the domain-specific interface that Client uses. • Client (Drawing Editor) • collaborates with objects conforming to the Target interface • Adaptee (TextView) • defines an existing interface that needs adapting • Adapter (TextShape) • adapts the interface of Adaptee to the Target interface
Collaborations • Clients call operations on an Adapter instance. In turn, the adapter calls Adaptee operations that carry out the request
Consequences • A class adapter adapts Adaptee to Target by committing to a concrete Adapter class. As a consequence, a class adapter won't work when we want to adapt a class and all its subclasses. • A class adapter lets Adapter override some of Adaptee's behavior, since Adapter is a subclass of Adaptee • An object adapter lets a single Adapter work with many Adaptees—that is, the Adaptee itself and all of its subclasses (if any). The Adapter can also add functionality to all Adaptees at once • An object adapter makes it harder to override Adaptee behavior. It will require subclassing Adaptee and making Adapter refer to the subclass rather than the Adaptee itself
Consequence • How much adapting does Adapter do? Adapters vary in the amount of work they do to adapt Adaptee to the Target interface. There is a spectrum of possible work, from simple interface conversion—for example, changing the names of operations—to supporting an entirely different set of operations. The amount of work Adapter does depends on how similar the Target interface is to Adaptee's
Real World Adapters • Real World Example: AC Power Adapters • Electronic products made for the USA cannot be used directly with outlets found in most other parts of the world • To use, you need an AC power adapter
OO Adapters Existing system and new vendor library do not change, new code is isolated within the adapter.
How Client Uses the Adapter • The client makes a request to the adapter by calling a method on it using a target interface • The adapter translates the request in one or more calls on the adaptee using the adaptee interface • The client receives the results of the call and never knows there is an adapter doing the translation
Duck Adapter: A turkey amongst ducks • If it walks like a duck and quacks like a duck, then it must be a duck! OR • If it walks like a duck and quacks like a duck, then it must might be a duck turkey wrapped with a duck adapter…
Object Implementation • First we look at the object implementation of this problem
Duck Classes public interface Duck { void quack(); void fly(); } public class MallardDuck : Duck { public void quack() { Console.WriteLine("Quack"); } public void fly() { Console.WriteLine("I'm flying"); } }
Turkey Classes public interface Turkey { void gobble(); void fly(); } public class WildTurkey : Turkey { public void gobble() { Console.WriteLine("Gobble Gobble"); } public void fly() { Console.WriteLine("I'm flying short distance"); } }
Possible Adapters public class TurkeyAdapter : Duck { private Turkey turkey; public TurkeyAdapter(Turkey turkey) { this.turkey = turkey; } public void quack() { turkey.gobble(); } public void fly() { for (int i = 0; i < 5; i++) { turkey.fly(); } } } public class DuckAdapter : Turkey { private Duck duck; public DuckAdapter(Duck duck) { this.duck = duck; } public void gobble() { duck.quack(); } public void fly() { duck.fly(); } } }
Calling Program static void Main(string[] args) { //Duck Test Drive MallardDuck duck = new MallardDuck(); WildTurkey turkey = new WildTurkey(); Duck turkeyAdapter = new TurkeyAdapter(turkey); Console.WriteLine("The TurkeyAdapter says..."); turkeyAdapter.quack(); turkeyAdapter.fly(); //TurkeyTestDrive Turkey duckAdapter = new DuckAdapter(duck); Console.WriteLine("The DuckAdapter says..."); duckAdapter.gobble(); duckAdapter.fly(); Console.ReadLine(); }
Class Implementation • Now we look at the class implementation of this problem
Duck Classes public interface iDuck { void quack(); void fly(); } public class MallardDuck : iDuck { public void quack() { Console.WriteLine("Quack"); } public void fly() { Console.WriteLine("I'm Flying Long Distance"); } }
Turkey Interface public interface iTurkey { void gobble(); void turkeyfly(); }
Duck Adapter public class DuckAdapter:MallardDuck,iTurkey { public void gobble() { quack(); } public void turkeyfly() { fly(); } }
Calling Program class Program { static void Main(string[] args) { DuckAdapter adapter = new DuckAdapter(); adapter.fly(); adapter.gobble(); Console.ReadLine(); } }