Streams

InputStreamReader reader = new InputStreamReader(System.in); 
BufferedReader console = new BufferedReader(reader);

Decorator Pattern: Input Streams

Name in Design Pattern
Actual Name (input streams)
Component Reader
ConcreteComponent InputStreamReader
Decorator BufferedReader
method()
read

How to Recognize Patterns

Litmus Test

.

Litmus Test

  1. Component objects can be decorated (visually or behaviorally enhanced)
    PASS
  2. The decorated object can be used in the same way as the undecorated object
    PASS
  3. The component class does not want to take on the responsibility of the decoration
    FAIL--the component class has setBorder method
  4. There may be an open-ended set of possible decorations

Putting Patterns to Work

Bundles

Bundles

.

Discounted Items

Discounted Items

.

Model/View Separation

Change Listeners

Change Listeners

Observing the Invoice

.

Iterating Through Invoice Items

Iterators

Iterators


.

Formatting Invoices

Formatting Invoices

Formatting Invoices

.

Formatting Invoices

.

Contents:


Bundle.java


import java.util.*;

/**
   A bundle of items that is again an item.
*/
public class Bundle implements LineItem
{
   /**
      Constructs a bundle with no items.
   */
   public Bundle() { items = new ArrayList(); }

   /**
      Adds an item to the bundle.
      @param item the item to add
   */
   public void add(LineItem item) { items.add(item); }

   public double getPrice() 
   {
      double price = 0;
      for (int i = 0; i < items.size(); i++)
      {
         LineItem item = (LineItem) items.get(i);
         price += item.getPrice();
      }
      return price; 
   }

   public String toString()
   {
      String description = "Bundle: ";
      for (int i = 0; i < items.size(); i++)
      {
         if (i > 0) description += ", ";
         LineItem item = (LineItem) items.get(i);
         description += item.toString();
      }
      return description;
   } 

   private ArrayList items;
}


DiscountedItem.java


/**
   A decorator for an item that applies a discount.
*/
public class DiscountedItem implements LineItem
{
   /**
      Constructs a discounted item.
      @param item the item to be discounted
      @param discount the discount percentage
   */
   public DiscountedItem(LineItem item, double discount) 
   { 
      this.item = item; 
      this.discount = discount;
   }

   public double getPrice() 
   {
      return item.getPrice() * (1 - discount / 100); 
   }

   public String toString()
   {
      return item.toString() + " (Discount " + discount
         + "%)";
   } 

   private LineItem item;
   private double discount;
}


Invoice.java


import java.util.*;
import javax.swing.event.*;

/**
   An invoice for a sale, consisting of line items.
*/
public class Invoice
{
   /**
      Constructs a blank invoice.
   */
   public Invoice()
   {
      items = new ArrayList();
      listeners = new ArrayList();
   }

  /**
      Adds an item to the invoice.
      @param item the item to add
   */
   public void addItem(LineItem item) 
   { 
      items.add(item); 
      // notify all observers of the change to the invoice
      ChangeEvent event = new ChangeEvent(this);
      for (int i = 0; i < listeners.size(); i++)
      {
         ChangeListener listener 
            = (ChangeListener) listeners.get(i);
         listener.stateChanged(event);
      }
   }

   /**
      Adds a change listener to the invoice.
      @param listener the change listener to add
   */
   public void addChangeListener(ChangeListener listener) 
   { 
      listeners.add(listener); 
   }

   /**
      Gets an iterator that iterates through the items.
      @return an iterator for the items
   */
   public Iterator getItems()
   {
      return new
         Iterator()
         {
            public boolean hasNext() 
            { 
               return current < items.size(); 
            }
            public Object next() 
            { 
               Object r = items.get(current);
               current++; 
               return r;
            }
            public void remove()
            {
               throw new UnsupportedOperationException();
            }
            private int current = 0;
         };
   }

   public String format(InvoiceFormatter formatter)
   {
      String r = formatter.formatHeader();
      Iterator iter = getItems(); 
      while (iter.hasNext())
      {
         LineItem item = (LineItem) iter.next();
         r += formatter.formatLineItem(item);
      }
      return r + formatter.formatFooter();
   }

   private ArrayList items;
   private ArrayList listeners;
}



/**
   This interface describes the tasks that an invoice
   formatter needs to carry out.
*/
public interface InvoiceFormatter
{
   /**
      Formats the header of the invoice.
      @return the invoice header
   */
   String formatHeader();
   /**
      Formats a line item of the invoice.
      @return the formatted line item
   */
   String formatLineItem(LineItem item);
   /**
      Formats the footer of the invoice.
      @return the invoice footer
   */
   String formatFooter();
}


LineItem.java


/**
   A line item in an invoice.
*/
public interface LineItem
{
   /**
      Gets the price of this line item.
      @return the price
   */
   double getPrice();
   /**
      Gets the description of this line item.
      @return the description
   */   
   String toString();
}


Product.java


/**
   A product with a price and description.
*/
public class Product implements LineItem
{
   /**
      Constructs a product.
      @param description the description
      @param price the price
   */
   public Product(String description, double price)
   {
      this.description = description;
      this.price = price;
   }
   public double getPrice() { return price; }
   public String toString() { return description; }
   private String description;
   private double price;
}


SimpleFormatter.java


/**
   A simple invoice formatter.
*/
public class SimpleFormatter implements InvoiceFormatter
{
   public String formatHeader()
   {
      total = 0;
      return "     I N V O I C E\n\n\n";
   }

   public String formatLineItem(LineItem item)
   {
      total += item.getPrice();
      return item.toString() + ": $" 
         + item.getPrice() + "\n";
   }

   public String formatFooter()
   {
      return "\n\nTOTAL DUE: $" + total + "\n";
   }
   
   private double total;
}


InvoiceTest.java


import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

/**
   A program that tests the invoice classes.
*/
public class InvoiceTest
{
   public static void main(String[] args)
   {
      final Invoice invoice = new Invoice();
      final InvoiceFormatter formatter = new SimpleFormatter();

      // this text area will contain the formatted invoice
      final JTextArea textArea = new JTextArea(20, 40);

      // when the invoice changes, update the text area
      ChangeListener listener = new
         ChangeListener()
         {
            public void stateChanged(ChangeEvent event)
            {
               textArea.setText(invoice.format(formatter));
            }
         };
      invoice.addChangeListener(listener);

      // add line items to a combo box
      final JComboBox combo = new JComboBox();   
      Product hammer = new Product("Hammer", 19.95);
      Product nails = new Product("Assorted nails", 9.95);
      combo.addItem(hammer);
      Bundle bundle = new Bundle();
      bundle.add(hammer);
      bundle.add(nails);
      combo.addItem(new DiscountedItem(bundle, 10));

      // make a button for adding the currently selected
      // item to the invoice
      JButton addButton = new JButton("Add");
      addButton.addActionListener(new
         ActionListener()
         {
            public void actionPerformed(ActionEvent event)
            {
               LineItem item = (LineItem) combo.getSelectedItem();
               invoice.addItem(item);
            }
         });

      // put the combo box and the add button into a panel
      JPanel panel = new JPanel();
      panel.add(combo);
      panel.add(addButton);

      // add the text area and panel to the content pane
      JFrame frame = new JFrame();
      Container contentPane = frame.getContentPane();
      contentPane.add(new JScrollPane(textArea), 
         BorderLayout.CENTER);
      contentPane.add(panel, BorderLayout.SOUTH);
      frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      frame.pack();
      frame.setVisible(true);   
   }
}


Results


Maintained by John Loomis, last updated 25 February 2007