Java_J2EE_Job_Interview_Companion
.pdf260 How would you go about …?
}
double totalTax = 0.0;
Iterator it = listItems.iterator(); while (it.hasNext()) {
Item item = (Item) it.next(); totalTax += item.getExtendedTax();
}
return totalTax;
}
/**
* calculates total price on the items in the built basket
*/
public double calculateTotal() throws ItemException { if (listItems == null) {
throw new ItemException("No items in the basket");
}
double total = 0.0;
Iterator it = listItems.iterator(); while (it.hasNext()) {
Item item = (Item) it.next();
total += item.getExtendedTaxPrice();
}
return total;
}
/**
* prints individual prices of the items in the built basket
*/
public void printExtendedTaxedPrice() throws ItemException { if (listItems == null) {
throw new ItemException("No items in the basket");
}
double totalTax = 0.0;
Iterator it = listItems.iterator(); while (it.hasNext()) {
Item item = (Item) it.next();
System.out.println(item + "" + item.getExtendedTaxPrice());
}
}
public Iterator getIterator() { return listItems.iterator();
}
}
Finally, the calling-code, which makes use of our shopping basket builder pattern to build the shopping basket step-by- step and also calculates the taxes and the grand total for the items in the shopping basket.
…//package & import statements
public class Shopping { /**
*Class with main(String[] args) method which initially gets loaded by the
*class loader. Subsequent classes are loaded as they are referenced in the program.
*/
public static void main(String[] args) throws ItemException { process();
}
public static void process() throws ItemException {
//------creational patterns: singleton, factory method and builder design patterns------
System.out.println("----create a shopping basket with items ---"); //Shopping basket using the builder pattern
ItemBuilder builder = new ShoppingBasketBuilder(); //build basket of items using a builder pattern
builder.buildBasket(Item.TYPE_BOOK, "Book - IT", 1, 12.00); builder.buildBasket(Item.TYPE_CD, "CD - JAZZ", 1, 15.00); builder.buildBasket(Item.TYPE_COSMETICS, "Cosmetics - Lipstick", 1, 1.0);
//let’s print prices and taxes of this built basket double totalTax = builder.calculateTotalTax(); builder.printExtendedTaxedPrice(); System.out.println("Sales Taxes: " + totalTax);
262 |
How would you go about …? |
return s.startsWith(STARTS_WITH);
}
}
Scenario: The XYZ retail has decided to count the number of items, which satisfy the above strategies.
Solution: You can apply the decorator design pattern around your strategy design pattern. Refer Q24 in Java section for the decorator design pattern used in java.io.*. The decorator acts as a wrapper around the CheckStrategy objects where by call the check(…) method on the CheckStrategy object and if it returns true then increment the counter. The decorator design pattern can be used to provide additional functionality to an object of some kind. The key to a decorator is that a decorator "wraps" the object decorated and looks to a client exactly the same as the object wrapped. This means that the decorator implements the same interface as the object it decorates.
Decorator design pattern: You can think of a decorator as a shell around the object decorated. The decorator catches any message that a client sends to the object instead. The decorator may apply some action and then pass the message it received on to the decorated object. That object probably returns a value to the decorator which may again apply an action to that result, finally sending the (perhaps-modified) result to the original client. To the client the decorator is invisible. It just sent a message and got a result. However the decorator had two chances to enhance the result returned.
public class CountDecorator implements CheckStrategy {
private CheckStrategy cs = null; |
|
LongerThan15 |
«interface» |
* |
StartsWithCD |
private int count = 0; |
|
|
CheckStrategy |
|
|
public CountDecorator(CheckStrategy cs) { |
+check() : boolean |
+check() : boolean |
|
+check() : boolean |
|
|
|
||||
this.cs = cs; |
|
|
|
|
|
} |
|
|
CountDecorator |
|
|
|
|
|
|
|
|
public boolean check(String description) { |
|
|
|
|
|
boolean isFound = cs.check(description); |
|
+check() : boolean 1 |
|
|
|
if (isFound) |
|
|
|
|
|
this.count++; |
|
|
|
|
|
return isFound; |
There is a subtle difference between the decorator pattern and the proxy |
||||
} |
pattern is that, the main intent of the decorator pattern is to enhance the |
||||
public int count() { |
functionality of the target object whereas the main intent of the proxy pattern |
||||
is to control access to the target object. |
|
|
|||
return this.count; |
|
|
|
|
|
}
A decorator object’s interface must conform to the public void reset() { interface of the component it decorates
this.count = 0;
}
}
Now, let’s see the calling class Shopping:
//…. package & import statements
public class Shopping {
//...
public static void process() throws ItemException {
...
Iterator it = builder.getIterator(); boolean bol = false;
CheckStrategy strategy = null;
it = builder.getIterator(); //for starting with CD strategy = new StartsWithCD();
strategy = new CountDecorator(strategy); while (it.hasNext()) {
Item item = (Item) it.next();
bol = strategy.check(item.getDescription()); System.out.println("\n" + item.getDescription() + " --> " + bol);
}
System.out.println("No of descriptions starts with CD -->" +
((CountDecorator) strategy).count());
it = builder.getIterator();
|
How would you go about …? |
263 |
//longer |
than 15 charecters |
|
strategy |
= new LongerThan15(); |
|
strategy |
= new CountDecorator(strategy); |
|
while (it.hasNext()) { |
|
|
Item |
item = (Item) it.next(); |
|
bol = strategy.check(item.getDescription()); System.out.println("\n" + item.getDescription() + " --> " + bol);
}
System.out.println("No of descriptions longer than 15 characters -->" +
((CountDecorator) strategy).count());
}
}
Running the above code produces an output of:
----count |
---item description starting with 'cd' or longer than 15 characters |
||||
Book-------------------- |
- IT |
> false |
description satarting with cd ---------------------------- |
||
|
|
|
|||
CD - |
JAZZ |
--> true |
> false |
|
|
Cosmetics |
- Lipstick |
|
|
||
CD - |
JAZZ |
IMPORTED -- |
> true |
>2 |
|
No of descriptions starts with CD -- |
|
||||
Book------------------- |
- IT |
> false |
description longer than 15 characters ----------------------- |
||
|
|
|
|||
CD - |
JAZZ |
--> false |
> true |
|
|
Cosmetics |
- Lipstick |
|
|
||
CD - |
JAZZ |
IMPORTED -- |
> true |
|
>2 |
No of descriptions longer than 15 characters -- |
Scenario: So far so good, for illustration purpose if you need to adapt the strategy class to the CountDecorator class so that you do not have to explicitly cast your strategy classes to CountDecorator as shown in bold arrow in the class Shopping. We can overcome this by slightly rearranging the classes. The class CountDecorator has two additional methods count() and reset(). If you only just add these methods to the interface CheckStrategy then the classes LongerThan15 and StartsWithCD should provide an implementation for these two methods. These two methods make no sense in these two classes.
Solution: So, to overcome this you can introduce an adapter class named CheckStrategyAdapter, which just provides a bare minimum default implementation. Adapter design pattern
public interface CheckStrategy {
public boolean check(String word); public int count();
public void reset();
}
/**
*This is an adapter class which provides default implementations to be extended not to be used and
*facilitates its subclasses to be adapted to each other. Throws an unchecked exception to indicate
*improper use.
*/
public class CheckStrategyAdapter implements CheckStrategy { public boolean check(String word) {
throw new RuntimeException("Improper use of CheckStrategyAdapter
class method check(String word)" );
}
public int count() {
throw new RuntimeException("Improper use of CheckStrategyAdapter class method count()" );
}
public void reset() {
throw new RuntimeException("Improper use of CheckStrategyAdapter class method reset()" );
}
}
public class LongerThan15 extends CheckStrategyAdapter { public static final int LENGTH = 15;
public boolean check(String description) { if (description == null)
return false;
264 |
How would you go about …? |
else
return description.length() > LENGTH;
}
}
public class StartsWithCD extends CheckStrategyAdapter { public static final String STARTS_WITH = "cd";
public boolean check(String description) { String s = description.toLowerCase();
if (description == null || description.length() == 0) return false;
else
return s.startsWith(STARTS_WITH);
}
}
public class CountDecorator extends CheckStrategyAdapter {
private CheckStrategy cs = null; private int count = 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public CountDecorator(CheckStrategy cs) { |
|
|
|
|
|
«interface» |
|
|
|
|
|
|
|
|||||||
this.cs = cs; |
|
|
|
|
|
|
|
|
|
|
|
CheckStrategy |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
} |
|
|
|
|
|
|
|
|
1 |
|
|
+check() : boolean |
|
|
|
|
|
|
|
|
public boolean check(String description) { |
|
|
|
|
|
|
+count() : int |
|
|
|
|
|
|
|
||||||
|
1 |
|
|
|
|
+reset() |
|
|
|
|
|
|
|
|||||||
boolean isFound = cs.check(description); |
|
|
|
|
|
|
|
|
|
|
|
LongerThan15 |
|
|||||||
if (isFound){ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.count++; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CountDecorator |
|
|
CheckStrategyAdapter |
|
|
|
|
|
|
|||||||||
} |
|
|
|
|
|
|
|
+check() : boolean |
|
|||||||||||
return isFound; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StartsWuthCD |
|
|
|
|
+check() : boolean |
|
|
+check() : boolean |
|||||||||||||||
|
|
|||||||||||||||||||
public int count() { |
|
|
+count() : int |
|
|
+count() : int |
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
||||||||||||||
|
|
+reset() |
|
|
|
+reset() |
|
|
|
|
|
|
||||||||
return this.count; |
|
|
|
|
|
|
|
|
+check() : boolean |
|
|
|||||||||
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void reset() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
this.count = 0; |
Adapter provides default implementation, so that it can be extended to provide specific implementation. |
|||||||||||||||||||
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
Now, let’s see the revised calling class Shopping:
// |
...package |
& import statements |
|
|
|
|
public class |
Shopping { |
|
|
|
|
|
|
//...... |
|
|
|
|
|
|
public static void process() throws ItemException { |
|
|
|
||
|
//-------------------------- |
Strategy and decorator design pattern-------------------------- |
|
|||
|
System.out.println("-count item description starting with 'cd'or longer than 15 characters -"); |
|||||
|
Iterator |
it = builder.getIterator(); |
|
|
|
|
|
boolean bol = false; |
|
|
|
|
|
|
CheckStrategy strategy = null; |
description satarting with cd |
"); |
|||
|
System.out.println("---------------- |
|||||
|
it = builder.getIterator(); |
|
|
|
|
|
|
//for starting with CD |
|
|
|
|
|
|
strategy |
= new StartsWithCD(); |
|
|
|
|
|
strategy |
= new CountDecorator(strategy); |
|
|
|
|
|
while (it.hasNext()) { |
|
|
|
|
|
|
Item |
item = (Item) it.next(); |
|
|
|
|
|
bol = strategy.check(item.getDescription()); |
> " + bol); |
|
|||
|
System.out.println(item.getDescription() + " -- |
|
||||
|
} |
|
|
|
|
|
|
System.out.println("No of descriptions starts with CD -- |
>" + strategy.count()); |
|
|||
|
System.out.println("-------------- |
description longer than 15 characters ------------------ |
"); |
|||
|
it = builder.getIterator(); |
|
|
|
|
|
|
//longer |
than 15 charecters |
|
|
|
|
|
strategy |
= new LongerThan15(); |
|
|
|
|
|
strategy |
= new CountDecorator(strategy); |
|
|
|
|
|
|
How would you go about …? |
265 |
||
|
|
|
|
|
|
|
while |
(it.hasNext()) { |
|
|
|
|
|
Item item = (Item) it.next(); |
|
|
|
|||
bol = strategy.check(item.getDescription()); |
> " + bol); |
|
||||
System.out.println(item.getDescription() + " -- |
|
|||||
} |
|
|
|
|
|
>" + strategy.count()); |
System.out.println("No of descriptions longer than 15 characters -- |
||||||
} |
|
|
|
|
|
|
} |
|
|
|
|
|
|
The output is: |
|
|
|
|
|
|
|
|
|||||
----count |
item description starting with 'cd'or longer than 15 characters --- |
|||||
Book--------------------- IT |
> false |
description satarting with cd ---------------------------- |
|
|
||
|
|
|
|
|
||
CD - JAZZ |
--> true |
> false |
|
|
|
|
Cosmetics |
- Lipstick |
|
|
|
|
|
CD - JAZZ |
IMPORTED -- |
> true |
>2 |
|
|
|
No of descriptions starts with CD |
|
|
|
|||
------------------- |
|
description longer than 15 characters ----------------------- |
|
|||
Book - IT |
--> false |
|
|
|
|
|
CD - JAZZ |
--> false |
> true |
|
|
|
|
Cosmetics |
- Lipstick |
|
|
|
|
|
CD - JAZZ |
IMPORTED -- |
> true |
|
>2 |
|
|
No of descriptions longer than 15 characters -- |
|
|
Scenario: The XYZ Retail also requires a piece of code, which performs different operations depending on the type of item. If the item is an instance of CD then you call a method to print its catalog number. If the item is an instance of Cosmetics then you call a related but different method to print its color code. If the item is an instance of Book then you call a separate method to print its ISBN number. One way of implementing this is using the Java constructs instanceof and explicit type casting as shown below:
it = builder.getIterator();
while(it.hasNext(); ) { String name = null;
Item item = (Item)iter.next();
if(item instanceof CD) {
((CD) item). markWithCatalogNumber(); } else if (item instanceof Cosmetics) {
((Cosmetics) item). markWithColourCode (); } else if (item instanceof Book) {
((Book) item). markWithISBNNumber();
}
}
Problem: The manipulation of a collection of polymorphic objects with the constructs typecasts and instanceof as shown above can get messy and unmaintainable with large elseif constructs and these constructs in frequently accessed methods/ loops can adversely affect performance. Solution: You can apply the visitor design pattern to avoid using these typecast and “instanceof” constructs as shown below:
Visitor pattern: The visitor pattern makes adding new operations easy and all the related operations are localized in a visitor. The visitor pattern allows you to manipulate a collection of polymorphic objects without the messy and unmaintainable typecasts and instanceof operations. Visitor pattern allows you to add new operations, which affect a class hierarchy without having to change any of the classes in the hierarchy. For example we can add a GoodsDebugVisitor class to have the visitor just print out some debug information about each item visited etc. In fact you can write any number of visitor classes for the Goods hierarchy e.g. GoodsLabellingVisitor, GoodsPackingVisitor etc.
public interface Item { //...
public void accept(ItemVisitor visitor);
}
public interface ItemVisitor { public void visit (CD cd);
public void visit (Cosmetics cosmetics); public void visit (Book book);
}
268 |
How would you go about …? |
/**
* inner class which iterates over basket of items */
class ItemsIterator implements com.item.Iterator { private int current = 0;
private int step = 1;
public Item nextItem() { Item item = null; current += step;
if (!isDone()) {
item = (Item) listItems.get(current);
}
return item;
}
public Item previousItem() { Item item = null;
current -= step; if (!isDone()) {
item = (Item) listItems.get(current);
}
return item;
}
public Item firstItem() { current = 0;
return (Item) listItems.get(current);
}
public Item lastItem() {
current = listItems.size() - 1; return (Item) listItems.get(current);
}
public boolean isDone() {
return current >= listItems.size() ? true : false;
}
public Item currentItem() { if (!isDone()) {
return (Item) listItems.get(current);
}else { return null;
}
}
public void setStep(int step) { this.step = step;
}
}
}
Now, let’s see the calling code Shopping:
//… package & import statements
public class Shopping { //..
public static void process() throws ItemException {
//Iterator pattern example, inner implementations of ShopingBasketBuilder is protected. com.item.Iterator itemIterator = builder.getItemIterator();
//say we want to traverse through every second item in the basket itemIterator.setStep(2);
Item item = null;
for (item = itemIterator.firstItem(); !itemIterator.isDone(); item = itemIterator.nextItem()) { System.out.println("nextItem:" + item.getDescription() + "==>" + item.getExtendedTaxPrice());
}
item = itemIterator.lastItem();
System.out.println("lastItem: " + item.getDescription() + "==> " + item.getExtendedTaxPrice());
How would you go about …? |
269 |
item = itemIterator.previousItem();
System.out.println("previousItem:" + item.getDescription()+ "=>" + item.getExtendedTaxPrice());
}
}
The output is:
--------------- steps through every 2nd item in the basket -----------------------
nextItem: Book - IT ====> 12.0
nextItem: Cosmetics - Lipstick ====> 1.1 lastItem: CD - JAZZ IMPORTED ====> 17.25 previousItem : CD - JAZZ====>16.5
Scenario: The XYZ Retail buys the items in bulk from warehouses and sells them in their retail stores. All the items sold need to be prepared for retail prior to stacking in the shelves for trade. The preparation involves 3 steps for all types of items, i.e. adding the items to stock in the database, applying barcode to each item and finally marking retail price on the item. The preparation process is common involving 3 steps but each of these individual steps is specific to type of item i.e. Book, CD, and Cosmetics.
Solution: The above functionality can be implemented applying the template method design pattern as shown below:
Template method pattern: When you have a sequence of steps to be processed within a method and you want to defer some of the steps to its subclass then you can use a template method pattern. So the template method lets the subclass to redefine some of the steps.
Example Good example of this is the process() method in the Struts RequestProcessor class, which executes a
sequence of processXXXX(…) methods allowing the subclass to override some of the methods when required. Refer |
|||||||||||||||
Q110 in Enterprise section. |
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
<<abstract>> |
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
Goods |
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//... |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public abstract class Goods implements Item { |
|
+prepareItemForRetail() |
|
|
|
addToStock() --> abstract |
|||||||||
//... |
|
|
|
|
|
+addToStock() |
|
|
|
applyBarcode --> abstract |
|||||
/** |
|
|
|
|
|
+applyBarcode() |
|
|
|
markRetailPrice --> abstract |
|||||
|
|
|
|
|
|
|
|||||||||
|
|
|
|
|
+markRetailPrice() |
|
|
|
|
|
|
||||
* The template method |
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public void prepareItemForRetail() { |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
addToStock(); |
|
CD |
|
Book |
|
|
|
Cosmetics |
|
||||||
applyBarcode(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
markRetailPrice(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
+addToStock() |
|
+addToStock() |
|
+addToStock() |
|
|||||||
public abstract void addToStock(); |
|
|
+applyBarcode() |
|
+applyBarcode() |
|
+applyBarcode() |
|
|||||||
|
+markRetailPrice() |
|
+markRetailPrice() |
|
+markRetailPrice() |
|
|||||||||
public abstract void applyBarcode(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||
public abstract void markRetailPrice(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//.. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public class Book extends Goods { |
|
|
|
|
|
|
|
|
|
|
|
|
|
||
//.. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//following methods gets called by the template method
public void addToStock() {
//database call logic to store the book in stock table. System.out.println("Book added to stock : " + this.getDescription());
}
public void applyBarcode() {
//logic to print and apply the barcode to book.
System.out.println("Bar code applied to book : " + this.getDescription());
}
public void markRetailPrice() {
//logic to read retail price from the book table and apply the retail price. System.out.println("Mark retail price for the book : " + this.getDescription());
}
}