正文
设计模式教程(Design Patterns Tutorial)笔记之二 结构型模式(Structural Patterns)
小程序:扫一扫查出行
【扫一扫了解最新限行尾号】
复制小程序
【扫一扫了解最新限行尾号】
复制小程序
目录
· Decorator
· What is the Decorator Design Pattern?
· Sample Code
· Adapter
· What is the Adapter Design Pattern?
· Sample Code
· Facade
· What is the Facade Design Pattern?
· Sample Code
· Bridge
· What is the Bridge Design Pattern?
· When to use the Bridge Design Pattern?
· Sample Code
· Flyweight
· What is the Flyweight Design Pattern?
· Sample Code
· Proxy
· What is the Proxy Design Pattern?
· Sample Code
DecoratorWhat is the Decorator Design Pattern?
• The Decorator allows you to modify an object dynamically.
• You would use it when you want the capabilities of inheritance with subclasses, but you need to add functionality at run time.
• It is more flexible than inheritance.
• Simplifies code because you add functionality using many simple classes.
• Rather than rewrite old code you can extend with new code.
Sample Code
• Pizza.java
// Blueprint for classes that will have decorators public interface Pizza { public String getDescription(); public double getCost(); } /*
public abstract class Pizza{ public abstract void setDescription(String newDescription);
public abstract String getDescription(); public abstract void setCost(double newCost);
public abstract double getCost(); // I could use getter and setter methods for every
// potential Pizza topping }
*/
• ThreeCheesePizza.java
// By going this route I'll have to create a new subclass
// for an infinite number of pizza.
// I'd also have to change prices in many classes
// when just 1 Pizza topping cost changes // Inheritance is static while composition is dynamic
// Through composition I'll be able to add new functionality
// by writing new code rather than by changing current code public class ThreeCheesePizza extends Pizza{ private String description = "Mozzarella, Fontina, Parmesan Cheese Pizza";
private double cost = 10.50; public void setDescription(String newDescription) { description = newDescription; } public String getDescription() { return description; } public void setCost(double newCost) { cost = newCost; } public double getCost() { return cost; }
}
• PlainPizza.java
// Implements the Pizza interface with only the required
// methods from the interface // Every Pizza made will start as a PlainPizza public class PlainPizza implements Pizza { public String getDescription() { return "Thin dough"; } public double getCost() { System.out.println("Cost of Dough: " + 4.00); return 4.00; } }
• ToppingDecorator.java
// Has a "Has a" relationship with Pizza. This is an
// Aggregation Relationship abstract class ToppingDecorator implements Pizza { protected Pizza tempPizza; // Assigns the type instance to this attribute
// of a Pizza // All decorators can dynamically customize the Pizza
// instance PlainPizza because of this public ToppingDecorator(Pizza newPizza){ tempPizza = newPizza; } public String getDescription() { return tempPizza.getDescription(); } public double getCost() { return tempPizza.getCost(); } }
• Mozzarella.java
public class Mozzarella extends ToppingDecorator { public Mozzarella(Pizza newPizza) { super(newPizza); System.out.println("Adding Dough"); System.out.println("Adding Moz");
} // Returns the result of calling getDescription() for
// PlainPizza and adds " mozzarella" to it public String getDescription(){ return tempPizza.getDescription() + ", mozzarella"; } public double getCost(){ System.out.println("Cost of Moz: " + .50); return tempPizza.getCost() + .50; } }
• TomatoSauce.java
public class TomatoSauce extends ToppingDecorator { public TomatoSauce(Pizza newPizza) {
super(newPizza); System.out.println("Adding Sauce");
} // Returns the result of calling getDescription() for
// PlainPizza, Mozzarella and then TomatoSauce public String getDescription(){ return tempPizza.getDescription() + ", tomato sauce"; } public double getCost(){ System.out.println("Cost of Sauce: " + .35); return tempPizza.getCost() + .35; } }
• PizzaMaker.java
public class PizzaMaker { public static void main(String[] args){ // The PlainPizza object is sent to the Mozzarella constructor
// and then to the TomatoSauce constructor Pizza basicPizza = new TomatoSauce(new Mozzarella(new PlainPizza())); System.out.println("Ingredients: " + basicPizza.getDescription()); System.out.println("Price: " + basicPizza.getCost()); } }
AdapterWhat is the Adapter Design Pattern?
• Allows 2 incompatible interfaces to work together.
• Used when the client expects a (target) interface.
• The Adapter class allows the use of the available interface and the Target interface.
• Any class can work together as long as the Adapter solves the issue that all classes must implement every method defined by the shared interface.
Sample Code
• EnemyAttacker.java
// This is the Target Interface : This is what the client
// expects to work with. It is the adapters job to make new
// classes compatible with this one. public interface EnemyAttacker { public void fireWeapon(); public void driveForward(); public void assignDriver(String driverName); }
• EnemyTank.java
// EnemyTank implements EnemyAttacker perfectly
// Our job is to make classes with different methods
// from EnemyAttacker to work with the EnemyAttacker interface import java.util.Random; public class EnemyTank implements EnemyAttacker{ Random generator = new Random(); public void fireWeapon() { int attackDamage = generator.nextInt(10) + 1; System.out.println("Enemy Tank Does " + attackDamage + " Damage"); } public void driveForward() { int movement = generator.nextInt(5) + 1; System.out.println("Enemy Tank moves " + movement + " spaces"); } public void assignDriver(String driverName) { System.out.println(driverName + " is driving the tank"); } }
• EnemyRobot.java
// This is the Adaptee. The Adapter sends method calls
// to objects that use the EnemyAttacker interface
// to the right methods defined in EnemyRobot import java.util.Random; public class EnemyRobot{ Random generator = new Random(); public void smashWithHands() { int attackDamage = generator.nextInt(10) + 1; System.out.println("Enemy Robot Causes " + attackDamage + " Damage With Its Hands"); } public void walkForward() { int movement = generator.nextInt(5) + 1; System.out.println("Enemy Robot Walks Forward " + movement + " spaces"); } public void reactToHuman(String driverName) { System.out.println("Enemy Robot Tramps on " + driverName); } }
• EnemyRobotAdapter.java
// The Adapter must provide an alternative action for
// the the methods that need to be used because
// EnemyAttacker was implemented. // This adapter does this by containing an object
// of the same type as the Adaptee (EnemyRobot)
// All calls to EnemyAttacker methods are sent
// instead to methods used by EnemyRobot public class EnemyRobotAdapter implements EnemyAttacker{ EnemyRobot theRobot; public EnemyRobotAdapter(EnemyRobot newRobot){ theRobot = newRobot; } public void fireWeapon() { theRobot.smashWithHands(); } public void driveForward() { theRobot.walkForward(); } public void assignDriver(String driverName) { theRobot.reactToHuman(driverName); } }
• TestEnemyAttackers.java
public class TestEnemyAttackers{ public static void main(String[] args){ EnemyTank rx7Tank = new EnemyTank(); EnemyRobot fredTheRobot = new EnemyRobot(); EnemyAttacker robotAdapter = new EnemyRobotAdapter(fredTheRobot); System.out.println("The Robot"); fredTheRobot.reactToHuman("Paul");
fredTheRobot.walkForward();
fredTheRobot.smashWithHands();
System.out.println(); System.out.println("The Enemy Tank"); rx7Tank.assignDriver("Frank");
rx7Tank.driveForward();
rx7Tank.fireWeapon();
System.out.println(); System.out.println("The Robot with Adapter"); robotAdapter.assignDriver("Mark");
robotAdapter.driveForward();
robotAdapter.fireWeapon(); } }
FacadeWhat is the Facade Design Pattern?
• When you create a simplified interface that performs many other actions behind the scenes.
• Can I withdrawal $50 from the bank?
• Check if the checking account is valid.
• Check if the security code is valid.
• Check if funds are available.
• Make changes accordingly.
Sample Code
• WelcomeToBank.java
public class WelcomeToBank{ public WelcomeToBank() { System.out.println("Welcome to ABC Bank");
System.out.println("We are happy to give you your money if we can find it\n"); } }
• AccountNumberCheck.java
public class AccountNumberCheck{ private int accountNumber = 12345678; public int getAccountNumber() { return accountNumber; } public boolean accountActive(int acctNumToCheck){ if(acctNumToCheck == getAccountNumber()) { return true; } else { return false; } } }
• SecurityCodeCheck.java
public class SecurityCodeCheck { private int securityCode = 1234; public int getSecurityCode() { return securityCode; } public boolean isCodeCorrect(int secCodeToCheck){ if(secCodeToCheck == getSecurityCode()) { return true; } else { return false; } } }
• FundsCheck.java
public class FundsCheck { private double cashInAccount = 1000.00; public double getCashInAccount() { return cashInAccount; } public void decreaseCashInAccount(double cashWithdrawn) { cashInAccount -= cashWithdrawn; } public void increaseCashInAccount(double cashDeposited) { cashInAccount += cashDeposited; } public boolean haveEnoughMoney(double cashToWithdrawal) { if(cashToWithdrawal > getCashInAccount()) { System.out.println("Error: You don't have enough money");
System.out.println("Current Balance: " + getCashInAccount()); return false; } else { decreaseCashInAccount(cashToWithdrawal); System.out.println("Withdrawal Complete: Current Balance is " + getCashInAccount()); return true; } } public void makeDeposit(double cashToDeposit) { increaseCashInAccount(cashToDeposit); System.out.println("Deposit Complete: Current Balance is " + getCashInAccount()); } }
• BankAccountFacade.java
// The Facade Design Pattern decouples or separates the client
// from all of the sub components // The Facades aim is to simplify interfaces so you don't have
// to worry about what is going on under the hood public class BankAccountFacade { private int accountNumber;
private int securityCode; AccountNumberCheck acctChecker;
SecurityCodeCheck codeChecker;
FundsCheck fundChecker; WelcomeToBank bankWelcome; public BankAccountFacade(int newAcctNum, int newSecCode){ accountNumber = newAcctNum;
securityCode = newSecCode; bankWelcome = new WelcomeToBank(); acctChecker = new AccountNumberCheck();
codeChecker = new SecurityCodeCheck();
fundChecker = new FundsCheck(); } public int getAccountNumber() { return accountNumber; } public int getSecurityCode() { return securityCode; } public void withdrawCash(double cashToGet){ if(acctChecker.accountActive(getAccountNumber()) &&
codeChecker.isCodeCorrect(getSecurityCode()) &&
fundChecker.haveEnoughMoney(cashToGet)) { System.out.println("Transaction Complete\n"); } else { System.out.println("Transaction Failed\n"); } } public void depositCash(double cashToDeposit){ if(acctChecker.accountActive(getAccountNumber()) &&
codeChecker.isCodeCorrect(getSecurityCode())) { fundChecker.makeDeposit(cashToDeposit); System.out.println("Transaction Complete\n"); } else { System.out.println("Transaction Failed\n"); } } }
• TestBankAccount.java
public class TestBankAccount { public static void main(String[] args){ BankAccountFacade accessingBank = new BankAccountFacade(12345678, 1234); accessingBank.withdrawCash(50.00); accessingBank.withdrawCash(990.00); } }
BridgeWhat is the Bridge Design Pattern?
• Decouple an abstraction from its implementation so that the two can vary independently.
• The Bridge Design Pattern is very poorly explained.
• Everyone seems to explain it differently.
• Progressively adding functionality while separating out major differences using abstract classes.
When to use the Bridge Design Pattern?
• When you want to be able to change both the abstractions (abstract classes) and concrete classes independently.
• When you want the first abstract class to define rules (Abstract TV).
• The concrete class adds additional rules (Concrete TV).
• An abstract class has a reference to the device and it defines abstract methods that will be defined (Abstract Remote).
• The Concrete Remote defines the abstract methods required.
Sample Code
• EntertainmentDevice.java
// Implementor
// With the Bridge Design Pattern you create 2 layers of abstraction
// In this example I'll have an abstract class representing
// different types of devices. I also have an abstract class
// that will represent different types of remote controls // This allows me to use an infinite variety of devices and remotes abstract class EntertainmentDevice { public int deviceState; public int maxSetting; public int volumeLevel = 0; public abstract void buttonFivePressed(); public abstract void buttonSixPressed(); public void deviceFeedback() { if(deviceState > maxSetting || deviceState < 0) { deviceState = 0; } System.out.println("On Channel " + deviceState); } public void buttonSevenPressed() { volumeLevel++; System.out.println("Volume at: " + volumeLevel); } public void buttonEightPressed() { volumeLevel--; System.out.println("Volume at: " + volumeLevel); } }
• TVDevice.java
// Concrete Implementor // Here is an implementation of the EntertainmentDevice
// abstract class. I'm specifying what makes it different
// from other devices public class TVDevice extends EntertainmentDevice { public TVDevice(int newDeviceState, int newMaxSetting){ deviceState = newDeviceState; maxSetting = newMaxSetting; } public void buttonFivePressed() { System.out.println("Channel Down"); deviceState--; } public void buttonSixPressed() { System.out.println("Channel Up"); deviceState++; } }
• RemoteButton.java
// Abstraction // This is an abstract class that will represent numerous
// ways to work with each device public abstract class RemoteButton{ // A reference to a generic device using aggregation private EntertainmentDevice theDevice; public RemoteButton(EntertainmentDevice newDevice){ theDevice = newDevice; } public void buttonFivePressed() { theDevice.buttonFivePressed(); } public void buttonSixPressed() { theDevice.buttonSixPressed(); } public void deviceFeedback(){ theDevice.deviceFeedback(); } public abstract void buttonNinePressed(); }
• TVRemoteMute.java
// Refined Abstraction // If I decide I want to further extend the remote I can public class TVRemoteMute extends RemoteButton{ public TVRemoteMute(EntertainmentDevice newDevice) {
super(newDevice);
} public void buttonNinePressed() { System.out.println("TV was Muted"); } }
• TVRemotePause.java
// Refined Abstraction // If I decide I want to further extend the remote I can public class TVRemotePause extends RemoteButton{ public TVRemotePause(EntertainmentDevice newDevice) {
super(newDevice);
} public void buttonNinePressed() { System.out.println("TV was Paused"); } }
• TestTheRemote.java
public class TestTheRemote{ public static void main(String[] args){ RemoteButton theTV = new TVRemoteMute(new TVDevice(1, 200)); RemoteButton theTV2 = new TVRemotePause(new TVDevice(1, 200)); // HOMEWORK -------------- RemoteButton theDVD = new DVDRemote(new DVDDevice(1,14)); // ----------------------- System.out.println("Test TV with Mute"); theTV.buttonFivePressed();
theTV.buttonSixPressed();
theTV.buttonNinePressed(); System.out.println("\nTest TV with Pause"); theTV2.buttonFivePressed();
theTV2.buttonSixPressed();
theTV2.buttonNinePressed();
theTV2.deviceFeedback(); // HOMEWORK System.out.println("\nTest DVD"); theDVD.buttonFivePressed();
theDVD.buttonSixPressed();
theDVD.buttonNinePressed();
theDVD.buttonNinePressed(); } }
FlyweightWhat is the Flyweight Design Pattern?
• Used when you need to create a large number of similar objects.
• To reduce memory usage you share objects that are similar in some way rather than creating new ones.
• Intrinsic State: Color.
• Extrinsic State: Size.
Sample Code
• FlyWeightTest.java
// The Flyweight design pattern is used when you need to
// create a large number of similar objects // To reduce memory this pattern shares Objects that are
// the same rather than creating new ones import javax.swing.*; import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random; public class FlyWeightTest extends JFrame{ private static final long serialVersionUID = 1L; JButton startDrawing; int windowWidth = 1750;
int windowHeight = 1000; // A new rectangle is created only if a new color is needed Color[] shapeColor = {Color.orange, Color.red, Color.yellow,
Color.blue, Color.pink, Color.cyan, Color.magenta,
Color.black, Color.gray}; public static void main(String[] args){ new FlyWeightTest(); } public FlyWeightTest(){ // Create the frame, position it and handle closing it this.setSize(windowWidth,windowHeight);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setTitle("Flyweight Test"); JPanel contentPane = new JPanel(); contentPane.setLayout(new BorderLayout()); final JPanel drawingPanel = new JPanel(); startDrawing = new JButton("Button 1"); contentPane.add(drawingPanel, BorderLayout.CENTER); contentPane.add(startDrawing, BorderLayout.SOUTH); startDrawing.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent event) {
Graphics g = drawingPanel.getGraphics(); long startTime = System.currentTimeMillis(); for(int i=0; i < 100000; ++i) { //
// Uses rectangles stored in the HashMap to
// speed up the program MyRect rect = RectFactory.getRect(getRandColor());
rect.draw(g, getRandX(), getRandY(),
getRandX(), getRandY()); //
/*
MyRect rect = new MyRect(getRandColor(), getRandX(), getRandY(), getRandX(), getRandY());
rect.draw(g);
*/ //
/*
g.setColor(getRandColor());
g.fillRect(getRandX(), getRandY(), getRandX(), getRandY());
*/ } long endTime = System.currentTimeMillis(); System.out.println("That took " + (endTime - startTime) + " milliseconds"); }
}); this.add(contentPane); this.setVisible(true); } // Picks random x & y coordinates private int getRandX(){ return (int)(Math.random()*windowWidth); } private int getRandY(){ return (int)(Math.random()*windowHeight); } // Picks a random Color from the 9 available private Color getRandColor(){
Random randomGenerator = new Random(); int randInt = randomGenerator.nextInt(9); return shapeColor[randInt]; } }
• MyRect.java
import java.awt.*;
public class MyRect {
private Color color = Color.black;
private int x, y, x2, y2; public MyRect(Color color) { this.color = color; } public void draw(Graphics g, int upperX, int upperY, int lowerX, int lowerY) {
g.setColor(color);
g.fillRect(upperX, upperY, lowerX, lowerY);
} /* Original forces creation of a rectangle every time public MyRect(Color color, int upperX, int upperY, int lowerX, int lowerY) {
this.color = color;
this.x = upperX;
this.y = upperY;
this.x2 = lowerX;
this.y2 = lowerY;
} public void draw(Graphics g) {
g.setColor(color);
g.fillRect(x, y, x2, y2);
}
*/
}
• RectFactory.java
// This factory only creates a new rectangle if it
// uses a color not previously used // Intrinsic State: Color
// Extrinsic State: X & Y Values import java.util.HashMap;
import java.awt.Color;
public class RectFactory { // The HashMap uses the color as the key for every
// rectangle it will make up to 8 total private static final HashMap<Color, MyRect> rectsByColor = new HashMap<Color, MyRect>(); public static MyRect getRect(Color color) {
MyRect rect = (MyRect)rectsByColor.get(color); // Checks if a rectangle with a specific
// color has been made. If not it makes a
// new one, otherwise it returns one made already if(rect == null) {
rect = new MyRect(color); // Add new rectangle to HashMap rectsByColor.put(color, rect); }
return rect;
}
}
ProxyWhat is the Proxy Design Pattern?
• Provide a class which will limit access to another class.
• You may do this for security reasons, because an Object is intensive to create, or is accessed from a remote location.
Sample Code
• ATMMachine.java
public class ATMMachine implements GetATMData{ public ATMState getYesCardState() { return hasCard; }
public ATMState getNoCardState() { return noCard; }
public ATMState getHasPin() { return hasCorrectPin; }
public ATMState getNoCashState() { return atmOutOfMoney; } // NEW STUFF public ATMState getATMState() { return atmState; }
public int getCashInMachine() { return cashInMachine; }
}
• GetATMData.java
// This interface will contain just those methods
// that you want the proxy to provide access to public interface GetATMData {
public ATMState getATMState();
public int getCashInMachine();
}
• ATMProxy.java
// In this situation the proxy both creates and destroys
// an ATMMachine Object public class ATMProxy implements GetATMData { // Allows the user to access getATMState in the
// Object ATMMachine public ATMState getATMState() { ATMMachine realATMMachine = new ATMMachine(); return realATMMachine.getATMState();
} // Allows the user to access getCashInMachine
// in the Object ATMMachine public int getCashInMachine() { ATMMachine realATMMachine = new ATMMachine(); return realATMMachine.getCashInMachine(); } }
• TestATMMachine.java
public class TestATMMachine { public static void main(String[] args){ ATMMachine atmMachine = new ATMMachine(); atmMachine.insertCard(); atmMachine.ejectCard(); atmMachine.insertCard(); atmMachine.insertPin(1234); atmMachine.requestCash(2000); atmMachine.insertCard(); atmMachine.insertPin(1234); // NEW STUFF : Proxy Design Pattern Code
// The interface limits access to just the methods you want
// made accessible GetATMData realATMMachine = new ATMMachine(); GetATMData atmProxy = new ATMProxy(); System.out.println("\nCurrent ATM State " + atmProxy.getATMState()); System.out.println("\nCash in ATM Machine $" + atmProxy.getCashInMachine()); // The user can't perform this action because ATMProxy doesn't
// have access to that potentially harmful method
// atmProxy.setCashInMachine(10000); } }
• ATMState.java
public interface ATMState { void insertCard();
void ejectCard();
void insertPin(int pinEntered);
void requestCash(int cashToWithdraw); }
作者:netoxi
出处:http://www.cnblogs.com/netoxi
本文版权归作者和博客园共有,欢迎转载,未经同意须保留此段声明,且在文章页面明显位置给出原文连接。欢迎指正与交流。