Adapter

There are two types of Adapter pattern. I have a clear preference for Object Adapters over Class Adapters where possible; I will explain this in detail later.

The Adapter pattern allows an existing class to be used with an interface that it doesn't match. It is often used to allow existing classes to work with others without needing to alter their source code.

This can be quite useful in a polymorphic setting where you are using third-party libraries, each with their own interface.

Fundamentally, an Adapter helps two incompatible interfaces work together. Otherwise incompatible classes can be made to work together by converting the interface of one class into an interface expected by the clients.

Class Adapter

In a Class Adapter, we use inheritance to create an adapter. A class (the adapter) can inherit another (the adaptee); using standard inheritance we are able to add additional functionality to the adaptee.

Let's suppose we have an ATM class, in our ATM.php file:

<?php 
 
class ATM 
{ 
  private $balance; 
 
  public function __construct(float $balance) 
  { 
    $this->balance = $balance; 
  } 
 
  public function withdraw(float $amount): float 
  { 
    if ($this->reduceBalance($amount) === true) { 
      return $amount; 
    } else { 
      throw new Exception("Couldn't withdraw money."); 
    } 
  } 
 
  protected function reduceBalance(float $amount): bool 
  { 
    if ($amount >= $this->balance) { 
      return false; 
    } 
 
    $this->balance = ($this->balance - $amount); 
    return true; 
  } 
 
  public function getBalance(): float 
  { 
    return $this->balance; 
  } 
} 

Let's create our ATMWithPhoneTopUp.php to form our adapter:

<?php 
 
class ATMWithPhoneTopUp extends ATM 
{ 
  public function getTopUp(float $amount, int $time): string 
  { 
    if ($this->reduceBalance($amount) === true) { 
      return $this->generateTopUpCode($amount, $time); 
    } else { 
      throw new Exception("Couldn't withdraw money."); 
    } 
  } 
 
  private function generateTopUpCode(float $amount, int $time): string 
  { 
    return $amount . $time . rand(0, 10000); 
  } 
} 

Let's wrap this all together in an index.php file:

<?php 
 
require_once('ATM.php'); 
 
$atm = new ATM(500.00); 
$atm->withdraw(50); 
echo $atm->getBalance(); 
echo "
"; 
 
require_once('ATMWithPhoneTopUp.php'); 
 
$adaptedATM = new ATMWithPhoneTopUp(500.00); 
echo "Top-up code: " . $adaptedATM->getTopUp(50, time()); 
echo "
"; 
echo $adaptedATM->getBalance(); 

Now that we have adapted our initial ATM class to yield top-up codes, we can now utilize this new top-up functionality. The output of all this is as follows:

450 
Top-up code: 5014606939121598 
450 

Note that if we wanted to adapt to multiple adaptees, this would be difficult in PHP.

In PHP, multiple inheritance isn't possible, unless you are working with Traits. In this case, we can only adapt one class to match the interface of another.

The other key architectural reason for us not using this approach is that it is often good design to prefer composition over inheritance (as described by the Composite Reuse Principle).

In order to explore this principle in more detail, we need to take a look at Object Adapters.

Object Adapter

The Composite Reuse Principle states that classes should achieve polymorphic behavior and code reuse by their composition.

By applying this principle, classes should contain instances of other classes when they want to implement a particular piece of functionality, as opposed to inheriting the functionality from a base or parent class.

For this reason, the Gang of Four stated the following:

"Favor 'object composition' over 'class inheritance'."

Why is this principle so vital? Consider our last example, where we used class inheritance; in such a case, there is no formal guarantee that our adapter would match the interface we want it to. What if the parent class exposed a function we didn't want the adapter to? Composition gives us more control.

By using composition over inheritance, we are able to better support the polymorphic behavior that is so vital in object-oriented programming.

Let's suppose we have a class to generate an insurance premium. It provides a monthly premium and an annual premium depending on how the customer wants to pay their premium. By paying annually, the customer gets a saving equivalent to half a month:

<?php 
 
class Insurance 
{ 
  private $limit; 
  private $excess; 
 
  public function __construct(float $limit, float $excess) 
  { 
    if ($excess >= $limit) { 
      throw New Exception('Excess must be less than premium.'); 
    } 
 
    $this->limit = $limit; 
    $this->excess = $excess; 
  } 
 
  public function monthlyPremium(): float 
  { 
    return ($this->limit-$this->excess)/200; 
  } 
 
  public function annualPremium(): float 
  { 
    return $this->monthlyPremium()*11.5; 
  } 
} 

Let's suppose a market comparison tool polymorphically uses classes such as the one mentioned earlier to actually go ahead and calculate insurance quotes from multiple different vendors; they use this interface to do this:

<?php 
 
interface MarketCompare 
{ 
  public function __construct(float $limit, float $excess); 
  public function getAnnualPremium(); 
  public function getMonthlyPremium(); 
} 

Accordingly, we can use this interface to build an Object Adapter to ensure our Insurance class, our premium generator, matches the interface that the market comparison tool is expecting:

<?php 
 
class InsuranceMarketCompare implements MarketCompare 
{ 
  private $premium; 
 
  public function __construct(float $limit, float $excess) 
  { 
    $this->premium = new Insurance($limit, $excess); 
  } 
 
  public function getAnnualPremium(): float 
  { 
    return $this->premium->annualPremium(); 
  } 
 
  public function getMonthlyPremium(): float 
  { 
    return $this->premium->monthlyPremium(); 
  } 
} 

Note how the class actually goes ahead and instantiates its own class for what it's trying to adapt.

The adapter then stores this class in a private variable. We then use this object in the private variable to proxy requests.

An Adapter, both a Class Adapter and an Object Adapter, should act as glue code. What I mean by that is that adapters shouldn't perform any calculations or computation, they merely act as a proxy between incompatible interfaces.

It is standard practice to keep logic out of our glue code and leave the logic down to the code that we are adapting. If, in doing this, we come up against the Single Responsibility Principle, we need to adapt another class.

As I mentioned earlier, adapting multiple classes isn't really possible in a Class Adapter, so you'd either have to wrap such logic in a Trait or we would need to use an Object Adapter, such as the one we're discussing here.

Let's try out this adapter. We'll do so by writing the following index.php file to see if our new class matches the expected interface:

<?php 
 
require_once('Insurance.php'); 
 
$quote = new Insurance(10000, 250); 
echo $quote->monthlyPremium(); 
echo "
"; 
 
require_once('MarketCompare.php'); 
require_once('InsuranceMarketCompare.php'); 
 
$quote = new InsuranceMarketCompare(10000, 250); 
echo $quote->getMonthlyPremium(); 
echo "
"; 
echo $quote->getAnnualPremium(); 

The output should look something like this:

48.75 
48.75 
560.625 

The key drawback of this method, compared to the Class Adapter method, is that we must implement common methods, even if those methods are merely forwarding methods.

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

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