zoukankan      html  css  js  c++  java
  • 设计模式大类--结构模式(下)

    五、Decorate(装饰者)
    描述:动态的给一个对象添加额外的职责,比继承达到更好的灵活性
    好处:某些功能需要用户动态决定加入方式和时机,装饰者提供即插即用的模型

    例子:
    举Adapter中的打桩示例,在Adapter中有两种类:方形桩 圆形桩,Adapter模式展示如何综合使用这两个类,在Decorator模式中,我们是要在打桩时增加一些额外功能,比如,挖坑 在桩上钉木板等,不关心如何使用两个不相关的类
    我们先建立一个接口:
    public interface Work
    {
      public void insert();

    }

    接口Work有一个具体实现:插入方形桩或圆形桩,这两个区别对Decorator是无所谓.我们以插入方形桩为例:
    public class SquarePeg implements Work{
      public void insert(){
        System.out.println("方形桩插入");
      }

    }

    现在有一个应用:需要在桩打入前,挖坑,在打入后,在桩上钉木板,这些额外的功能是动态,可能随意增加调整修改,比如,可能又需要在打桩之后钉架子(只是比喻).
    那么我们使用Decorator模式,这里方形桩SquarePeg是decoratee(被装饰者),我们需要在decoratee上加些"装饰",这些装饰就是那些额外的功能.
    public class Decorator implements Work{

      private Work work;
      //额外增加的功能被打包在这个List中
      private ArrayList others = new ArrayList();

      //在构造器中使用组合new方式,引入Work对象;
      public Decorator(Work work)
      {
        this.work=work;
      
        others.add("挖坑");

        others.add("钉木板");
      }

      public void insert(){

        newMethod();
      }


      
      //在新方法中,我们在insert之前增加其他方法,这里次序先后是用户灵活指定的   
      public void newMethod()
      {
        otherMethod();
        work.insert();


      }

      public void otherMethod()
      {
        ListIterator listIterator = others.listIterator();
        while (listIterator.hasNext())
        {
          System.out.println(((String)(listIterator.next())) + " 正在进行");
        }

      }

    }

    在上例中,我们把挖坑和钉木板都排在了打桩insert前面,这里只是举例说明额外功能次序可以任意安排.

    好了,Decorator模式出来了,我们看如何调用:

    Work squarePeg = new SquarePeg();
    Work decorator = new Decorator(squarePeg);
    decorator.insert();

    Decorator模式至此完成.

    如果你细心,会发现,上面调用类似我们读取文件时的调用:

    FileReader fr = new FileReader(filename);
    BufferedReader br = new BufferedReader(fr);

    实际上Java 的I/O API就是使用Decorator实现的,I/O变种很多,如果都采取继承方法,将会产生很多子类,显然相当繁琐.

    六、Bridege(桥接)
    描述:将抽象和行为划分开,各自独立,但能动态结合
    好处:多个concrete class之间有概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口,分别放置抽象和行为

    例如,一杯咖啡为例,有中杯和大杯之分,同时还有加奶 不加奶之分. 如果用单纯的继承,这四个具体实现(中杯 大杯 加奶 不加奶)之间有概念重叠,因为有中杯加奶,也有中杯不加奶, 如果再在中杯这一层再实现两个继承,很显然混乱,扩展性极差.那我们使用Bridge模式来实现它.

    如何实现?
    以上面提到的咖啡 为例. 我们原来打算只设计一个接口(抽象类),使用Bridge模式后,我们需要将抽象和行为分开,加奶和不加奶属于行为,我们将它们抽象成一个专门的行为接口.

    先看看抽象部分的接口代码:

    public abstract class Coffee
    {
      CoffeeImp coffeeImp;

      public void setCoffeeImp() {
        this.CoffeeImp = CoffeeImpSingleton.getTheCoffeImp();
      }

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

      public abstract void pourCoffee();
    }

    其中CoffeeImp 是加不加奶的行为接口,看其代码如下:

    public abstract class CoffeeImp
    {
      public abstract void pourCoffeeImp();
    }

    现在我们有了两个抽象类,下面我们分别对其进行继承,实现concrete class:

    //中杯
    public class MediumCoffee extends Coffee
    {
      public MediumCoffee() {setCoffeeImp();}

      public void pourCoffee()
      {
        CoffeeImp coffeeImp = this.getCoffeeImp();
        //我们以重复次数来说明是冲中杯还是大杯 ,重复2次是中杯
        for (int i = 0; i < 2; i++)
        {

          coffeeImp.pourCoffeeImp();
        }
      
      }
    }

    //大杯
    public class SuperSizeCoffee extends Coffee
    {
      public SuperSizeCoffee() {setCoffeeImp();}

      public void pourCoffee()
      {
        CoffeeImp coffeeImp = this.getCoffeeImp();
        //我们以重复次数来说明是冲中杯还是大杯 ,重复5次是大杯
        for (int i = 0; i < 5; i++)
        {

          coffeeImp.pourCoffeeImp();
        }
      
      }
    }

    上面分别是中杯和大杯的具体实现.下面再对行为CoffeeImp进行继承:

    //加奶
    public class MilkCoffeeImp extends CoffeeImp
    {
      MilkCoffeeImp() {}

      public void pourCoffeeImp()
      {
        System.out.println("加了美味的牛奶");
      }
    }

    //不加奶
    public class FragrantCoffeeImp extends CoffeeImp
    {
      FragrantCoffeeImp() {}

      public void pourCoffeeImp()
      {
        System.out.println("什么也没加,清香");
      }
    }

    Bridge模式的基本框架我们已经搭好了,别忘记定义中还有一句:动态结合,我们现在可以喝到至少四种咖啡:
    1.中杯加奶
    2.中杯不加奶
    3.大杯加奶
    4.大杯不加奶

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

    public class CoffeeImpSingleton
    {
      private static CoffeeImp coffeeImp;

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

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

    看看中杯加奶 和大杯加奶 是怎么出来的:

    //拿出牛奶
    CoffeeImpSingleton coffeeImpSingleton = new CoffeeImpSingleton(new MilkCoffeeImp());

    //中杯加奶
    MediumCoffee mediumCoffee = new MediumCoffee();
    mediumCoffee.pourCoffee();

    //大杯加奶
    SuperSizeCoffee superSizeCoffee = new SuperSizeCoffee();
    superSizeCoffee.pourCoffee();


    七、Flyweight(享元)
    描述:运用共享技术有效的支持大量细粒度的对象
    好处:避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类).

    当大量从数据源中读取字符串,其中肯定有重复的,那么我们使用Flyweight模式可以提高效率,以唱片CD为例,在一个XML文件中,存放了多个CD的资料.

    每个CD有三个字段:
    1.出片日期(year)
    2.歌唱者姓名等信息(artist)
    3.唱片曲目 (title)

    其中,歌唱者姓名有可能重复,也就是说,可能有同一个演唱者的多个不同时期 不同曲目的CD.我们将"歌唱者姓名"作为可共享的ConcreteFlyweight.其他两个字段作为UnsharedConcreteFlyweight.

    首先看看数据源XML文件的内容:


    <?xml version="1.0"?>
    <collection>

    <cd>
    <title>Another Green World</title>
    <year>1978</year>
    <artist>Eno, Brian</artist>
    </cd>

    <cd>
    <title>Greatest Hits</title>
    <year>1950</year>
    <artist>Holiday, Billie</artist>
    </cd>

    <cd>
    <title>Taking Tiger Mountain (by strategy)</title>
    <year>1977</year>
    <artist>Eno, Brian</artist>
    </cd>

    .......

    </collection>


    虽然上面举例CD只有3张,CD可看成是大量重复的小类,因为其中成分只有三个字段,而且有重复的(歌唱者姓名).

    CD就是类似上面接口 Flyweight:


    public class CD {

      private String title;
      private int year;
      private Artist artist;

      public String getTitle() {  return title; }
      public int getYear() {    return year;  }
      public Artist getArtist() {    return artist;  }

      public void setTitle(String t){    title = t;}
      public void setYear(int y){year = y;}
      public void setArtist(Artist a){artist = a;}

    }

    将"歌唱者姓名"作为可共享的ConcreteFlyweight:

    public class Artist {

      //内部状态
      private String name;

      // note that Artist is immutable.
      String getName(){return name;}

      Artist(String n){
        name = n;
      }

    }

    再看看Flyweight factory,专门用来制造上面的可共享的ConcreteFlyweight:Artist

    public class ArtistFactory {

      Hashtable pool = new Hashtable();

      Artist getArtist(String key){

        Artist result;
        result = (Artist)pool.get(key);
        ////产生新的Artist
        if(result == null) {
          result = new Artist(key);
          pool.put(key,result);
          
        }
        return result;
      }

    }

    当你有几千张甚至更多CD时,Flyweight模式将节省更多空间,共享的flyweight越多,空间节省也就越大

  • 相关阅读:
    内部类
    多重继承关系初始化顺序及初始化
    String
    Error
    算法:插入排序
    算法:冒泡排序
    算法:选择排序
    注册Activity
    java变量的作用域和基本数据类型转换
    java数据类型
  • 原文地址:https://www.cnblogs.com/aiguozhe/p/3754693.html
Copyright © 2011-2022 走看看