Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Ganesh_JavaSE7_Programming_1z0-804_study_guide.pdf
Скачиваний:
94
Добавлен:
02.02.2015
Размер:
5.88 Mб
Скачать

Chapter 5 Object-Oriented Design Principles

The Factory Design Pattern

In real life, factories are manufacturing units that produce multiple instances of a product (or flavors of a product). For instance, a car factory produces cars of specific types and models. The main responsibility of the factory is to keep producing cars of the required type and model. Here, one important thing to observe is that a car may have different variants and the car factory should be able to manufacture on demand the required variants of the same car.

Similarly, you can implement a factory that returns the required type of object(s) on demand in OOP. In this case, the factory decides which class(es) to instantiate to create the required object(s) and exactly how to create them.

Figure 5-2 shows the UML class diagram of the factory pattern. Client invokes ProductFactory to get an appropriate object from the product hierarchy. ProductFactory creates one of the Products from the Product hierarchy based on the provided information. Client uses the product object without knowing which actual product it is using or how ProductFactory created this product object.

 

Uses

<<interface>>

Client

Product

 

ProductFactory

creates

ConcreteProduct

 

Uses

Figure 5-2.  UML class diagram of factory design pattern

Let’s consider an example. In the FunPaint application, there are different types of shapes, such as Circle and Rectangle. Now, the Canvas object might not want to know about the concrete shape that gets created. Assume that the Canvas object receives a shape identifier from the front end based on which corresponding shape object needs to be created. Here, you can have a ShapeFactory that can create the required shape object and return it to the Canvas object. Listing 5-12 shows the implementation of this pattern.

Listing 5-12.  Test.java

// Shape.java

public interface Shape { public void draw();

public void fillColor();

}

// Circle.java

public class Circle implements Shape { private int xPos, yPos; private int radius;

public Circle(int x, int y, int r) { xPos = x;

yPos = y;

radius = r;

System.out.println("Circle constructor");

}

132

Chapter 5 Object-Oriented Design Principles

@Override

public void draw() { System.out.println("Circle draw()"); // draw() implementation

}

@Override

public void fillColor() {

// fillColor() implementation

}

}

// Rectangle.java

public class Rectangle implements Shape {

public Rectangle(int length, int height) { this.length = length;

this.height = height; System.out.println("Rectangle constructor");

}

private int length, height; @Override

public void draw() { System.out.println("Rectangle draw()"); // draw() implementation

}

@Override

public void fillColor() {

// fillColor() implementation

}

}

// ShapeFactory.java public class ShapeFactory {

public static Shape getShape(String sourceType) { switch(sourceType) {

case "Circle":

return new Circle(10, 10, 20); case "Rectangle":

return new Rectangle(10, 20);

}

return null;

}

}

// Canvas.java

import java.util.ArrayList; import java.util.Iterator;

133

Chapter 5 Object-Oriented Design Principles

public class Canvas {

private ArrayList<Shape> shapeList = new ArrayList<Shape>(); public void addNewShape(String shapeType) {

Shape shape = ShapeFactory.getShape(shapeType); shapeList.add(shape);

}

public void redraw() {

Iterator<Shape> itr = shapeList.iterator(); while(itr.hasNext()) {

Shape shape = itr.next(); shape.draw();

}

}

}

// Test.java public class Test {

public static void main(String[] args) { Canvas canvas = new Canvas(); canvas.addNewShape("Circle"); canvas.addNewShape("Rectangle"); canvas.redraw();

}

}

It prints the following:

Circle constructor Rectangle constructor Circle draw() Rectangle draw()

Let‘s analyze this implementation. You define a Shape interface, which defines two public methods, draw() and fillColor(). Classes Circle and Rectangle implement this interface and provide implementation of interface methods. Class Canvas maintains a list of shapes drawn on canvas, and it offers a method addNewShape() to allow the front-end of the application to create a new instance of requested shape. From the main() method, you invoke the addnewShape() method of Canvas class. In turn, this method calls the getShape() method of ShapeFactory class. The getShape() method examines the requested type of shape, creates a new instance based on the requested type, and returns it to Canvas.

The following insights may be drawn from the above example:

The Canvas class does not need to know how to create concrete shape objects.

This transparency becomes very useful in case concrete object creation is expensive and complicated.

The Canvas class does not need to know the exact concrete shape types. You can observe from the Canvas implementation that Canvas is only aware of the Shape interface. Therefore, if you add another concrete shape (say Square), you do not need to change the Canvas implementation.

Java SDK defines many such factories. For example, java.util.Calendar is an implementation of the factory design pattern. Listing 5-13 uses the Calendar class.

134

Chapter 5 Object-Oriented Design Principles

Listing 5-13.  mainClass.java

import java.util.Calendar;

public class MainClass {

public static void main(String[] args) {

Calendar calendar = Calendar.getInstance(); System.out.println(calendar);

}

}

This program prints the following:

java.util.GregorianCalendar [...]

The output of Listing 5-13 contains all the fields of the calendar object (shown here as […] to save the space). There are many other examples in Java SDK where the factory pattern is used, including the following:

createStatement() of java.sql.Connection interface, which creates a new Statement to communicate to database.

createSocket() of java.rmi.server.RmiClientSocketFactory interface, which returns a new client Socket.

Differences Between Factory and Abstract Factory Design Patterns

Both factory design patterns and abstract factory design patterns belong to the creational design pattern category. As explained in preceding section, a factory design pattern creates (or manufactures) the requested type of object on demand. By contrast, the abstract factory is basically a factory of factories. In other words, the abstract factory design pattern introduces one more indirection to create a specified object. A client of the abstract factory design pattern first requests a proper factory from the abstract factory object, and then it requests an appropriate object from the factory object.

Another very important difference between these two patterns is their applicability: when you have only one type of object to be created, you can use a factory design pattern; when you have a family of objects to be created, you can use an abstract factory design pattern.

Let’s explore the abstract factory pattern by reference to its class diagram (Figure 5-3). Assume that there are two product hierarchies, ProductA and ProductB, along with their concrete product classes. You want to create either ConcreteProductA1 and ConcreteProductB1 (as a group) or ConcreteProductA2 and ConcreteProductB2. In this situation, you define an abstract ProductFactory with two subclasses, ProductFactory1 and ProductFactory2.

ProductFactory1 creates ConcreteProductA1 and ConcreteProductB1 and ProductFactory2 creates

ConcreteProductA2 and ConcreteProductB2. Hence, based on the requirement, you create a required factory object; the selected factory object gives you the required concrete product objects.

135

Chapter 5 Object-Oriented Design Principles

 

 

Client

 

 

ProductA

 

 

ProductB

 

uses

 

uses

uses

 

 

 

 

 

 

 

 

 

 

ConcreteProductA1

ConcreteProductA2

ProductFactory

ConcreteProductB1

ConcreteProductB2

 

 

+createProductA()

 

 

 

 

+createProductB()

 

 

 

ConcreteFactory1

ConcreteFactory2

 

 

creates

 

 

creates

 

Figure 5-3.  UML class diagram for abstract factory design pattern

Let’s work out an example now. In Listing 5-12, you implemented the factory design pattern. Now, let’s assume that shapes can be of two types: DisplayFriendly and PrinterFriendly. Hence, now there are two flavors available for your Circle class (as well as for your Rectangle class, mutatis mutandis): DisplayFriendlyCircle and PrinterFriendlyCircle. Now, obviously you want to create only one type of objects: either display-friendly or

printer-friendly. Well, you’ve established that when you want to create a family of objects you should use the abstract factory design pattern. Listing 5-14 shows the implementation.

Listing 5-14.  Test.java

// Shape.java

public interface Shape { public void draw();

}

// PrinterFriendlyShape.java

public interface PrinterFriendlyShape extends Shape {

}

// DisplayFriendlyShape.java

public interface DisplayFriendlyShape extends Shape {

}

// DisplayFriendlyCircle.java

public class DisplayFriendlyCircle implements DisplayFriendlyShape { private int xPos, yPos;

private int radius;

public DisplayFriendlyCircle(int x, int y, int r) { xPos = x;

yPos = y;

136

Chapter 5 Object-Oriented Design Principles

radius = r;

System.out.println("DisplayFriendlyCircle constructor");

}

@Override

public void draw() { System.out.println("DisplayFriendlyCircle draw()"); // draw() implementation

}

}

// DisplayFriendlyRectangle.java

public class DisplayFriendlyRectangle implements DisplayFriendlyShape { public DisplayFriendlyRectangle(int length, int height) {

this.length = length; this.height = height;

System.out.println("DisplayFriendlyRectangle constructor");

}

private int length, height; @Override

public void draw() { System.out.println("DisplayFriendlyRectangle draw()"); // draw() implementation

}

}

// PrinterFriendlyCircle.java

public class PrinterFriendlyCircle implements PrinterFriendlyShape{ private int xPos, yPos;

private int radius;

public PrinterFriendlyCircle(int x, int y, int r) { xPos = x;

yPos = y; radius = r;

System.out.println("PrinterFriendlyCircle constructor");

}

@Override

public void draw() { System.out.println("PrinterFriendlyCircle draw()"); // draw() implementation

}

}

// PrinterFriendlyRectangle.java

public class PrinterFriendlyRectangle implements PrinterFriendlyShape { public PrinterFriendlyRectangle(int length, int height) {

this.length = length; this.height = height;

System.out.println("PrinterFriendlyRectangle constructor");

}

private int length, height; @Override

137

Chapter 5 ObjeCt-Oriented design prinCiples

public void draw() { System.out.println("PrinterFriendlyRectangle draw()"); // draw() implementation

}

}

// ShapeFactory.java

public interface ShapeFactory {

public Shape getShape(String sourceType);

}

// DisplayFriendlyFactory.java

public class DisplayFriendlyFactory implements ShapeFactory { @Override

public Shape getShape(String sourceType) { switch(sourceType){

case "Circle":

return new DisplayFriendlyCircle(10, 10, 20); case "Rectangle":

return new DisplayFriendlyRectangle(10, 20);

}

return null;

}

}

// PrinterFriendlyFactory.java

public class PrinterFriendlyFactory implements ShapeFactory { @Override

public Shape getShape(String sourceType) { switch(sourceType) {

case "Circle":

return new PrinterFriendlyCircle(10, 10, 20); case "Rectangle":

return new PrinterFriendlyRectangle(10, 20);

}

return null;

}

}

// Test.java public class Test {

public static void main(String[] args) { Canvas canvas = new Canvas();

canvas.addNewShape("Circle", "DisplayFriendly"); canvas.addNewShape("Rectangle", "DisplayFriendly"); canvas.redraw();

}

}

138

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]