你的位置:首页 > Java教程

[Java教程]结构型设计模式


结构型设计模式概述 

结构型设计模式用于处理类或对象之间的组合,即描述类和对象之间怎样组织起来形成大的结构,从而实现新的功能。

实现的机制:

结构型对象模式采用组合/聚合机制来组合类,包括桥梁模式(Bridge)、组合模式(Composite)、装饰器模式(Decorator)、外观模式(Facade)、享元模式(FlyWeight)、代理模式(Proxy)。

结构型类模型采用继承机制来组合类,包括适配器模式(Adapter)。


 

(一)外观(Facade)模式(门面模式) 

问题提出:

在软件系统中,客户程序经常会与复杂系统的内部子系统之间产生耦合,而导致客户程序随着子系统的变化而变化。那么,如何简化客户程序与子系统之间的交互接口?如何将复杂系统的内部子系统与客户程序之间的依赖解耦?

(一)外观(Facade)模式 

public class Class1 {

public void method1(){

….

}

}

public class Class2 {

public void method2(){

….

}

}

public class Class3 {

public void method3(){

….

}

}

public class Class4 {

public void method4(){

….

}

}  

假如客户程序要使用Class1、Class2、Class4完成一项业务功能,使用Class3、Class1完成另一项业务功能。

public class ClientNoFacade {

public void methodA() {//完成第一项业务功能

Class1 c1 = new Class1();

c1.method1();

Class2 c2 = new Class2();

c2.method2();

Class4 c4 = new Class4();

c4.method4();

}

public void methodB() {//完成第二项业务功能

Class3 c3 = new Class3();

c3.method3();

Class1 c1 = new Class1();

c1.method1(); }

}


 

(二)装饰器(Decorator)模式 

问题提出:在软件系统中,有时候我们会使用继承来扩展对象的功能,但是由于继承为类型引入的静态特质,使得这种扩展方式缺乏灵活性;并且随着子类的增多(扩展功能的增多),各种子类的组合(扩展功能的组合)会导致更多子类的膨胀。如何使“对象功能的扩展”能够根据需要来动态地实现?同时避免“扩展功能的增多”带来的子类膨胀问题?

考虑场景:

星巴克的分店几乎开遍世界各地。它们提供了各式各样的美味咖啡:爱尔兰咖啡、蓝山咖啡、卡布基诺、雀巢。每样咖啡都有自己的描述属性和收费行为。另外它们还提供各种配料:奶、砂糖、冰块、豆浆。不同的咖啡加入不同的配料计算的价格是不一样的。

如何设计?

用继承的方式实现简直是垃圾。

我们换一种思路:

现在用户点了一杯爱尔兰双倍牛奶咖啡,我们要做的是:

创建一个爱尔兰咖啡对象

用奶装饰它

再用奶装饰它

调用cost()方法,并依赖委托将配料的价格计算进去

代码实现:

//咖啡接口

package com.lovo.decoretor;

public interface Coffee {

public int cost();

}

//装饰者类

package com.lovo.decoretor;

public abstract class Drcoretor implements Coffee {

@Override
public abstract int cost();

}

//配料

package com.lovo.decoretor;

public class BingDecoretor extends Drcoretor {

private Coffee coffee;

public BingDecoretor(Coffee coffee){
this.coffee = coffee;
}


public int cost() {
// TODO Auto-generated method stub
return 2+coffee.cost();
}

}

//配料

package com.lovo.decoretor;

public class NaiDecoretor extends Drcoretor {

private Coffee coffee;

public NaiDecoretor(Coffee coffee){
this.coffee = coffee;
}
public int cost() {
// TODO Auto-generated method stub
return 1+coffee.cost();
}

}

//蓝山咖啡

package com.lovo.decoretor;

public class LanShanCoffee implements Coffee{

@Override
public int cost() {
// TODO Auto-generated method stub
return 10;
}

}

//测试

package com.lovo.decoretor;

public class TestDecoretor {

public static void main(String[] args) {

Coffee coffee = new LanShanCoffee();

//System.out.println(coffee.cost());

coffee = new NaiDecoretor(coffee);

coffee=new BingDecoretor(coffee);

System.out.println(coffee.cost());
}

}

在装饰模式中的各个角色有:

抽象构件(Component)角色:给出一个抽象接口,以规范准备接收附加责任的对象。

具体构件(Concrete Component)角色:定义一个将要接收附加责任的类。

装饰(Decorator)角色:持有一个构件(Component)对象的实例,并定义一个与抽象构件接口一致的接口。

具体装饰(Concrete Decorator)角色:负责给构件对象"贴上"附加的责任。


 

(三)静态代理(Proxy)模式

问题提出:在软件系统中,有些对象有时候由于跨越网络或者其他的障碍,而不能够或者不想直接访问另一个对象,如果直接访问会给系统带来不必要的复杂性,这时候可以在客户程序和目标对象之间增加一层中间层,让代理对象来代替目标对象打点一切。

例如:

package com.lovo.proxy;

public interface Book {

public void SellBook();
}

//代理

package com.lovo.proxy;

public class BookProxy implements Book{

private RealSubject r;

public BookProxy(RealSubject r) {
super();
this.r = r;
// r = new RealSubject();
}

@Override
public void SellBook() {
r.SellBook();
}

}

package com.lovo.proxy;

public class RealSubject implements Book{

@Override
public void SellBook() {
System.out.println("卖书");
}

}

//测试

package com.lovo.proxy;

public class Test {

public static void main(String[] args) {

Book b = new BookProxy(new RealSubject());
b.SellBook();
}
}


 

(四)桥梁(Bridge)模式 

问题提出:

在软件系统中,某些类型由于自身的逻辑,它具有两个或多个维度的变化,那么如何应对这种“多维度的变化”?如何利用面向对象的技术来使得该类型能够轻松的沿着多个方向进行变化,而又不引入额外的复杂度?

例如:

package com.lovo.Bridge1;

public interface Draw {

public void draw();
}

package com.lovo.Bridge1;

public interface Shap {

public void mydraw();
}

package com.lovo.Bridge1;

public class HuanShiXian implements Draw{

@Override
public void draw() {
System.out.println("画实线");
}

}

package com.lovo.Bridge1;

public class HuaXuXian implements Draw{

@Override
public void draw() {
System.out.println("画虚线");
}

}

package com.lovo.Bridge1;

public class JuXing implements Shap{
private int width;
private int height;
private Draw draw;

public JuXing(int width, int height, Draw draw) {
super();
this.width = width;
this.height = height;
this.draw = draw;
}

public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}

public int getHeight() {
return height;
}

public void setHeight(int height) {
this.height = height;
}

@Override
public void mydraw() {
System.out.println("画矩形:长:"+this.width+",高:"+this.height);
draw.draw();
}

}

package com.lovo.Bridge1;

public class Test {

public static void main(String[] args) {
Draw draw = new HuanShiXian();

Shap s = new JuXing(100, 50, draw);

s.mydraw();
}
}


 

(五)适配器(Adaptor)模式(类的,对象的适配器模式)

问题的提出:

把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。

假设我们要打桩,有两种类:方形桩 和圆形桩.

public class SquarePeg{  

 public void insert(String str){   

  System.out.println("SquarePeg insert():"+str);  

 }

}

public class RoundPeg{  

 public void insertIntohole(String msg){   

  System.out.println("RoundPeg insertIntoHole():"+msg);

}

}

现在有一个应用,需要既打方形桩,又打圆形桩.那么我们需要将这两个没有关系的类综合应用.假设RoundPeg我们没有源代码,或源代码我们不想修改,那么我们使用Adapter来实现这个应用

public class PegAdapter extends SquarePeg{

  private RoundPeg roundPeg;  

 public PegAdapter(RoundPeg peg) {

this.roundPeg=peg;

}

  public void insert(String str) {

roundPeg.insertIntoHole(str);

}

}

上面代码中,RoundPeg属于Adaptee,是被适配者.

PegAdapter是Adapter,将Adaptee(被适配者RoundPeg)和Target(目标SquarePeg)进行适配.

实际上这是将组合方法(composition)和继承(inheritance)方法综合运用.

PegAdapter是继承了SquarePeg,如果我们需要两边继承,即继承SquarePeg 又继承RoundPeg,因为Java中不允许多继承,但是我们可以实现(implements)两个接口(interface)

public interface IRoundPeg{  

 public void insertIntoHole(String msg);

}

public interface ISquarePeg{  

 public void insert(String str);

}

新的RoundPeg 和SquarePeg

public class SquarePeg implements ISquarePeg{  

 public void insert(String str){   

  System.out.println("SquarePeg insert():"+str);  

 }

}

public class RoundPeg implements IRoundPeg{  

 public void insertIntohole(String msg){   

  System.out.println("RoundPeg insertIntoHole():"+msg);  

 }

}

新的PegAdapter

public class PegAdapter implements IRoundPeg, ISquarePeg{  

 private RoundPeg roundPeg;  

 private SquarePeg squarePeg;   

// 构造方法

  public PegAdapter(RoundPeg roundPeg, SquarePeg squarePeg){

this.roundPeg=roundPeg;

this.squarePeg=squarePeg;

}

}