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

07-04-09 tellhow

现在有一个应用,需要既打方形桩,又打圆形桩.那么我们需要将这两个没有关系的类综合应用.假设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
2007-04-09 16:41
>可以实现打圆形桩,但如果要打方形桩,该如何实现呢?

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

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

tellhow
2007-04-10 10:26
首先感谢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大虾指出,谢了!

power1128
2007-04-15 13:14
我是这样理解的,作者的意思是,现在有一个打方形桩的系统,比如叫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();

tellhow
2007-04-16 11:14
首先感谢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类睡在一起。

power1128
2007-04-16 17:22
你最后那个比喻有意思:)

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

tellhow
2007-04-17 09:46
呵呵,我也看了下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)后,如果以后我们加了牛奶还要加糖怎么办?

Coolyu0916
2007-04-17 09:58
无所谓的事情,估计是为了说明的简单

不过一般我觉得是使用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();
}
<p>

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

tellhow
2007-04-18 09:29
我们之所以学习设计模式,是为了提高程序的可重用性和可拓展性,延长软件的生命!

bridge模式的倒咖啡案例中有四种咖啡:

1.中杯加奶

2.中杯不加奶

3.大杯加奶

4.大杯不加奶

为什么要设计一个单态类(Singleton)来hold当前的CoffeeImp呢?

如果让CoffeeImp再多一种选择:糖,那么就有八种咖啡:

1.中杯只加奶

2.中杯只加糖

3.中杯奶和糖都加

4.中杯奶和糖都不加

5.大杯只加奶

6.大杯只加糖

7.大杯奶和糖都加

8.大杯奶和糖都不加

以后CoffeeImp可能还有更多的选择.

bridge模式再加什么模式能实现这些功能的拓展呢?

tellhow
2007-04-18 09:38
banq大虾工作比较忙,而且他的回复太深奥,我们这些初学者挺难深刻理解的!

我觉得应该对每个设计模式都开个帖,初学者都进来讨论讨论,谈谈自己的见解,共同学习,共同进步!

Coolyu0916
2007-04-18 09:57
banq只是一个举例,那你不需要那么认真。

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

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

tellhow
2007-04-19 15:49
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修改过]

Coolyu0916
2007-04-19 17:12
楼上的好像这不是Decorator模式

通常Decorator具备跟原来的接口一样的方法,

只是在做接口方法的前面可以做一些其他的事情。

tellhow
2007-04-20 09:22
>楼上的好像这不是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

猜你喜欢
2Go 1 2 下一页