Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Professional Java.JDK.5.Edition (Wrox)

.pdf
Скачиваний:
31
Добавлен:
29.02.2016
Размер:
12.07 Mб
Скачать

Chapter 3

Of course, the Seller and Buyer have differing objectives. The Seller is looking to make a profit, setting a 20 percent profit margin on any products sold. The following code makes that assumption:

package wrox.pattern.strategy; public class Seller implements Role {

/*

*Seller will be happy if they make 20% profit on whatever they sell.

*(non-Javadoc)

*@see wrox.Pattern.strategy.Role#isSatisfied(wrox.Pattern.strategy.Product, double)

*/

public boolean isSatisfied(Product product, double price) { if (price - product.getCost() > product.getCost() * .2) {

return true;

}else { return false;

}

}

}

The Seller, on the other hand, is looking for a product that is within a spending limit. It is important to note that the Buyer class is not limited to the methods described by the Role interface, making it possible to establish the limit member variable in the Buyer class that is not present in the Seller class.

The algorithm for what is acceptable is an arbitrary part of this example, but it is set so that the Buyer cannot spend above the chosen limit and will not pay more that 200 percent of the initial product cost. The role of Buyer is expressed in the isSatisfied()method:

package wrox.Pattern.strategy; public class Buyer implements Role {

private double limit;

public Buyer(double limit) { this.limit= limit;

}

/*

*The buyer is happy if he can afford the product,

*and the price is less then 200% over cost.

*@see wrox.Pattern.strategy.Role#isSatisfied(wrox.Pattern.strategy.Product, double)

*/

public boolean isSatisfied(Product product, double price) { if ( price < limit && price < product.getCost() * 2 ) {

return true;

}else {

return false;

}

}

}

136

Exploiting Patterns in Java

The code example that follows uses a class for the abstraction of a product. It’s a data object that is part of the scenario. The code is as follows:

package wrox.pattern.strategy; public class Product {

private String name; private String description; private double cost;

public Product(String name, String description, double cost) { this.name = name;

this.description = description; this.cost = cost;

}

// Setters and Getter Omitted.

The next section looks at the class that uses the pluggable strategy.

Context

Next, let’s look at the Person class that manages the Role objects. First, the Person class has an association with the Role interface. In addition, it is important to note that there is a setter and getter for the Role. This allows the person’s roles to change as the program executes. It’s also much cleaner code. This example uses two roles: Buyer and Seller. In the future, other Role implementing objects such as Wholesaler, Broker, and others can be added because there is no dependency to the specific subclasses:

package wrox.pattern.strategy;

public class Person { private String name; private Role role;

public Person(String name) { this.name= name;

}

public Role getRole() { return role;

}

public void setRole(Role role) { this.role= role;

}

Another key point is that the satisfied method of the Person class delegates the Role specific behavior to its Role interface. Polymorphism allows the correct underlying object to be chosen:

public boolean satisfied(Product product, double offer) { return role.isSatisfied(product, offer);

}

}

Now, the code of the pattern has been implemented. Next, lets view what behavior an application can exhibit by implementing this pattern. To start, you can establish Products, People, and Roles:

137

Chapter 3

package wrox.pattern.strategy;

public class Person {

// previous methods omitted.

public static void main(String[] args) {

Product house= new Product(“house”, “4 Bedroom North Arlington”, 200000); Product condo= new Product(“condo”, “2 Bedroom McLean”, 100000);

Person tim= new Person(“Tim”);

Person allison= new Person(“Allison”);

You are buying and selling houses. The next step is to establish initial roles and assign the roles to the people. The people will then exhibit the behavior of the role they have been assigned:

tim.setRole(new Buyer(500000)); allison.setRole(new Seller());

if (!allison.satisfied(house, 200000)) {

System.out.println(“offer of 200,000 is no good for the seller”);

}

if (!tim.satisfied(house, 600000)) {

System.out.println(“offer of 600,000 is no good for the buyer”);

}

if (tim.satisfied(house, 390000) && allison.satisfied(house, 390000)) { System.out.println(“They Both agree with 390,000 “);

To further demonstrate the capabilities of the Strategy pattern, switch the initial Seller to the Buyer by calling setRole()on the Person object. It is possible to switch to a Buyer without modifying the Person object:

allison.setRole(new Buyer(190000));

if (allison.satisfied(condo, 110000)) { System.out.println(“As a buyer she can afford the condo “);

}

}

}

}

By implementing the Strategy pattern, it is possible to change an object’s behavior on the fly with no affect on its implementation. This is a very powerful tool in software design. In the next section, the composite patterns will build on the same principle of abstracting behavior to treat a class hierarchy with a single common interface.

Composite

The Composite design pattern allows you to treat a collection of objects as if they were one thing. In this way you can reduce the complexity of the code required if you were going to handle collections as special cases. Figure 3-16 shows the structure of the composite pattern in conjunction with the classes implementing the pattern in this example.

138

Exploiting Patterns in Java

Component

*

 

 

Asset

*

+operation()

 

 

+fairMarketValue()

 

Leaf

Composite

 

Stock

CompositeAsset

+add()

1

 

+add()

1

 

 

 

 

Portfolio

MutualFund

Figure 3-16

The example used here to demonstrate this behavior is a portfolio management system that consists of stocks and mutual funds. A mutual fund is a collection of stocks, but you would like to apply a common interface to both stocks and mutual funds to simplify the handling of both. This allows you to perform operations such as calculate Fair Market Value, buy, sell, and assess percent contribution with a common interface. The Composition pattern would clearly reduce the complexity of building these operations. The pattern consists of the classes, a leaf, and composite. Figure 3-16 should look similar to the example built in the earlier section of this chapter.

Component

First is the component interface; it declares the common interface that both the single and composite nodes will implement. The example is using fairMarketValue, an operation that can be calculated over stocks, mutual funds, and portfolios:

package wrox.pattern.composite;

public interface Asset {

public double fairMarketValue();

}

Leaf

The leaf class represents the singular atomic data type implementing the component interface. In this example, a Stock class will represent the leaf node of the pattern. The Stock class is a leaf node in that it does not hold a reference to any other Asset objects:

139

Chapter 3

package wrox.pattern.composite;

public class Stock implements Asset {

private String name; private double price; private double quantity;

public Stock(String name, double price, double quantity) { this.name= name;

this.price= price; this.quantity= quantity;

}

Stock price is calculated by multiplying share price times quantity:

public double fairMarketValue() {

return price * quantity;

}

}

Composite

The following section declares the Composite object called CompositeAsset. Notice that CompositeAsset is declared abstract. A valid composite asset, such as a mutual fund or portfolio, extends this abstract class:

package wrox.pattern.composite; import java.util.ArrayList; import java.util.Iterator; import java.util.List;

public abstract class CompositeAsset implements Asset { private List assets= new ArrayList();

public void add(Asset asset) { assets.add(asset);

}

Iterate through the child investments. If one of the child investments happens to also be a composite asset, it will be handled recursively without requiring a special case. So, for example, it would be possible to have a mutual fund comprising mutual funds:

public double fairMarketValue() { double total = 0;

for (Iterator i= assets.iterator(); i.hasNext(); ) { Asset asset= (Asset)i.next();

total = total + asset.fairMarketValue();

}

return total;

}

}

140

Exploiting Patterns in Java

Once that is complete, what follows is to build the concrete composite objects: MutualFund and Portfolio. Nothing significant is required for the Mutual Fund class; its behavior is inherited from the

CompositeAsset:

package wrox.pattern.composite;

public class MutualFund extends CompositeAsset{

private String name;

public MutualFund(String name) { this.name = name;

}

}

The Portfolio class extends CompositeAsset as well; the difference is that it calls the super class directly and modifies the resulting calculation for fair market. It subtracts a 2 percent management fee:

package wrox.pattern.composite;

public class Portfolio extends CompositeAsset { private String name;

public Portfolio(String name) { this.name= name;

}

/* Market value - Management Fee

* @see wrox.Pattern.composite.CompositeAsset#fairMarketValue() */

public double fairMarketValue() {

return super.fairMarketValue() - super.fairMarketValue() * .02;

}

}

The only thing left to do is exercise the code. The next class is of an Investor. The Investor is the client code taking advantage of the Composite design pattern:

package wrox.pattern.composite;

public class Investor { private String name; private Portfolio porfolio;

public Investor(String name, Portfolio portfolio) { this.name= name;

this.porfolio= portfolio;

}

By calling the fair market value on the investor’s portfolio, the Composite pattern will be able to traverse the collection of stocks and mutual funds to determine the value of the whole thing without worrying about the object structure:

141

Chapter 3

public double calcNetworth( ){

return porfolio.fairMarketValue();

}

public static void main(String[] args) {

Portfolio portfolio= new Portfolio(“Frequently Used Money”); Investor investor= new Investor(“IAS”, portfolio);

portfolio.add(new Stock(“wrox”, 450, 100));

MutualFund fund= new MutualFund(“Don Scheafer’s Intellectual Capital”); fund.add(new Stock(“ME”, 35, 100) );

fund.add(new Stock(“CV”, 22, 100) ); fund.add(new Stock(“BA”, 10, 100) ); portfolio.add(fund);

double total =investor.calcNetworth();

System.out.println(“total =” + total);

}

}

With the composite pattern, it is very easy to simplify operations over complex data structures.

Summar y

This chapter gave you a strong appreciation of the value of patterns in developing Java solutions. They are critical in learning from the experience of others, but also in understanding APIs used by the Java platform.

In this chapter, you learned about patterns, why they’re important, tricks to understanding them, and several important patterns in Java programming.

Now that you have learned how to think like a Java developer, the rest of the book will focus on practical examples of developing Java solutions. These chapters will not be comprehensive examinations of the technologies in each chapter, but rather a real-life example of a development problem, which is solved using various technologies.

The first chapter in this new phase of the book is Chapter 4, “Developing Effective User Interfaces with JFC.” In this chapter, you will learn how to use Swing to build Java desktop applications.

142

Developing Effective User

Interfaces with JFC

Java Foundation Classes (JFC) is a package of libraries for developing robust graphical user displays for client-side applications that can be implemented on enterprise systems. The JFC API libraries comprise five different components:

AWT. The Abstract Windowing Toolkit (AWT) classes are comprised of legacy graphics code from Java 1.x that were developed to create simple user interfaces for applications and applets.

Accessibility. The Accessibility classes accommodate assistive technologies that provide access to information in user interface components.

Java 2D. The Java 2D classes contain a broad set of advanced graphics APIs that allow users to create and manipulate image, shape, and text components.

Drag and Drop. The Drag and Drop classes allow users to initiate drag operations so that components can be dropped on designated target areas. This is accomplished by setting up a drop target listener to handle drop events and a management object to handle drag and drop operations.

Swing. The Swing classes are built atop of the AWT classes to provide high-quality GUI components for enterprise applications.

Large tomes have been written about JFC, specifically Swing libraries and their advanced presentation features, with numerous pages of APIs affiliated with those libraries that could easily be acquired by your Integrated Development Environment (IDE) or the Internet during your development activities. Along with those library presentations were some simple applications that provided little instructional value other than to demonstrate how things work in a basic fashion. Rather than getting bogged down with a recital of those voluminous API’s, this chapter will concentrate the discussion on many of the Swing features that you will need to incorporate into your professional development activities to be successful. You’ll learn advanced GUI applications that

Chapter 4

combine multiple layout managers to achieve relevant presentation applications that manage data and navigation flow in an efficient manner. All of the sample applications incorporate listeners and their interfaces to manage events generated by users in their navigation activities along with Gang of Four (GoF) design patterns to promote best practices in your modeling and implementation operations.

This chapter starts by demonstrating some foundation knowledge about layout managers so that you can conceptualize Swing layout designs from a high-level perspective and then implement them using JFC libraries in an efficient manner. With a solid handle on what these libraries can do for you, you will be able to approach your development tasks with greater confidence, which will result in more germane product development. The next two sections of this chapter will cover some practical applications, the first being an Annotation Editor that links meta data to passages in a text file followed by an illustration of how an Installation Wizard can easily be crafted with JFC libraries and GoF design patterns to manage navigation flows and data persistence.

Layout Managers

Layout managers are used in Java Swing applications to arrange objects when they are added to a Container object. The setLayout() method is used to override default layout managers appropriated to JPanel (FlowLayout) and JFrame (BorderLayout) containers.

This section of the chapter will discuss seven important layout managers:

BorderLayout

BoxLayout

CardLayout

FlowLayout

GridbagLayout

GridLayout

SpringLayout

All of these layout managers will be covered at length within interesting Swing applications that implement listeners to react to user selections on various visualization components. Most importantly, these applications will demonstrate how the different layout managers can be amalgamated to craft relevant GUI presentations.

BorderLayout

The BorderLayout manager is the default layout for a frame. A BorderLayout uses five regions in its display space. Those regions are generally referred to as: NORTH, SOUTH, WEST, EAST, and CENTER. Those regions generally refer to the same attributes that a map would use. The NORTH and SOUTH regions extend to the top and bottom areas of the Container, while the EAST and WEST regions extend from the bottom of the NORTH and top of the SOUTH regions and to the left and right sides of the Container, respectively. The CENTER region occupies all of the residual space the remains in the center of the Container.

144

Developing Effective User Interfaces with JFC

The BorderLayout manager is typically generated by instantiating a new BorderLayout class with a constructor that has no parameters, or with a constructor that specifies two integer values that specify the horizontal and vertical pixels between components in this fashion: new BorderLayout(int hGap, int vGap).

The constructor methods for the BorderLayout manager are shown in the method summary table that follows.

Method

Description

 

 

 

public

BorderLayout()

No parameters

public

BorderLayout(int hGap, int vGap)

The hGap and vGap integer parameters

 

 

specify the horizontal and vertical pix-

 

 

els between components

 

 

 

The following BorderLayout example emulates a test application that quizzes the user with five fairly simple arithmetic queries. As a user sequentially steps through the test questions, a progress bar will track where the test taker is with respect to the end of the test and what the running score is. Figure 4-1 provides a model of the application and shows how the different GUI components will occupy the

BorderLayout panel.

BorderLayout

BorderLayout.NORTH

BorderLayout.EAST

BorderLayout.CENTER

JProgressBar

Command pattern: execute()

ButtonGroup

JFrame

InfoButton

Figure 4-1

The BorderLayoutPanel application will incorporate the Command pattern to handle button requests for quiz questions and the answers to those questions by the user. Some of the benefits and drawbacks of the Command pattern are outlined in the table that follows.

145

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