Tuesday, May 4, 2010

Mediator Pattern-1

The mediator pattern is a behavioral object design pattern. The mediator pattern centralizes communication between objects into a mediator object. This centralization is useful since it localizes in one place the interactions between objects, which can increase code maintainability, especially as the number of classes in an application increases. Since communication occurs with the mediator rather than directly with other objects, the mediator pattern results in a loose coupling of objects.
The classes that communicate with the mediator are known as Colleagues. The mediator implementation is known as the Concrete Mediator. The mediator can have an interface that spells out the communication with Colleages. Colleagues know their mediator, and the mediator knows its colleagues.
Now, let's look at an example of this pattern. We'll create a Mediator class (without implementing a mediator interface in this example). This mediator will mediate the communication between two buyers (a Swedish buyer and a French buyer), an American seller, and a currency converter.
The Mediator has references to the two buyers, the seller, and the converter. It has methods so that objects of these types can be registered. It also has a placeBid() method. This method takes a bid amount and a unit of currency as parameters. It converts this amount to a dollar amount via communication with the dollarConverter. It then asks the seller if the bid has been accepted, and it returns the answer.

Mediator.java

package com.cakes;

public class Mediator {

 Buyer swedishBuyer;
 Buyer frenchBuyer;
 AmericanSeller americanSeller;
 DollarConverter dollarConverter;

 public Mediator() {
 }

 public void registerSwedishBuyer(SwedishBuyer swedishBuyer) {
  this.swedishBuyer = swedishBuyer;
 }

 public void registerFrenchBuyer(FrenchBuyer frenchBuyer) {
  this.frenchBuyer = frenchBuyer;
 }

 public void registerAmericanSeller(AmericanSeller americanSeller) {
  this.americanSeller = americanSeller;
 }

 public void registerDollarConverter(DollarConverter dollarConverter) {
  this.dollarConverter = dollarConverter;
 }

 public boolean placeBid(float bid, String unitOfCurrency) {
  float dollarAmount = dollarConverter.convertCurrencyToDollars(bid, unitOfCurrency);
  return americanSeller.isBidAccepted(dollarAmount);
 }
}
Here is the Buyer class. The SwedishBuyer and FrenchBuyer classes are subclasses of Buyer. The buyer has a unit of currency as a field, and it also has a reference to the mediator. The Buyer class has a attemptToPurchase() method. This method submits a bid to the mediator's placeBid() method. It returns the mediator's response.

Buyer.java

package com.cakes;

public class Buyer {

 Mediator mediator;
 String unitOfCurrency;

 public Buyer(Mediator mediator, String unitOfCurrency) {
  this.mediator = mediator;
  this.unitOfCurrency = unitOfCurrency;
 }

 public boolean attemptToPurchase(float bid) {
  System.out.println("Buyer attempting a bid of " + bid + " " + unitOfCurrency);
  return mediator.placeBid(bid, unitOfCurrency);
 }
}
The SwedishBuyer class is a subclass of Buyer. In the constructor, we set the unitOfCurrency to be "krona". We also register the SwedishBuyer with the mediator so that the mediator knows about the SwedishBuyer object.

SwedishBuyer.java

package com.cakes;

public class SwedishBuyer extends Buyer {

 public SwedishBuyer(Mediator mediator) {
  super(mediator, "krona");
  this.mediator.registerSwedishBuyer(this);
 }
}
The FrenchBuyer class is similar to the SwedishBuyer class, except the unitOfCurrency is "euro", and it registers with the mediator as the FrenchBuyer.

FrenchBuyer.java

package com.cakes;

public class FrenchBuyer extends Buyer {

 public FrenchBuyer(Mediator mediator) {
  super(mediator, "euro");
  this.mediator.registerFrenchBuyer(this);
 }
}

In the constructor of the AmericanSeller class, the class gets a reference to the mediator and the priceInDollars gets set. This is the price of some good being sold. The seller registers with the mediator as the AmericanSeller. The seller's isBidAccepted() method takes a bid (in dollars). If the bid is over the price (in dollars), the bid is accepted and true is returned. Otherwise, false is returned.

AmericanSeller.java

package com.cakes;

public class AmericanSeller {

 Mediator mediator;
 float priceInDollars;

 public AmericanSeller(Mediator mediator, float priceInDollars) {
  this.mediator = mediator;
  this.priceInDollars = priceInDollars;
  this.mediator.registerAmericanSeller(this);
 }

 public boolean isBidAccepted(float bidInDollars) {
  if (bidInDollars >= priceInDollars) {
   System.out.println("Seller accepts the bid of " + bidInDollars + " dollars\n");
   return true;
  } else {
   System.out.println("Seller rejects the bid of " + bidInDollars + " dollars\n");
   return false;
  }
 }

}
The DollarConverter class is another colleague class. When created, it gets a reference to the mediator and registers itself with the mediator as the DollarConverter. This class has methods to convert amounts in euros and kronor to dollars.

DollarConverter.java

package com.cakes;

public class DollarConverter {

 Mediator mediator;

 public static final float DOLLAR_UNIT = 1.0f;
 public static final float EURO_UNIT = 0.7f;
 public static final float KRONA_UNIT = 8.0f;

 public DollarConverter(Mediator mediator) {
  this.mediator = mediator;
  mediator.registerDollarConverter(this);
 }

 private float convertEurosToDollars(float euros) {
  float dollars = euros * (DOLLAR_UNIT / EURO_UNIT);
  System.out.println("Converting " + euros + " euros to " + dollars + " dollars");
  return dollars;
 }

 private float convertKronorToDollars(float kronor) {
  float dollars = kronor * (DOLLAR_UNIT / KRONA_UNIT);
  System.out.println("Converting " + kronor + " kronor to " + dollars + " dollars");
  return dollars;
 }

 public float convertCurrencyToDollars(float amount, String unitOfCurrency) {
  if ("krona".equalsIgnoreCase(unitOfCurrency)) {
   return convertKronorToDollars(amount);
  } else {
   return convertEurosToDollars(amount);
  }
 }
}
The Demo class demonstrates our mediator pattern. It creates a SwedishBuyer object and a FrenchBuyer object. It creates an AmericanSeller object with a selling price set to 10 dollars. It then creates a DollarConverter. All of these objects register themselves with the mediator in their constructors. The Swedish buyer starts with a bid of 55 kronor and keeps bidding up in increments of 15 kronor until the bid is accepted. The French buyer starts bidding at 3 euros and keeps bidding in increments of 1.50 euros until the bid is accepted.

Demo.java

package com.cakes;

public class Demo {

 public static void main(String[] args) {

  Mediator mediator = new Mediator();

  Buyer swedishBuyer = new SwedishBuyer(mediator);
  Buyer frenchBuyer = new FrenchBuyer(mediator);
  float sellingPriceInDollars = 10.0f;
  AmericanSeller americanSeller = new AmericanSeller(mediator, sellingPriceInDollars);
  DollarConverter dollarConverter = new DollarConverter(mediator);

  float swedishBidInKronor = 55.0f;
  while (!swedishBuyer.attemptToPurchase(swedishBidInKronor)) {
   swedishBidInKronor += 15.0f;
  }

  float frenchBidInEuros = 3.0f;
  while (!frenchBuyer.attemptToPurchase(frenchBidInEuros)) {
   frenchBidInEuros += 1.5f;
  }

 }

}
The console output of the execution of Demo is shown here.

Console Output

Buyer attempting a bid of 55.0 krona
Converting 55.0 kronor to 6.875 dollars
Seller rejects the bid of 6.875 dollars

Buyer attempting a bid of 70.0 krona
Converting 70.0 kronor to 8.75 dollars
Seller rejects the bid of 8.75 dollars

Buyer attempting a bid of 85.0 krona
Converting 85.0 kronor to 10.625 dollars
Seller accepts the bid of 10.625 dollars

Buyer attempting a bid of 3.0 euro
Converting 3.0 euros to 4.285714 dollars
Seller rejects the bid of 4.285714 dollars

Buyer attempting a bid of 4.5 euro
Converting 4.5 euros to 6.4285717 dollars
Seller rejects the bid of 6.4285717 dollars

Buyer attempting a bid of 6.0 euro
Converting 6.0 euros to 8.571428 dollars
Seller rejects the bid of 8.571428 dollars

Buyer attempting a bid of 7.5 euro
Converting 7.5 euros to 10.714286 dollars
Seller accepts the bid of 10.714286 dollars
In this example of the mediator pattern, notice that all communication between our objects (buyers, seller, and converter) occurs via the mediator. The mediator pattern helps reduce the number of object references needed (via composition) as classes proliferate in a project as a project grows.

No comments:

Post a Comment