It is not the strongest of the species that survives, nor the most intelligent that survives. It is the one that is the most adaptable to change.
The measure of intelligence is the ability to change.
Science always has its origin in the adaptation of thought to some definite field of experience.
This chapter is a two-for-one package: object and class Adapters. This chapter has many lessons, but one of the most interesting is seeing the difference between using inheritance and using composition. The class scope version of the Adapter design pattern uses inheritance, as can be seen in the class diagram in Figure 7-1.
In the class diagram, one implementation of the pattern is to have one class with dual inheritance. As you will see, dual inheritance is not allowed in PHP 5, but there are alternatives to dual inheritance where the pattern can be correctly implemented with a combination of inheritance and implementation.
As you know, an important dictum of design patterns is to favor
composition over inheritance. In looking at the second Adapter pattern,
the Adapter
participant uses composition to hold a
reference to the Adaptee
instead of inheritance.
Generally, composition is favored over inheritance because the binding between participants is looser and allows for the advantages of reuse, structure, and revision without the disadvantages that tight binding brings with inheriting from concrete classes or classes containing implemented methods.
Adapters are easy to understand; we use them all the time. Most familiar are electrical adapters that you use to charge your mobile phone. You can’t just plug your mobile phone into a standard outlet with your USB connector. Instead, you have to use an adapter that accepts the USB plug on one end and the electrical outlet on the other. Figure 7-3 shows a typical adapter arrangement used to recharge a mobile phone or computer.
In part, the answer to the question “When should I use an Adapter pattern?” is similar to the answer you would get when any adapter use comes into question. For example, if someone asked you how to recharge your mobile phone, you’d simply tell them to use an adapter so that the USB connecter and wall socket were compatible.
Breaking the problem down into its simplest parts, you have the following:
USB connector—unchanged
Standard wall socket—unchanged
You could get out your electrical tools and either change the USB connector or rewire the wall socket, but that would be a lot of extra work and you could possibly break the connector or socket (or both!). Or, you could get an adapter. Most likely you’ll get an adapter. The same is true with software development.
Suppose you and your fellow developers have worked out a PHP program that allows you to easily create custom desktop web designs for your clients. Your system handles everything from the look and feel of the site to a MySQL database. Everything works smoothly for a multicolumn design and UX for laptop and desktop size screens. In fact, you have what looks a lot like a desktop CMS created with PHP.
One day some clients ask you to add a mobile version of their website. Your designers tell you that a horizontal design is out for mobile, and you’ll have to redesign the look and feel. Likewise, your whole UX will have to be changed as well. After they whip up a mobile module, you find that it is incompatible with your desktop module. Figure 7-4 illustrates this problem.
Rather than changing the web development module that contains all of your classes and operations to link a web page with a database or change the new mobile module that has been painstakingly created for a mobile site, what would you do? The answer, just like when recharging your phone, is to use an adapter. In The Adapter Pattern Using Composition, you will find an example that reflects updating a system to include a module using the Adapter design pattern.
You will find a lot of uses for the Adapter pattern. Many web development professionals who use special vendor-supplied accelerators, UIs, or other enhancement modules often need an adapter of some sort to use with existing software. Likewise, if two different groups have developed incompatible modules, often it’s less time-consuming to use an adapter than to have one or the other group redevelop its modules. (You also might want to encourage your development groups to communicate a little better in the future!)
The class Adapter design pattern is relatively easy but less flexible than the object version of the Adapter. It is easier because it inherits its functionality from the Adaptee participant, and so there’s less to recode in the Adapter. Of course, given a concrete Adaptee from which the Adapter inherits, the binding is relatively tight, and so in creating an application using a class Adapter pattern, one needs to be especially cognizant of where the adaptation is going to be.
Because the class adapter has dual inheritance, as suggested in Figure 7-1, the PHP example is doing some fancy footwork. Natively, PHP does not support dual inheritance. (Don’t feel bad, neither does Java.) You can find elaborate examples of where PHP fakes or simulates dual inheritance, but a better lesson is that PHP natively handles a combination of interface implementation and class inheritance. The following statement illustrates the correct structure of a statement that inherits a class while simultaneously implementing an interface:
class ChildClass extends ParentClass implements ISomeInterface
So, when implementing the class Adapter pattern, one of the participants needs to be a PHP interface participant.
In looking for something both useful and minimal, a currency exchange/converter came to mind. Imagine a business website where the developer is selling both software services and products. Up to this point, all of the business was in the United States, where he could do all calculations in U.S. dollars. Without having to change a class that calculates how many dollars are involved in a transaction, the developer wants something that will handle exchanges in euros. By adding an Adapter, the program can calculate either dollars or euros.
To begin, suppose you have a perfectly good class, DollarCalc
, that adds the values of purchased
services and products and returns the total:
<?php //DollarCalc.php class DollarCalc { private $dollar; private $product; private $service; public $rate=1; public function requestCalc($productNow,$serviceNow) { $this->product=$productNow; $this->service=$serviceNow; $this->dollar=$this->product + $this->service; return $this->requestTotal(); } public function requestTotal() { $this->dollar*=$this->rate; return $this->dollar; } } ?>
In looking at the class, one of the properties is $rate
, and the requestTotal()
method calculates the value of
a transaction using $rate
. In this
original form, the value is set at 1, and multiplies the value of the
two parameters variable by 1, which has the effect of not multiplying
the total value at all. However, the $rate
variable is handy for providing
customers with discounts or adding surcharges for enhanced service or
products. This class is not part of the class Adapter pattern, but it is
a starting point.
The developer’s client announces that her company has decided to
expand to Europe and is going to kick things off small with entry into
the Luxembourg market. You quickly learn that Luxembourg is part of
the eurozone, and so you need to develop an application that does the
same calculations in euros. You decide that you can make it just like
DollarCalc
, and all you do is
change the names of the variables.
<?php //EuroCalc.php class EuroCalc { private $euro; private $product; private $service; public $rate=1; public function requestCalc($productNow,$serviceNow) { $this->product=$productNow; $this->service=$serviceNow; $this->euro=$this->product + $this->service; return $this->requestTotal(); } public function requestTotal() { $this->euro*=$this->rate; return $this->euro; } } ?>
Next, plug in the rest of your application to the EuroCalc
class and you’ll be good to go.
However, you realize that all of the data for your customer is in
dollars. In other words, you cannot “plug” in your system for the euro
without redoing your entire program. You don’t want to do that. You
need an adapter for your EuroCalc
.
At this point, we need to pause to get our bearings. Figure 7-5 shows the class diagram using the names of the classes in this implementation of the class Adapter pattern.
Just as you can get an adapter to fit into European sockets, you
can create an adapter for making your system work with euros.
Fortunately, the class Adapter pattern is designed for such a
situation. The first thing you need to do is to create an interface.
In the class diagram the interface is named ITarget
. It has a single method, requester()
. The requester()
is an abstract method that
leaves up to the implementation exactly what will be done with
it:
<?php //ITarget.php //Target interface ITarget { function requester(); } ?>
The developer can now implement the requester()
method to request euros instead
of dollars.
In the inheritance version of the Adapter design pattern, the
Adapter participant implements both the ITarget
interface and the concrete class
EuroCalc
. Not much is required to
create the EuroAdapter
. Most of the
work has been done in the EuroCalc
class, a concrete class whose interface it inherits. All that’s really
necessary is to implement the requester()
method so that it can translate
dollar values into euro values:
<?php //EuroAdapter.php //Adapter include_once('EuroCalc.php'); include_once('ITarget.php'); class EuroAdapter extends EuroCalc implements ITarget { public function __construct() { $this->requester(); } function requester() { $this->rate=.8111; return $this->rate; } } ?>
The class version of the Adapter pattern is one of the few design patterns where you will see one concrete class inherit from another concrete class. Almost all inheritance in design patterns is from an abstract class that allows the concrete classes to implement the abstract methods and properties as needed. In other words, when thinking about class inheritance, think concrete classes inherit from abstract classes.
By both implementation and extension, the EuroAdapter
class has the interfaces of both
the interface and concrete class. Using the requester()
method, it sets the rate value
(exchange rate) so that the adaptee’s functionality can be used
without having to change
it.
To see how it works, the Client
class makes
requests from both the EuroAdapter
and the DollarCalc
classes. As you
can see, the original DollarCalc
works fine, but it does not have the ITarget
interface. The EuroAdapter
class has both, and in using
type hinting, either would comply with its own interface:
<?php //Client.php //Client include_once('EuroAdapter.php'); include_once('DollarCalc.php'); class Client { private $requestNow; private $dollarRequest; public function __construct() { $this->requestNow=new EuroAdapter(); $this->dollarRequest=new DollarCalc(); $euro="€"; echo "Euros: $euro" . $this->makeAdapterRequest($this->requestNow) . "<br/>"; echo "Dollars: $" . $this->makeDollarRequest($this->dollarRequest); } private function makeAdapterRequest(ITarget $req) { return $req->requestCalc(40,50); } private function makeDollarRequest(DollarCalc $req) { return $req->requestCalc(40,50); } } $worker=new Client(); ?>
The output shows that the pattern can handle either dollars or euros now, thanks to the Adapter pattern:
Euros: €72.999
Dollars: $90
The calculation is a simple one, but with more complex calculations, the inheritance should provide the necessary interface and the implementation of the Target interface that sets up a class Adapter.
The object version of the Adapter pattern uses composition instead of inheritance, but it accomplishes the same goal. By comparing the two versions of the Adapter pattern, you can see the advantages and disadvantages of using one type or the other. The class version allows the Adapter to inherit most of the functionality it needs with only a slight tweaking by the interface. With the object version, the Adapter participant uses the Adaptee and implements the Target interface. In the class version, the Adapter is an Adaptee and implements the Target interface.
A common problem for PHP programmers is adapting to the mobile environment. Not too long ago, all you had to worry about was providing a site that would fit a range of desktop environments. A single layout worked for most desktops, and the designers made it look good. With mobile devices, designers and developers had to rethink not only the design elements of what a page shows in desktop and mobile environments, but also a way to switch from one to the other.
Using an object Adapter pattern, this example begins with a simple PHP class that brings up a simple page with text and graphics. For text, the example uses the placeholder text (“lorem ipsum”) as filler stored in a text file. The CSS is simple as well, setting up a text-graphic relationship of text-left and graphic-right in an elementary two-column design. The CSS is stored as a separate CSS file.
In order to understand the composition Adapter, you need to first look at the original class that later requires an adapter. It uses a straightforward but very loose interface:
<?php interface IFormat { public function formatCSS(); public function formatGraphics(); public function horizontalLayout(); } ?>
It allows for many CSS and graphics choices, but one method
implies a horizontal layout—something we know we don’t want
for a small mobile device. The implementation of the interface is
quite simple, as you can see in the following class, Desktop
:
<?php include_once("IFormat.php"); class Desktop implements IFormat { private $head="<!doctype html><html><head>"; private $headClose="<meta charset='UTF-8'> <title>Desktop</title></head><body>"; private $cap="</body></html>"; private $sampleText; public function formatCSS() { echo $this->head; echo "<link rel='stylesheet' href='desktop.css'>"; echo $this->headClose; echo "<h1>Hello, Everyone!</h1>"; } public function formatGraphics() { echo "<img class='pixRight' src='pix/fallRiver720.png' width='720' height='480' alt='river'>"; } public function horizontalLayout() { $textFile = "text/lorem.txt"; $openText = fopen($textFile, 'r'); $textInfo = fread($openText, filesize($textFile)); fclose($openText); $this->sampleText=$textInfo; echo "<div>" . $this->sampleText . "</div>"; echo "<p/><div>" . $this->sampleText . "</div>"; } public function closeHTML() { echo $this->cap; } } ?>
Most of the code is formatting an HTML page for viewing. The text and image, along with the header, represent minimum examples. The CSS has a stylesheet for color palette, the body text, a header, a “second column” in placing the image to the right of the text, and padding around the image (any CSS with a far more elaborate design for desktop works just as well, and you may want to substitute your own for this one):
@charset "UTF-8"; //desktop.css /* CSS Document */ /*DDDCC5,958976,611427,1D2326,6A6A61*/ @media only screen and (min-device-width : 800px) { } body { font-family:Verdana, Geneva, sans-serif; color:#1D2326; font-size:12px; background-color:#DDDCC5; } h1 { font-family:"Arial Black", Gadget, sans-serif; font-size:24px; color:#611427; } .pixRight { float:right; margin: 0px 0px 5px 5px; } image { padding: 10px 10px 10px 0px; }
The CSS is saved in a file named desktop.css and is called through a
PHP-generated HTML <link>
tag.
Figure 7-6 shows the output of the PHP.
As you can see in Figure 7-6, the whole thing is way too wide for a small mobile device. So the goal will be to take the same content and adapt it for a mobile design.
To get started, take a look at Figure 7-7, which shows
the actual class names and operations that will be used in creating
a mobile version of the website. I added the original interface for
the Mobile
class (IMobileFormat
), but given the way in which
the MobileAdapter
is used, it
implements the IFormat
interface.
In looking at the IMobileFormat
and the IFormat
interfaces, you can see that they
are incompatible: one has a method horizontalLayout()
and the other a
verticalLayout()
interface.
However, using the MobileAdapter
(Adapter), which inherits IFormat
(Target) originally used by the Desktop
class, the Mobile
(Adaptee) class can now interact
compatibly with other implementations of the IFormat
interface.
Not surprisingly, the Adapter pattern is also known as the
wrapper pattern. The Adapter (MobileAdapter
) participant “wraps” the
Adaptee (Mobile
) participant so
that the Adaptee can use the same interface as itself. To see how
the wrapping process works, start with the IMobileFormat
interface and the Mobile
class, which is almost identical to
the Desktop
class except for the
fact that it has a different interface:
<?php interface IMobileFormat { public function formatCSS(); public function formatGraphics(); public function verticalLayout(); } ?>
Keep in mind that this process is designed to let two
incompatible interfaces work together. The differences are small,
but as you know, when converting from a desktop to a mobile design
the key difference is that one can be organized horizontally into
columns and the other uses vertical organization. The IMobileFormat
is implemented by the
following Mobile
class (it looks
almost identical to the Desktop
class):
<?php include_once('IMobileFormat.php'); class Mobile implements IMobileFormat { private $head="<!doctype html><html><head>"; private $headClose="<meta charset='UTF-8'> <title>Mobile</title></head><body>"; private $cap="</body></html>"; private $sampleText; public function formatCSS() { echo $this->head; echo "<link rel='stylesheet' href='mobile.css'>"; echo $this->headClose; echo "<h1>Hello, Everyone!</h1>"; } public function formatGraphics() { echo "<img src='pix/fallRiver960.png' width=device-width height=device-height alt='river'>"; } public function verticalLayout() { $textFile = "text/lorem.txt"; $openText = fopen($textFile, 'r'); $textInfo = fread($openText, filesize($textFile)); fclose($openText); $this->sampleText=$textInfo; echo "<p/><div>" . $this->sampleText . "</div>"; echo "<p/><div>" . $this->sampleText . "</div>"; } public function closeHTML() { echo $this->cap; } } ?>
As noted, it looks a lot like the Desktop
class, but the graphics are set up
differently, with a different graphic. It also calls a different CSS
file. The CSS file includes an @media
statement to provide different
resolutions flexibility:
@charset "UTF-8"; /* CSS Document */ /*DDDCC5,958976,611427,1D2326,6A6A61*/ @media only screen and (min-device-width : 640px) and (max-device-width : 960px) { img { max-width: 100%; } } body { font-family:Verdana, Geneva, sans-serif; color:#1D2326; font-size:24px; background-color:#DDDCC5; } h1 { font-family:"Arial Black", Gadget, sans-serif; font-size:48px; color:#611427; } image { padding: 5px 5px 5px 0px; }
The CSS, while important as far as the looks go, is used here only for formatting the appearance of the page on mobile devices. A designer could do a lot more. What’s important is that different classes for desktop and mobile appearance need to work together. So now you can see the key Adapter participant that brings them all together:
<?php include_once("IFormat.php"); include_once("Mobile.php"); class MobileAdapter implements IFormat { private $mobile; public function __construct(IMobileFormat $mobileNow) { $this->mobile = $mobileNow; } public function formatCSS() { $this->mobile->formatCSS(); } public function formatGraphics() { $this->mobile->formatGraphics(); } public function horizontalLayout() { $this->mobile->verticalLayout(); } } ?>
As you can see, the MobileAdapter
is instantiated with a
Mobile object instance. Note also that the IMobileFormat
is used in type hinting to
ensure that the argument is a Mobile object. What makes the Adapter
participant interesting is how it implements the horizontalLayout()
method to include the
Mobile
object verticalLayout()
method. In fact, all of
the MobileAdapter
methods wrap a
Mobile
method. It just so happens
that one of the methods in the adapter participant is not in the
interface of the adapter (verticalLayout()
). They could all be
different, and the adapter would just wrap them in one of the
methods in its own interface (IFormat
).
The final step is to launch the application using the
MobileAdapter
class. Keep in mind
that the Client
class is an
integral part of the design pattern, and even though it is just making
requests, it must do so in a way that is consistent with the intent
and design of the Adapter pattern:
<?php include_once('Mobile.php'); include_once('MobileAdapter.php'); class Client { private $mobile; private $mobileAdapter; public function __construct() { $this->mobile = new Mobile(); $this->mobileAdapter = new MobileAdapter($this->mobile); $this->mobileAdapter->formatCSS(); $this->mobileAdapter->formatGraphics(); $this->mobileAdapter->horizontalLayout(); $this->mobile->closeHTML(); } } $worker=new Client(); ?>
The Client
class in the
Adapter design must wrap an instance of the Adaptee (Mobile
) to help integrate it into the
Adapter itself. The Client
instantiates the Adapter using the
Adaptee as a parameter in the Adapter instantiation. So the client
must create an Adaptee object
first (new Mobile()
) and then an
instance of the Adapter (new
MobileAdapter($this->mobile
).
The Client
class makes most
of the requests through the MobileAdapter
instance. However, to close up
the code, it uses the instance of the Mobile
class.
Because the application requires no special implementation of the
closeHTML()
method, the Client
launches the method directly from the
Mobile
instance.
Figure 7-8 shows the same content configured for a mobile device. In this example the device is an iPhone.
A lot has been written about how to design and configure mobile web applications, and any design can be used with the Adapter pattern. The point is that starting with a Desktop design (of clearly humble origins), the Adapter was able to pick up and use a different design appropriate for mobile devices without disrupting any implementation of the original desktop design.
PHP programmers live with change. Different versions of
PHP change, new functionalities are added and others taken away.
Likewise, big and small changes in PHP sometimes accompany MySQL
changes. For example, the mysql
extension was upgraded to mysqli
, and
PHP developers needed to adapt to the new API in mysqli
. Would an Adapter design be appropriate
here? Probably not. Depending on how your program is configured, an
Adapter would or would not work. All of the connecting and interaction
code could be rewritten, but that’s not exactly what the Adapter pattern
is supposed to do. That’s like rewiring your USB connector to plug into
a standard wall socket. However, if all of your old mysql
code existed in modules, you could
change the module (class) and swap in a new module with the same
interface but using mysqli
instead of
mysql
. I don’t think a swap is the
same as an adapter, but it’s in the same spirit. In an Adapter, nothing
in the original code changes. All code changes are in the
Adapter.
Where the Adapter is most useful is where two incompatible interfaces need to be used together. The Adapter “marries” the interfaces. You can think of the Adapter as a marriage counselor; it resolves the differences by creating a common interface. It saves the separate parts from wholly rewriting themselves so that they can function together.