Software design patterns

profilelazy808
week5_dp.pdf

Design Patterns EECS 3311

Song Wang [email protected]

eecs.yorku.ca/~wangsong/

Overview of Last Lectures

▪ Introduction to Design Patterns ▪ Pattern’s Elements

▪ Types of Design Patterns

▪ Java Design Patterns ▪ The Singleton Pattern

▪ The Factory Pattern

▪ The Builder Pattern

▪ The Prototype Pattern

• Only your first Attempt counts

• Average of Quiz 4 is 76.9%

Quiz 4

70

72

74

76

78

80

82

84

86

88

Quiz 1 Quiz 2 Quiz 3 Quiz 4

Average

Average

Acknowledge

• Some of the covered materials are based on SOEN 344 at Concordia, SE463 at UW, SENG321 at UVic, CSE331 at University of Washington and previous EECS3311 offerings:

• Nikolaos Tsantalis, Jo Atlee, Marty Stepp, Mike Godfrey, Jonathan S. Ostroff, M. Ernst, S. Reges, D. Notkin, R. Mercer, Davor Svetinovic, Jack Jiang, Jackie Wang

Outlines

▪ Java Design Patterns ▪ The Adapter Pattern

▪ The Bridge Pattern

▪ The Composite Pattern

▪ Visitor Design Pattern

▪ Iterator Pattern

▪ State Design Pattern

▪ Event-driven pattern

▪ Observer design patterns

Pattern: Adapter

▪ Structural patterns describe how classes and objects can be combined to

form larger structures.

▪ The difference between class patterns and object patterns is that class patterns

describe how inheritance can be used to provide more useful program

interfaces.

▪ Object patterns, on the other hand, describe how objects can be composed into

larger structures using object composition, or the inclusion of objects within

other objects.

▪ The Structural patterns are:

▪ Adapter

▪ Bridge

▪ Composite

▪ Proxy

▪ Flyweight

▪ Facade

▪ Decorator

Structural Patterns

▪ The Adapter pattern can be used to make one class interface

match another to make programming easier.

▪ The Bridge pattern separates an object’s interface from its

implementation, so you can vary them separately.

▪ The Composite pattern is a composition of objects, each of which

may be either simple or itself a composite object.

▪ The Proxy pattern is frequently a simple object that takes the

place of a more complex object that may be invoked later, for

example when the program runs in a network environment.

Structural Patterns (cont.)

▪ The Flyweight pattern is a pattern for sharing objects, where each

instance does not contain its own state, but stores it externally.

This allows efficient sharing of objects to save space, when there

are many instances, but only a few different types.

▪ The Facade pattern is used to make a single class represent an

entire subsystem.

▪ The Decorator pattern, which can be used to add responsibilities

to objects dynamically.

Structural Patterns (cont.)

▪ Adapters are used to enable objects with different interfaces to

communicate with each other.

▪ The Adapter pattern is used to convert the programming interface

of one class into that of another. We use adapters whenever we

want unrelated classes to work together in a single program.

▪ Adapters come in two flavors, object adapters and class

adapters.

▪ The concept of an adapter is: we write a class that has the desired

interface and then make it communicate with the class that has a

different interface.

▪ Adapters in Java can be implemented in two ways: by

inheritance, and by object composition.

Adapter Pattern: Definition & Applicability

▪ Object adapters use a compositional technique to adapt one interface to

another.

▪ The adapter inherits the target interface that the client expects to see, while it

holds an instance of the adaptee.

▪ When the client calls the request() method on its target object (the adapter), the

request is translated into the corresponding specific request on the adaptee.

▪ Object adapters enable the client and the adaptee to be completely decoupled

from each other. Only the adapter knows about both of them.

Adapter Pattern: Object Adapters

/**

* The SquarePeg class.

* This is the Target class.

*/

public class SquarePeg {

public void insert(String str) {

System.out.println("SquarePeg insert():

" + str);}

}

/**

* The RoundPeg class.

* This is the Adaptee class.

*/

public class RoundPeg {

public void insertIntoHole(String msg) {

System.out.println("RoundPeg insertIntoHole(): " + msg);}

}

If a client only understands the SquarePeg interface for inserting pegs using the insert() method, how can it insert round pegs, which are pegs, but that are inserted differently, using the insertIntoHole() method?

Adapter Pattern: Example

Solution:

Design a RoundToSquarePeg adapter that enables to insertIntoHole() a RoundPeg object connected to the adapter to be inserted as a SquarePeg, using insert().

/** * The RoundToSquarePegAdapter class. * This is the Adapter class. * It adapts a RoundPeg to a SquarePeg. * Its interface is that of a SquarePeg. */ public class RoundToSquarePegAdapter extends SquarePeg {

private RoundPeg roundPeg; public RoundToSquarePegAdapter(RoundPeg peg) { //the roundPeg is plugged into the adapter this.roundPeg = peg;}

public void insert(String str) { //the roundPeg can now be inserted in the same manner as a squarePeg! roundPeg.insertIntoHole(str);}

}

Adapter Pattern: Example

Example: // Test program for Pegs.

public class TestPegs {

public static void main(String args[]) {

// Create some pegs.

RoundPeg roundPeg = new RoundPeg();

SquarePeg squarePeg = new SquarePeg();

// Do an insert using the square peg.

squarePeg.insert("Inserting square peg...");

// Now we'd like to do an insert using the round peg.

// But this client only understands the insert()

// method of pegs, not a insertIntoHole() method.

// The solution: create an adapter that adapts

// a square peg to a round peg!

RoundToSquarePegAdapter adapter = new RoundToSquarePegAdapter(roundPeg);

adapter.insert("Inserting round peg...");}

}

Execution trace: SquarePeg insert(): Inserting square peg...

RoundPeg insertIntoHole(): Inserting round peg...

Adapter Pattern: Example

▪ Class adapters use multiple inheritance to achieve their goals.

▪ As in the object adapter, the class adapter inherits the interface of the client's

target. However, it also inherits the interface of the adaptee as well.

▪ Since Java does not support true multiple inheritance, this means that one of

the interfaces must be inherited from a Java Interface type.

▪ Both of the target or adaptee interfaces could be Java Interfaces.

▪ The request to the target is simply rerouted to the specific request that was

inherited from the adaptee interface.

Adapter Pattern: Class Adapters

Here are the interfaces for round and square pegs:

/**

*The IRoundPeg interface.

*/

public interface IRoundPeg {

public void insertIntoHole(String msg);

}

/**

*The ISquarePeg interface.

*/

public interface ISquarePeg {

public void insert(String str);

}

Adapter Pattern: Example

Here are the new RoundPeg and SquarePeg classes. These are essentially the same as before except they now implement the appropriate interface.

// The RoundPeg class.

public class RoundPeg implements IRoundPeg {

public void insertIntoHole(String msg) {

System.out.println("RoundPeg insertIntoHole(): " + msg);}

}

// The SquarePeg class.

public class SquarePeg implements ISquarePeg {

public void insert(String str) {

System.out.println("SquarePeg insert(): " + str);}

}

Adapter Pattern: Example

And here is the new PegAdapter class:

/**

* The PegAdapter class.

* This is the two-way adapter class.

*/

public class PegAdapter implements ISquarePeg, IRoundPeg {

private RoundPeg roundPeg;

private SquarePeg squarePeg;

public PegAdapter(RoundPeg peg) {

this.roundPeg = peg;}

public PegAdapter(SquarePeg peg) {

this.squarePeg = peg;}

public void insert(String str) {

roundPeg.insertIntoHole(str);}

public void insertIntoHole(String msg){

squarePeg.insert(msg);}

}

Adapter Pattern: Example

A client that uses the two-way adapter: // Test program for Pegs.

public class TestPegs {

public static void main(String args[]) {

// Create some pegs.

RoundPeg roundPeg = new RoundPeg();

SquarePeg squarePeg = new SquarePeg();

// Do an insert using the square peg.

squarePeg.insert("Inserting square peg...");

// Create a two-way adapter and do an insert with it.

ISquarePeg roundToSquare = new PegAdapter(roundPeg);

roundToSquare.insert("Inserting round peg...");

// Do an insert using the round peg.

roundPeg.insertIntoHole("Inserting round peg...");

// Create a two-way adapter and do an insert with it.

IRoundPeg squareToRound = new PegAdapter(squarePeg);

squareToRound.insertIntoHole("Inserting square peg...");}

}

Adapter Pattern: Example

Client program output:

SquarePeg insert(): Inserting square peg...

RoundPeg insertIntoHole(): Inserting round peg...

RoundPeg insertIntoHole(): Inserting round peg...

SquarePeg insert(): Inserting square peg...

Adapter Pattern: Example

Class and object adapters have different trade-offs.

A class adapter:

▪ adapts Adaptee to Target by committing to a concrete Adapter class;

▪ lets Adapter override some of Adaptee's behavior, since Adapter is a

subclass of Adaptee;

▪ introduces only one object, and no additional indirection is needed to get

to the adaptee.

An object adapter

▪ lets a single Adapter work with many Adaptees - that is, the Adaptee itself

and all of its subclasses (if any). The Adapter can also add functionality to all

Adaptees at once.

▪ makes it harder to override Adaptee behavior. It will require subclassing

Adaptee and making Adapter refer to the subclass rather than the Adaptee

itself.

Adapter Pattern: Consequences of the Adapter Pattern

Pattern: Bridge

The Bridge pattern is used to separate the interface of class from its implementation, so that either can be varied separately.

▪ At first sight, the bridge pattern looks much like the Adapter pattern, in that a class is used to convert one kind of interface to another. However, the intent of the Adapter pattern is to make one or more classes’ interfaces look the same as that of a particular class.

▪ The Bridge pattern is designed to separate a class’ interface from its implementation, so that you can vary or replace the implementation without changing the client code.

Example:

Suppose that we have a program that displays a list of products in a window. The simplest interface for that display is a simple JList box. But, once a significant number of products have been sold, we may want to display the products in a table along with their sales figures.

The Bridge Pattern Definition & Applicability

Following the example from the previous slide, suppose that we need to make

some changes in the way these lists display data. So, rather than deriving new

classes whenever we need to change these displays further, let’s build a single

bridge that does this work for us.

Building the Bridge Pattern

We want the bridge class to return an appropriate visual component, so we will

extend the Java JSrollPane class:

public class ListBridge extends JScrollPane

{…}

When we design a bridge class, we have to decide how the bridge will determine

which of the several classes it will instantiate. This could be decided based on the

values or quantity of data to be displayed, or based on some simple constants.

Here we define the two constants inside the ListBridge class:

static public final int TABLE = 1, LIST = 2;

Building the Bridge Pattern

The constructor of the ListBridge class:

public ListBridge(Vector v, int table_type)

{

Vector sort = sortVector(v); //sort the vector

if (table_type == LIST)

getViewport().add(makeList(sort)); //make table

if (table_type == TABLE)

getViewport().add(makeTable(sort)); //make list

}

Building the Bridge Pattern

We can use the JTable and JList classes directly without modification and thus

can put any adapting interface computations in the data models that construct the

data for the list and table.

private JList makeList(Vector v) {

return new JList(new ListModel(v));

}

//---------------------------------

private JTable makeTable(Vector v) {

return new JTable(new TableModel(v));

}

Where ListModel and TableModel are Java API classes.

Building the Bridge Pattern

public class ListBridge extends JScrollPane{

static public final int TABLE = 1, LIST = 2;

private JList makeList(Vector v) {

return new JList(new ListModel(v));

}

private JTable makeTable(Vector v) {

return new JTable(new TableModel(v));

}

public ListBridge(Vector v, int table_type) { //constructor

Vector sort = sortVector(v); //sort the vector

if (table_type == LIST)

getViewport().add(makeList(sort)); //make

table

if (table_type == TABLE)

getViewport().add(makeTable(sort)); //make

list

}

}

The Bridge Pattern Class

▪ The Bridge pattern is intended to keep the interface to your client

program constant while allowing you to change the actual kind of

class you display or use.

▪ This can prevent you from recompiling a complicated set of user

interface modules, and only require that you recompile the bridge

itself and the actual end display class.

▪ You can extend the implementation class and the bridge class

separately, and usually without much interaction with each other.

Consequences of the Bridge Pattern

Pattern: Composite

The Composite Design pattern allows a client object to treat both

single components and collections of components identically.

Composite patterns are often used to represent recursive data

structures. The recursive nature of the Composite structure naturally

gives way to recursive code to process that structure.

Use the Composite pattern when:

▪ You want to represent part-whole hierarchies of objects.

▪ You want clients to be able to ignore the difference between

compositions of objects and individual objects. Clients will

treat all objects in the composite structure uniformly.

The Composite Pattern Definition & Applicability

In the UML class diagram below:

▪ The Client uses an abstract component, AComponent, for some abstract task,

operation().

▪ At run-time, the Client holds a reference to a concrete component such as

Leaf1 or Leaf2.

▪ When the operation task is requested by the Client, the specific concrete

behavior with the particular concrete component will be performed.

The Composite Pattern Example

▪ The Composite pattern allows you to define a class hierarchy of

simple objects and more complex composite objects so that they

appear to be the same to the client program.

▪ Because of this simplicity, the client can be that much simpler,

since nodes and leaves are handled in the same way.

▪ The Composite pattern also makes it easy for you to add new

kinds of components to your collection, as long as they support a

similar programming interface.

▪ The composite is essentially a singly-linked tree, in which any of

the objects may themselves be additional composites.

Consequences of the Composite Pattern