求教设计模式之Adapter(适配器)的问题


现在有一个应用,需要既打方形桩,又打圆形桩.那么我们需要将这两个没有关系的类综合应用.假设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);}

}

代码中PegAdapter类继承了SquarePeg类,
并重载了父类insert()方法.
可以实现打圆形桩,但如果要打方形桩,该如何实现呢?
请banq大虾不吝赐教!

>可以实现打圆形桩,但如果要打方形桩,该如何实现呢?

要打方形桩参考打圆形桩方式.

设计模式和代码展现的是一种模式和方式,可以重用的.

首先感谢banq大虾的回复!
我是菜鸟,对Adapter(适配器)也不太理解,如果提出幼稚的问题,还请大虾耐心教导!
我对Adapter(适配器)的理解:
将两个不兼容的类纠合在一起使用,并实现这两个不兼容类的功能.(这两个类对我们是透明的,我们只知道其方法而不知道其代码.)
public class PegAdapter extends SquarePeg{

  private RoundPeg roundPeg;

  public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)

  public void insert(String str){ roundPeg.insertIntoHole(str);}

}
PegAdapter类虽然继承了SquarePeg类,但insert()方法被重载了.
PegAdapter p=new PegAdapter(peg);
p.insert(str);//打圆形桩
PegAdapter类能实现打圆形桩的功能,打方形桩的功能却不知道如何实现了.

我对代码做了如下修改:
public class PegAdapter extends SquarePeg{

  private RoundPeg roundPeg;

  public PegAdapter(RoundPeg peg)(this.roundPeg=peg;)

  public void insertIntoHole(String str){ roundPeg.insertIntoHole(str);}

}

PegAdapter类不重载insert()方法.
PegAdapter p=new PegAdapter(peg);
p.insert(str);//打方形桩
p.insertIntoHole(str);//打圆形桩
这样一来,PegAdapter类既能实现打圆形桩的功能,也能实现打方形桩的功能了.

有什么不对的地方,还请banq大虾指出,谢了!

我是这样理解的,作者的意思是,现在有一个打方形桩的系统,比如叫SquareClient,它可能会调用SquarePeg的insert()方法,这个SquareClient和SquarePeg是我们已经有的系统,现在出现了另一个RoundPeg类,我们希望SquareClient也能操作它,并且不用修改代码。我们引入adapter,注意PegAdapter extends SquarePeg,也就是说PegAdapter是SquarePeg的子类,这样我们的SquareClient就能透明的操作这个类了,比如:

//用圆形桩对象为参数,构造adapter
PegAdapter square = new PegAdapter(roundObject);
SquareClient client = new SquareClient();
client.setPeg(square);
//客户端不用修改,同样是调用insert,实际上是调用方形桩的insertIntoHole
client.insert();

首先感谢power1128大哥的回复!
看完回复我似乎明白了点!

//用圆形桩对象为参数,构造adapter
PegAdapter square = new PegAdapter(roundObject);
SquareClient client = new SquareClient();
client.setPeg(square);
//客户端不用修改,同样是调用insert,实际上是调用方形桩的insertIntoHole
client.insert();

上面的代码是否可以这样理解:
SquareClient类的setPeg()方法中的参数只能是SquarePeg类及其子类。而我们要使client.insert()既能打方形桩也能打圆形桩:

//用圆形桩对象为参数,构造adapter
PegAdapter square = new PegAdapter(roundObject);
SquarePeg square2 = new SquarePeg();
SquareClient client = new SquareClient();
client.setPeg(square);
//打圆形桩;
client.insert();
client.setPeg(square2);
//打方形桩;
client.insert();

我开始一直把PegAdapter类理解成既能打方形桩也能打圆形桩的。
现在我明白了:PegAdapter类就像SquarePeg类娶进门的老婆,徒有名分而已,而实际上天天晚上和RoundPeg类睡在一起。

你最后那个比喻有意思:)

我也有个比喻:就好像cpu的转接卡一样,比如940针转939针,使的940针的cpu能在939针的主板上用,这个转接卡就像一个adpater.我也正在学设计模式,刚看到bridge,呵呵,大家一起学习

呵呵,我也看了下bridge,对有些地方不理解:

看看是如何动态结合的,在使用之前,我们做个准备工作,设计一个单态类
(Singleton)用来hold当前的CoffeeImp:

public class CoffeeImpSingleton
{
  private static CoffeeImp coffeeImp;

  public CoffeeImpSingleton(CoffeeImp coffeeImpIn)
   {this.coffeeImp = coffeeImpIn;}

  public static CoffeeImp getTheCoffeeImp()
  {
    return coffeeImp;
  }
}


倒咖啡这个案例,为什么要设计一个单态类(Singleton)来hold当前的CoffeeImp呢? 如果不设计单态类(Singleton),为出现什么问题?设计了单态类(Singleton)后,如果以后我们加了牛奶还要加糖怎么办?

无所谓的事情,估计是为了说明的简单
不过一般我觉得是使用factory模式的


public abstract class Coffee
{
  CoffeeImp coffeeImp;

  public void setCoffeeImp() {
    this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
//this.CoffeeImp = CoffeeImpFactory.create()//这样是开发中常用的形式。
  }

  public CoffeeImp getCoffeeImp() {return this.CoffeeImp;}

  public abstract void pourCoffee();
}

如果又要加奶又要加糖,可能要新加一个行为接口的实现,比如叫MilkSugarCoffeeImp,在这个类的pourCoffeeImp()方法里实现既加糖又加奶的操作,然后行为工厂创建该类的一个对象,传递给Coffee

我们之所以学习设计模式,是为了提高程序的可重用性和可拓展性,延长软件的生命!
bridge模式的倒咖啡案例中有四种咖啡:
1.中杯加奶
2.中杯不加奶
3.大杯加奶
4.大杯不加奶
为什么要设计一个单态类(Singleton)来hold当前的CoffeeImp呢?

如果让CoffeeImp再多一种选择:糖,那么就有八种咖啡:
1.中杯只加奶
2.中杯只加糖
3.中杯奶和糖都加
4.中杯奶和糖都不加
5.大杯只加奶
6.大杯只加糖
7.大杯奶和糖都加
8.大杯奶和糖都不加
以后CoffeeImp可能还有更多的选择.
bridge模式再加什么模式能实现这些功能的拓展呢?

banq大虾工作比较忙,而且他的回复太深奥,我们这些初学者挺难深刻理解的!
我觉得应该对每个设计模式都开个帖,初学者都进来讨论讨论,谈谈自己的见解,共同学习,共同进步!

banq只是一个举例,那你不需要那么认真。

现在我的问题是你在咖啡里面加奶、加糖,你甚至可以加胡椒,加番茄酱,加任何东西,但是你加了这些东西之后希望做什么那??只是为了得到一杯咖啡么??我想应该是为了咖啡的味道变化吧。你可以设置每个都有一个改变味道的方法,那么当咖啡使用的时候,我们可以让咖啡变舔,当你的匹萨饼使用的时候匹萨的味道依然是变甜。这样才能道道你的软件复用。

那么我们再近一步,可能糖可以使东西变甜,牛奶也可以,蜂蜜也可以,用户可以任意选择怎么办??那么就是定义一个接口具备变甜的方法,然后糖实现它,牛奶、蜂蜜都实现它。这样做就更加灵活了。

bridge模式的定义:将抽象和行为划分开来,各自独立,但能动态的结合.

在咖啡案例中,
中杯和大杯是抽象的.
加牛奶和不加牛奶是行为.
通过bridge模式将这两个概念分开并动态的结合起来.

问题就出在:行为是多样的,我们加了牛奶后还可以加其他的比如糖,蜂蜜.

感谢power1128 兄台的回复!
在power1128 的回复中有提到:如果又要加奶又要加糖,可能要新加一个行为接口的实现,比如叫MilkSugarCoffeeImp,在这个类的pourCoffeeImp()方法里实现既加糖又加奶的操作,然后行为工厂创建该类的一个对象,传递给Coffee

power1128是把又要加奶又要加糖看成一种行为了.这样一来系统的复杂性必然随着行为种类的增多而增加.

感谢Coolyu0916 兄台的回复!
Coolyu0916是将改变味道作为一种行为,而将加糖,牛奶、蜂蜜做为甜味道的具体实现.如果要又加牛奶又加辣椒,这种味道就不太好说了!

我个人的想法是:采用bridge模式+Decorator(油漆工)模式.
Decorator(油漆工)模式能够动态地实现各种行为.
比如我要又加糖又加牛奶:
MediumCoffee mediumCoffee=new MediumCoffee();
Decorator decorator=new Decorator();
decorator.add("加糖");
decorator.add("加牛奶");
mediumCoffee.pourCoffee(decorator.get());

不知道这样做可不可以?
[该贴被tellhow于2007年04月19日 16:05修改过]
[该贴被tellhow于2007年04月19日 16:05修改过]

楼上的好像这不是Decorator模式
通常Decorator具备跟原来的接口一样的方法,
只是在做接口方法的前面可以做一些其他的事情。

>楼上的好像这不是Decorator模式
通常Decorator具备跟原来的接口一样的方法,
只是在做接口方法的前面可以做一些其他的事情。

上面的代码我随便写的,只是表示下意思,并没跟着banq的代码来.上面的Decorator类当然是CoffeeImp行为接口的子类了,也当然具备原来的接口一样的方法,Decorator类还具备父类没有的add方法,remove方法,clear方法和get方法.add方法的参数类型也是CoffeeImp,参数植是行为的具体实例,比如milkCoffeeImp等等!
我上面代码:
decorator.add("加糖");
decorator.add("加牛奶");
参数类型是String类型,只是表示下意思,实际上参数类型应该是CoffeeImp.

get方法返回List.

需要注意的是:不应该把"什么也没加"看成一种行为,而应该看成一种状态.只有两种状态:"加了东西"(糖,牛奶等等!)和"什么都没有加"!
pourCoffee方法应该加上一个判断:
if(impList.size==0){
System.out.println("什么也没加,清香!");
}

上面都是我的愚见,不足之处请大虾们提出!
关于Decorator(油漆工)的介绍在下面的网页上有http://www.jdon.com/designpatterns/decorator.htm