结构模式(Structural Pattern):描述如何将类或者对象结合在一起形成更大的结构。结构模式描述两种不同的东西:类与类的实例。根据这一不同,类的结构模式可以分为类的结构模式和对象的结构模式。
类的结构模式---类的结构模式使用继承把类、接口等组合在一起,以形成更大的结构。当一个类从父类继承并实现某接口时,这个新的类就把父类的结构和接口的结构结合起来。类的结构模式是静态的。一个类的结构模式的典型例子就是适配器模式。
对象的结构模式---对象的结构模式描述怎样把各种不同的类型的对象组合在一起,以实现新的功能的方法。对象的结构模式是静态的。典型例子就是代理人模式。
1. 适配器模式-Adapter
适配器模式把一个类的接口变换成客户端所期待的另一种接口,从而使原本接口不匹配而无法在一起的工作的两个类能够在一起工作。
适配器的两种模式:类的适配器模式,对象的适配器模式
package Adapter;
public interface Target
{
//The original class has below method.
void sampleOperation1();
//The original class has no below method.
void sampleOperation2();
}
package Adapter;
public class Adaptee
{
//The original class has this method.
public void sampleOperation1(){};
}
package Adapter;
/*
* Class adapter mode
*/
public class Adapter extends Adaptee implements Target
{
//As the original class has no this method. So adapter class add this method.
public void sampleOperation2(){};
}
package Adapter;
/*
* Object adapter mode
*/
public class Adapter2 implements <span style="font-family: Arial, Helvetica, sans-serif;">Target </span>
{
private Adaptee adaptee;
public Adapter2(Adaptee adaptee)
{
super();
this.adaptee=adaptee;
}
/*
* The original class has this method.
*/
public void sampleOperation1()
{
adaptee.sampleOperation1();
}
/*
* As the original class has not this method. So add this method here
*/
public void sampleOperation2()
{
//write you code here
}
}
缺省适配器模式为一个接口提供缺省的实现,这样子类型可以从这个缺省的实现进行扩展,而不必从原有的接口进行扩展。
package DefaultAdapter;
public interface Target
{
void sampleOperation1();
void sampleOperation2();
}
package DefaultAdapter;
/*
* default adapter class
*/
public abstract class Adapter implements Target
{
//provide default implementation for this two methods.
public void sampleOperation1(){};
public void sampleOperation2(){};
}
package DefaultAdapter;
/*
*How to user adapter class.
*/
public class UseAdapter extends Adapter
{
public void sampleOperation2()
{
//write your code here
}
}
3.合成模式-Composite
合成模型模式属于对象的结构模式,有时又叫做部分-整体模式(part-whole)。合成模式将对象组织到树结构中,可以用来描述整体与部分的的关系。合成模式可以使客户端将单纯元素与复合元素同等看等。
在面向对象的的语言中,树结构也同样威力巨大。一个基于继承的类型的等级结构便是一个树结构;一个基于合成的对象的结构也是树结构。有向树结构的又可以分为三种:从上向下,从下向上和双向的,树的节点和它们的相互关系都是一样的,但是连接它们的关系的方向却是不一样的。
由上向下的树图-----每一个树枝节点都有箭头指向的它的子节点,从而一个客户端可以要求一个树枝节点给出所有的子节点,而一个节点却并不知道它的父节点。
由下向上的树图-----每一个节点都有箭头指向它的父节点,但是一个父节点却不知道其子节点。
双向树图------每一个节点都同时知道它的父节点和所有的子节点。
注意:一个树结构由两种节点组成:树枝节点和树叶节点。树枝节点可以有子节点,而一个树叶节点却不可以有子节点。一个树枝节点可以不带有任何节点,但是因为它有带叶子的能力,因此仍然是树枝节点,而不会成为叶子节点。而一个树叶节点则永远不可能带有子节点。
根节点是一个特殊的节点,一个根节点没有父节点,因为它是树结构的根。一般讨论的树都是只有一个根节点的树。一个树的根节点一般是树枝节点,如果根节点是树叶节点的话,这个树就变成了只有一个节点的树。
合成模式分为安全式和透明式的合成模式。
透明方式的合成模式-----在抽象构件角色里面声明所有用来管理子类对象的方法,包括add(), remove(), getChild()方法。这样做的好外是所有构件类都有相同的接口。在客户端看来,树叶类对象与合成类对象的区别起码在接口层消失了,客户端可以同等地对待所有的对象。这就是透明方式的合成模式。缺点:不够安全,因为树叶类对象和合成类对象在本质上是有区别的。树叶类对象不可能有下一个层次的对象,因此add(),remove(),getChild()方法没有意义,但是编译时不会出错,而只会在运行时期出错。
package GoThroughComposite;
import java.awt.Composite;
import java.util.Enumeration;
public interface Component {
void sampleOperation();
//Return itself instance
Composite getComposite();
void add(Component component);
void removie(Component component);
Enumeration components();
}
package GoThroughComposite;
import java.util.Enumeration;
import java.util.Vector;
public class Composite implements Component{
private Vector comVector = new Vector();
@Override
public void sampleOperation() {
Enumeration enumeration = components();
while (enumeration.hasMoreElements())
{
((Component)enumeration.nextElement()).sampleOperation();
}
}
@Override
public Composite getComposite() {
return this;
}
@Override
public void add(Component component) {
comVector.addElement(component);
}
@Override
public void removie(Component component) {
comVector.removeElement(component);
}
@Override
public Enumeration components() {
return comVector.elements();
}
}
package GoThroughComposite;
import java.util.Enumeration;
public class Leaf implements Component{
@Override
public void sampleOperation() {
// Write your code here
}
@Override
public Composite getComposite() {
return null;
}
@Override
public void add(Component component) { }
@Override
public void removie(Component component) { }
@Override
public Enumeration components() {
return null;
}
}
安全方式的合成模式-----在树枝构件角色里面声明所有用来管理子类对象的方法。这样的做法是安全的做法,因为树叶类型的对象根本就没有管理子类型对象的方法,因此,如果客户端对树叶类对象使用这些方法时,程序会在编译时出错。编译不通过,就不会出现在运行时的错误。缺点:不够透明,因为树叶类和合成类将具有不同的接口。
package SafeComposite;
public interface Component {
//Return itself instance
Composite getComposite();
//One sample method
void sampleOperation();
}
package SafeComposite;
import java.awt.image.ComponentSampleModel;
import java.util.Enumeration;
import java.util.Vector;
public class Composite implements Component{
private Vector componentVector = new java.util.Vector();
@Override
public Composite getComposite() {
// TODO Auto-generated method stub
return this;
}
@Override
public void sampleOperation() {
Enumeration enumeration = components();
while(enumeration.hasMoreElements())
{
((Component)enumeration.nextElement()).sampleOperation();
}
}
public void add(Component component)
{
componentVector.addElement(component);
}
public void removie(Component component)
{
componentVector.removeElement(component);
}
public Enumeration components()
{
return componentVector.elements();
}
}
package SafeComposite;
public class Leaf implements Component{
public void sampleOperation(){};
//return itself instance
public Composite getComposite(){return null;};
}
4. 装饰模式 - Decorator - Wrapper - 包装模式
装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案。
装饰模式使用原来被装饰的类的一个子类的实例,把客户端的调用委派到被装饰类。装饰模式的关键在于这种扩展是完全透明的。
在装饰模式的各个角色:1)抽象构件角色:给出一个抽象的接口,以规范准备接收附加责任的对象。2)具体构件角色:定义一个将要接收附加责任的类。3)装饰角色:持有一个构件角色的对象的实例, 并定义一个与抽象构件接口一致的接口。4)具体装饰角色:负责给构件对象“贴上”附加的责任。
package Decorator;
public interface Component {
void sampleOperation();
}
package Decorator;
public class ConcretComponent implements Component{
public ConcretComponent()
{}
public void sampleOperation()
{
}
}
package Decorator;
public class Decorator implements Component{
private Component component;
public Decorator(Component component)
{
this.component = component;
}
public Decorator(){};
@Override
public void sampleOperation() {
// TODO Auto-generated method stub
}
}
package Decorator;
public class ConcreteDecorator extends Decorator{
public void sampleOperation()
{
super.sampleOperation();
}
}
5.代理模式 - Proxy
代理模式是对象的结构模式。代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
代理模式涉及三个角色:
1)抽象主题角色:声明了真实主题角色和代理主题角色的共同接口,这样以来任何可以使用真实主题的地方都可以使用代理主题;2)代理主题角色:代理主题角色内部含有真实主题的引用,从而可以在任何时候操作真实主题对象;代理主题提供一个和真实主题角色相同的接口, 以便在任何时候都可以替代真实主体;控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象);代理角色通常在将客户端调用传递给真实主题之前或之后,都要执行某个操作,而不是单纯地将调用传递给真实主题对象。3)真实主题角色:定义代理主题角色所代表的真实主题对象。
package Proxy;
public abstract class Subject {
/**
* 声明一个抽像的请求方法
*/
abstract public void request();
}
package Proxy;
public class RealSubject extends Subject{
/**
*构造子
*/
public RealSubject()
{
}
/**
* 实现请求方法
*/
public void request()
{
System.out.println("From real Subject");
}
}
package Proxy;
public class ProxySubject extends Subject{
private RealSubject realSubject;
/**
* 构造子
*/
public ProxySubject()
{
}
/**
* 实现请求方法
*/
public void request()
{
preRequest();
if(realSubject==null)
{
realSubject = new RealSubject();
}
realSubject.request();
postRequest();
}
/**
* Some operation before request
*/
public void preRequest()
{
System.out.println("Some action you can do before invoking real subject.");
}
/**
* Some operation after request
*/
public void postRequest()
{
System.out.println("Some action you can do after invoking real subject.");
}
}
package Proxy;
public class Client {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Subject subject = new ProxySubject();
subject.request();
}
}
代理模式与其它模式的关系:
适配器模式: 粗看上去,适配器模式与代理模式很相像,它们都可视为一个对象提供一个前置的接口。但是适配器模式的用意是要改变所考虑的对象的接口,而代理模式并不能改变所代理对象的接口。
装饰模式:装饰模式与所要装饰的对象具有相同的接口,因此这两种模式也有可能混淆。但是装饰模式应为所装饰的对象提供增强功能,而代理模式对对象的使用施加控制,并不提供对象本身的增强功能。
门面模式:有时候,门面模式兼任代理的责任和保护,检查使用者的角色。
虚拟代理模式
package VisualProxy;
import java.awt.Component;
import java.awt.Graphics;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.SwingUtilities;
public class ImageIconProxy implements Icon
{
private ImageIcon realIcon = null;
private String imageName;
private int width;
private int height;
boolean isIconCreated = false;
public ImageIconProxy(String imageName,int width,int height)
{
this.imageName = imageName;
this.width = width;
this.height = height;
}
public int getIconHeight()
{
return realIcon.getIconHeight();
}
public int getIconWidth()
{
return realIcon.getIconWidth();
}
public void paintIcon(final Component c, Graphics g, int x, int y)
{
if(isIconCreated)
{
realIcon.paintIcon(c, g, x, y);
g.drawString("Java and Pattern by Jeff Yan, Ph.D", x+20, y+30);
}
else
{
g.drawRect(x, y, width-1, height-1);
g.drawString("Loading photo...", x+20, y+30);
synchronized(this)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
Thread.currentThread().sleep(2000);
realIcon = new ImageIcon(imageName);
isIconCreated = true;
}
catch(InterruptedException ex)
{
ex.printStackTrace();
}
c.repaint();
}
});
}
}
}
}
package VisualProxy;
import java.awt.Graphics;
import java.awt.Insets;
import javax.swing.Icon;
import javax.swing.JFrame;
public class Client extends JFrame
{
private static int IMAGIN_WIDTH = 670;
private static int IMAGIN_HEIGHT = 880;
private Icon imageIconProxy = null;
static public void main(String[] args)
{
Client app = new Client();
app.show();
}
public Client()
{
super("Visual Proxy Client");
imageIconProxy = new ImageIconProxy("D:\\Program Files\\eclipse3.6.1\\WorkSpace\\JavaModel\\resourcefile\\Girl2.JPG", IMAGIN_WIDTH, IMAGIN_WIDTH);
setBounds(100,100,IMAGIN_WIDTH+10,IMAGIN_HEIGHT+30);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void paint(Graphics g)
{
super.paint(g);
Insets insets = getInsets();
imageIconProxy.paintIcon(this, g, insets.left, insets.top);
}
}