结构型模式描述如何将类或者对象结合在一起形成更大的结构,就像搭积木, 可以通过简单积木的组合形成复杂的、功能更为强大的结构。共包含代理模式、适配器模式、桥接模式、装饰器模式、外观模式、享元模式、组合模式7类。
一、代理模式
代理模式是指由于某些原因需要给某对象提供一个代理以控制该对象的访问。这时,访问对象不适合或者不能直接引用目标对象,而是以代理对象作为访问对象和目标对象之间的中介。可以理解为生活情境中中介所扮演的角色。
代理模式的结构主要是通过定义一个继承抽象主题的代理来包含真实主题,从而实现对真实主题的访问。(Spring AOP即借鉴了代理模式中的动态代理思想)
代理模式的关键在于代理类与真实主题均实现同一个接口,代理类中封装真实主题对象实例并在真实主题执行同名方法前后进行函数织入。
类结构图如下:
public class ProxyTest {
public static void main(String[] args) {
Proxy proxy = new Proxy();
proxy.Request();
}
}
//抽象主题
interface Subject {
void Request();
}
//真实主题
class RealSubject implements Subject {
public void Request() {
System.out.println("访问真实主题的方法...");
}
}
//代理类
class Proxy implements Subject {
private RealSubject realSubject;
public void Request() {
if (realSubject == null) realSubject = new RealSubject();
preRequest();
realSubject.Request();//在真实主题方法执行前后执行切面函数
postRequest();
}
public void preRequest() {
System.out.println("访问真实主题之前的处理");
}
public void postRequest() {
System.out.println("访问真实主题之后的处理");
}
}
程序运行结果输出如下:
访问真实主题之前的处理
访问真实主题的方法...
访问真实主题之后的处理
二、适配器模式
适配器模式是指将一个类的接口转换成客户所希望的另一个接口,使得原本由于接口不兼容而不能一起工作的那些类能够一起工作。
适配器模式包含以下角色:目标接口、适配者类、适配器类
适配器模式的关键在于适配器需要实现目标接口,通过封装适配者对象实例在目标接口方法调用时执行适配者的对应方法即可(当然是不同名的)。适配器模式可以理解为在不改变原本对象方法名的基础上通过加一层壳子实现方法调用,而代理模式更偏向于在真实主题方法执行前后做点什么。
public class AdapterTest {
public static void main(String[] args) {
Target target = new ObjectAdapter(new Adaptee());
target.request();
}
}
//目标接口
interface Target {
void request();
}
//适配者接口
class Adaptee {
public void specificRequest() {
System.out.println("适配者中的业务代码被调用!");
}
}
//对象适配器类
class ObjectAdapter implements Target {
private Adaptee adaptee;
public ObjectAdapter(Adaptee adaptee) {
this.adaptee = adaptee;
}
public void request() {
adaptee.specificRequest();
}
}
程序运行结果输出如下:
适配者中的业务代码被调用!
三、桥接模式
桥接模式是指将抽象与实现分离,使它们可以独立变化。使用组合关系代替继承关系实现,从而降低了抽象和实现这两个可变维度的耦合度。现实情境中如不同颜色和字体的文字、不同品牌和价位的汽车等,如果使用继承方式,则m种A维度和n种B维度组合而成的子类就有m*n种,数量多且不易扩展。
桥接模式包含以下角色:抽象化角色、扩展抽象化角色、实现化角色与具体实现化角色。
桥接模式的关键在于将类的两个维度分为抽象化角色与实现化角色,抽象化角色中封装了实现化角色的对象实例,在调用时将具体实现化角色实例传入抽象化角色实例中即可完成两个维度叠加后的具体结果。注意两个维度分为抽象类和实现接口,其性质是不同的。
import com.sun.corba.se.spi.oa.ObjectAdapter;
public class test {
public static void main(String[] args) {
Implementor impl = new ConcreteImplementorA();
Abstraction abstraction = new RefinedAbstraction(impl);
abstraction.Operation();
}
}
//实现化角色
interface Implementor {
public void OperationImpl();
}
//具体实现化角色
class ConcreteImplementorA implements Implementor {
public void OperationImpl() {
System.out.println("具体实现化角色被访问");
}
}
//抽象化角色
abstract class Abstraction {
protected Implementor impl;
protected Abstraction(Implementor impl) {
this.impl = impl;
}
public abstract void Operation();
}
//扩展抽象化角色
class RefinedAbstraction extends Abstraction {
protected RefinedAbstraction(Implementor impl) {
super(impl);
}
public void Operation() {
System.out.println("扩展抽象化角色被访问");
impl.OperationImpl();
}
}
程序运行结果输出如下:
扩展抽象化角色被访问
具体实现化角色被访问
四、装饰器模式
装饰器模式是指在不改变现有对象结构的情况下,动态地给该对象增加一些职责(即增加其额外功能)的模式。通常情况下,扩展类功能会使用继承方式来实现,但继承耦合度高,且扩展功能增多会使得子类臃肿,装饰器则通过对象包装在类结构不变前提下提供额外功能。现实情境中的物品美观优化等都是装饰器模式。
装饰器模式包含以下角色:抽象构件角色、具体构件角色、抽象装饰角色、具体装饰角色。
装饰器模式的关键在于装饰角色与构件角色均实现同一个接口,装饰类中封装了构件类对象,通过在运行时动态传入构件对象实例实现在构件方法调用的前后进行功能增加或增强。
扩展:装饰器模式和代理模式的对比分析
对装饰器来说,装饰者和被装饰者都实现同一个接口;对代理来说,代理类和真实主题类均实现同一个接口。同时,两个模式都可以很容易地在真实对象方法执行前后增加自定义方法。
而两者区别在于,装饰器模式的被装饰对象是在外部动态传入的,而代理模式的被代理对象则是在代理对象自己的构造方法内部创建的而不是调用时传入的。因此,装饰器模式关注于在一个对象上动态添加方法,而代理模式关注于控制对对象的访问。
代理和装饰其实从另一个角度更容易去理解两个模式的区别:代理更多的是强调对对象的访问控制,比如说,访问A对象的查询功能时,访问B对象的更新功能时,访问C对象的删除功能时,都需要判断对象是否登陆,那么我需要将判断用户是否登陆的功能抽提出来,并对A对象、B对象和C对象进行代理,使访问它们时都需要去判断用户是否登陆,简单地说就是将某个控制访问权限应用到多个对象上;而装饰器更多的强调给对象加强功能,比如说要给只会唱歌的A对象添加跳舞功能,添加说唱功能等,简单地说就是将多个功能附加在一个对象上。
所以,代理模式注重的是对对象的某一功能的流程把控和辅助,它可以控制对象做某些事,重心是为了借用对象的功能完成某一流程,而非对象功能如何。而装饰模式注重的是对对象功能的扩展,不关心外界如何调用,只注重对对象功能加强,装饰后还是对象本身。
import com.sun.corba.se.spi.oa.ObjectAdapter;
public class test {
public static void main(String[] args) {
Component p = new ConcreteComponent();
p.operation();
System.out.println("---------------------------------");
Component d = new ConcreteDecorator(p);
d.operation();
}
}
//抽象构件角色
interface Component {
void operation();
}
//具体构件角色
class ConcreteComponent implements Component {
public ConcreteComponent() {
System.out.println("创建具体构件角色");
}
public void operation() {
System.out.println("调用具体构建角色的方法operation");
}
}
//抽象装饰角色
class Decorator implements Component {
private Component component;
public Decorator(Component component) {
this.component = component;
}
public void operation() {
component.operation();
}
}
//具体装饰角色
class ConcreteDecorator extends Decorator {
public ConcreteDecorator(Component component) {
super(component);
}
public void operation() {
super.operation();
addedFunction();
}
public void addedFunction() {
System.out.println("为具体构件角色增加额外的功能addedFunction");
}
}
程序运行结果输出如下:
创建具体构件角色
调用具体构建角色的方法operation
---------------------------------
调用具体构建角色的方法operation
为具体构件角色增加额外的功能addedFunction
五、外观模式
外观模式又叫做门面模式,是指通过为多个复杂的子系统提供一个一致的接口,而使得这些子系统更加容易被访问的模式。该模式有一个统一的对外接口,外部应用不需要关心内部子系统的具体细节。
外观模式的结构较为简单,主要是定义了一个高层接口,包含对各个子系统的引用,客户端可以通过它访问各个子系统的功能。
外观模式的关键在于在各子系统上层增加统一的封装借口,便于客户端调用。
外观模式包括以下角色:外观角色、子系统角色、客户角色。
import com.sun.corba.se.spi.oa.ObjectAdapter;
public class test {
public static void main(String[] args) {
Facade f = new Facade();
f.method();
}
}
//外观角色
class Facade {
private SubSystem1 s1 = new SubSystem1();
private SubSystem2 s2 = new SubSystem2();
private SubSystem3 s3 = new SubSystem3();
public void method() {
s1.method1();
s2.method2();
s3.method3();
}
}
//子系统角色1
class SubSystem1 {
public void method1() {
System.out.println("子系统1的method1被调用");
}
}
//子系统角色2
class SubSystem2 {
public void method2() {
System.out.println("子系统2的method2被调用");
}
}
//子系统角色3
class SubSystem3 {
public void method3() {
System.out.println("子系统2的method3被调用");
}
}
程序运行结果输出如下:
子系统1的method1被调用
子系统2的method2被调用
子系统2的method3被调用
六、享元模式
享元模式是指使用共享技术有效地支持大量细粒度对象的复用,通过共享已经存在的对象来大幅减少需要创建的对象数量、避免大量相似类的开销。现实情境中的黑白棋子、图像坐标点或颜色等都是享元模式的产生背景。
享元模式包括以下角色:抽象享元角色、具体享元角色、非享元角色(不可以共享的外部状态)、享元工厂角色(创建管理享元角色)
享元模式的关键在于将被复用的对象属性中不随环境改变的状态(内部状态)与随环境改变的状态(外部状态)进行分离,对象的构建与管理封装在享元工厂类的HashMap或ArrayList中,需要取用对象时直接从享元工厂中获取对象,而对象涉及外部状态的方法则在调用时动态传参即可,从而实现了对象创建开销的极大减少与外部状态传入的低耦合。
import com.sun.corba.se.spi.oa.ObjectAdapter;
import java.awt.*;
import java.util.HashMap;
public class test {
public static void main(String[] args) {
FlyweightFactory factory = new FlyweightFactory();
Flyweight f01 = factory.getFlyweight("a");
Flyweight f02 = factory.getFlyweight("a");
Flyweight f03 = factory.getFlyweight("a");
Flyweight f11 = factory.getFlyweight("b");
Flyweight f12 = factory.getFlyweight("b");
f01.operation(new UnsharedConcreteFlyweight("第1次调用a。"));
f02.operation(new UnsharedConcreteFlyweight("第2次调用a。"));
f03.operation(new UnsharedConcreteFlyweight("第3次调用a。"));
f11.operation(new UnsharedConcreteFlyweight("第1次调用b。"));
f12.operation(new UnsharedConcreteFlyweight("第2次调用b。"));
}
}
//非享元角色
class UnsharedConcreteFlyweight {
private String info;
UnsharedConcreteFlyweight(String info) {
this.info = info;
}
public String getInfo() {
return info;
}
public void setInfo(String info) {
this.info = info;
}
}
//抽象享元角色
interface Flyweight {
void operation(UnsharedConcreteFlyweight state);
}
//具体享元角色
class ConcreteFlyweight implements Flyweight {
private String key;
ConcreteFlyweight(String key) {
this.key = key;
System.out.println("具体享元" + key + "被创建");
}
public void operation(UnsharedConcreteFlyweight outState) {
System.out.println("具体享元" + key + "被调用");
System.out.println("非享元信息为:" + outState.getInfo());
}
}
//享元工厂角色
class FlyweightFactory {
private HashMap<String, Flyweight> flyweights = new HashMap<String, Flyweight>();
public Flyweight getFlyweight(String key) {
Flyweight flyweight = (Flyweight) flyweights.get(key);
if (flyweight != null) {
System.out.println("具体享元" + key + "已经存在,被成功获取!");
} else {
flyweight = new ConcreteFlyweight(key);
flyweights.put(key, flyweight);
}
return flyweight;
}
}
程序运行结果输出如下:
具体享元a被创建
具体享元a已经存在,被成功获取!
具体享元a已经存在,被成功获取!
具体享元b被创建
具体享元b已经存在,被成功获取!
具体享元a被调用
非享元信息为:第1次调用a。
具体享元a被调用
非享元信息为:第2次调用a。
具体享元a被调用
非享元信息为:第3次调用a。
具体享元b被调用
非享元信息为:第1次调用b。
具体享元b被调用
非享元信息为:第2次调用b。
七、组合模式
组合模式,又叫做整体-部分模式,是指一种将对象组合成树状的层次结构的模式,用来表示“整体-部分”关系,使用户对单个对象和组合对象具有一致的访问性。组合模式一般用来描述整体和部分的关系,将对象组织到树形结构中,顶层节点为根节点,下面包含树枝节点和叶子节点。现实情境中的公司-分公司-部门、大学-学院-专业等都是组合模式的体现。
组合模式包含以下角色:抽象构件角色、叶子构件角色、树枝构件角色。
组合模式的关键在于区分树枝类和叶子类,树枝类中封装ArrayList用于管理叶子节点,在遍历时进行叶子节点的方法调用。
以透明方式为例,结构图如下:
public class test {
public static void main(String[] args) {
Component c0 = new Composite();
Component c1 = new Composite();
Component leaf1 = new Leaf("1");
Component leaf2 = new Leaf("2");
Component leaf3 = new Leaf("3");
c0.add(leaf1);
c0.add(c1);
c1.add(leaf2);
c1.add(leaf3);
c0.operation();
}
}
//抽象构件
interface Component {
void add(Component c);
void remove(Component c);
Component getChild(int i);
void operation();
}
//树叶构件
class Leaf implements Component {
private String name;
public Leaf(String name) {
this.name = name;
}
public void add(Component c) {
}
public void remove(Component c) {
}
public Component getChild(int i) {
return null;
}
public void operation() {
System.out.println("树叶" + name + ":被访问!");
}
}
//树枝构建
class Composite implements Component {
private ArrayList<Component> children = new ArrayList<Component>();
public void add(Component c) {
children.add(c);
}
public void remove(Component c) {
children.remove(c);
}
public Component getChild(int i) {
return children.get(i);
}
public void operation() {
for (Object obj : children) {
((Component) obj).operation();
}
}
}
程序运行结果输出如下:
树叶1:被访问!
树叶2:被访问!
树叶3:被访问!
本文主要参考:http://c.biancheng.net/