23种java设计模式.pdf
java:23 种设计模式种设计模式 JAVA 的设计模式经前人总结可以分为 23 种 设计模式根据使用类型可以分为三种:1、创建模式:Factory(工厂模式)、Singleton(单态)、Builder(Builder(建造者模式)、Prototype(原型模式)、工厂方法模式。2、结构模式:Flyweight(共享模式)、Bridge(桥模式)、Decorator(装饰模式)、Composite(组合模式)、Adapter(适配器模式)、Proxy(代理模式)、Facade(外观模式)。3、行为模式:Iterator(迭代模式)(迭代模式)、Template(模板模式)、Chain of Responsibility(责 任 链 模 式)、Memento(纪 念 品 模 式)、Mediator(中介模式)、Interpreter(解释器模式)、Strategy(策略模式)、State、Observer(观察者模式)、Visitor(访问模式)、Command(命令模式)。注:以上翻译不甚准确,以英文为准。创建模式创建模式 设计模式之设计模式之 Factory 工厂模式定义工厂模式定义:提供创建对象的接口提供创建对象的接口.为何使用为何使用?工厂模式是我们最常用的模式了,著名的 Jive 论坛,就大量使用了工厂模式,工厂模式在Java 程序系统可以说是随处可见。为什么工厂模式是如此常用?因为工厂模式就相当于创建实例对象的 new,我们经常要根据类 Class 生成实例对象,如 A a=new A()工厂模式也是用来创建实例对象的,所以以后new 时就要多个心眼,是否可以考虑实用工厂模式,虽然这样做,可能多做一些工作,但会给你系统带来更大的可扩展性和尽量少的修改量。我们以类 Sample 为例,如果我们要创建 Sample 的实例对象:Sample sample=new Sample();可是,实际情况是,通常我们都要在创建 sample 实例时做点初始化的工作,比如赋值 查询数据库等。首先,我们想到的是,可以使用 Sample 的构造函数,这样生成实例就写成:Sample sample=new Sample(参数);但是,如果创建 sample 实例时所做的初始化工作不是象赋值这样简单的事,可能是很长一段代码,如果也写入构造函数中,那你的代码很难看了(就需要 Refactor 重整)。为什么说代码很难看,初学者可能没有这种感觉,我们分析如下,初始化工作如果是很长一段代码,说明要做的工作很多,将很多工作装入一个方法中,相当于将很多鸡蛋放在一个篮子里,是很危险的,这也是有背于Java面向对象的原则,面向对象的封装(Encapsulation)和分派(Delegation)告诉我们,尽量将长的代码分派“切割”成每段,将每段再“封装”起来(减少段和段之间偶合联系性),这样,就会将风险分散,以后如果需要修改,只要更改每段,不会再发生牵一动百的事情。在本例中,首先,我们需要将创建实例的工作与使用实例的工作分开,也就是说,让创建实例所需要的大量初始化工作从 Sample 的构造函数中分离出去。这时我们就需要Factory工厂模式来生成对象了,不能再用上面简单new Sample(参数)。还有,如果 Sample 有个继承如 MySample,按照面向接口编程,我们需要将 Sample 抽象成一个接口.现在 Sample 是接口,有两个子类 MySample 和 HisSample.我们要实例化他们时,如下:Sample mysample=new MySample();Sample hissample=new HisSample();随着项目的深入,Sample 可能还会生出很多儿子出来,那么我们要对这些儿子一个个实例化,更糟糕的是,可能还要对以前的代码进行修改:加入后来生出儿子的实例.这在传统程序中是无法避免的.但如果你一开始就有意识使用了工厂模式,这些麻烦就没有了.工厂方法工厂方法 你会建立一个专门生产 Sample 实例的工厂:public class Factory public static Sample creator(int which)/getClass 产生 Sample 一般可使用动态类装载装入类。if(which=1)return new SampleA();else if(which=2)return new SampleB();那么在你的程序中,如果要实例化 Sample 时.就使用 Sample sampleA=Factory.creator(1);这样,在整个就不涉及到 Sample 的具体子类,达到封装效果,也就减少错误修改的机会,这个原理可以用很通俗的话来比喻:就是具体事情做得越多,越容易范错误.这每个做过具体工作的人都深有体会,相反,官做得越高,说出的话越抽象越笼统,范错误可能性就越少.好象我们从编程序中也能悟出人生道理?呵呵.使用工厂方法 要注意几个角色,首先你要定义产品接口,如上面的 Sample,产品接口下有Sample 接口的实现类,如 SampleA,其次要有一个 factory 类,用来生成产品 Sample,如下图,最右边是生产的对象 Sample:进一步稍微复杂一点,就是在工厂类上进行拓展,工厂类也有继承它的实现类concreteFactory 了。抽象工厂抽象工厂 工厂模式中有:工厂方法(Factory Method)抽象工厂(Abstract Factory).这两个模式区别在于需要创建对象的复杂程度上。如果我们创建对象的方法变得复杂了,如上面工厂方法中是创建一个对象 Sample,如果我们还有新的产品接口 Sample2.这里假设:Sample 有两个 concrete 类 SampleA 和 SamleB,而 Sample2 也有两个concrete 类 Sample2A 和 SampleB2 那么,我们就将上例中 Factory 变成抽象类,将共同部分封装在抽象类中,不同部分使用子类实现,下面就是将上例中的 Factory 拓展成抽象工厂:public abstract class Factory public abstract Sample creator();public abstract Sample2 creator(String name);public class SimpleFactory extends Factory public Sample creator().return new SampleA public Sample2 creator(String name).return new Sample2A public class BombFactory extends Factory public Sample creator().return new SampleB public Sample2 creator(String name).return new Sample2B 从上面看到两个工厂各自生产出一套 Sample 和 Sample2,也许你会疑问,为什么我不可以使用两个工厂方法来分别生产 Sample 和 Sample2?抽象工厂还有另外一个关键要点,是因为 SimpleFactory 内,生产 Sample 和生产Sample2 的方法之间有一定联系,所以才要将这两个方法捆绑在一个类中,这个工厂类有其本身特征,也许制造过程是统一的,比如:制造工艺比较简单,所以名称叫SimpleFactory。在实际应用中,工厂方法用得比较多一些,而且是和动态类装入器组合在一起应用,举例举例 我们以 Jive 的 ForumFactory 为例,这个例子在前面的 Singleton 模式中我们讨论过,现在再讨论其工厂模式:public abstract class ForumFactory private static Object initLock=new Object();private static String className=com.jivesoftware.forum.database.DbForumFactory;private static ForumFactory factory=null;public static ForumFactory getInstance(Authorization authorization)/If no valid authorization passed in,return null.if(authorization=null)return null;/以下使用了 Singleton 单态模式 if(factory=null)synchronized(initLock)if(factory=null).try /动态转载类 Class c=Class.forName(className);factory=(ForumFactory)c.newInstance();catch(Exception e)return null;/Now,返回 proxy.用来限制授权对 forum 的访问 return new ForumFactoryProxy(authorization,factory,factory.getPermissions(authorization);/真正创建 forum 的方法由继承 forumfactory 的子类去完成.public abstract Forum createForum(String name,String description)throws UnauthorizedException,ForumAlreadyExistsException;.因为现在的 Jive 是通过数据库系统存放论坛帖子等内容数据,如果希望更改为通过文件系统实现,这个工厂方法 ForumFactory 就提供了提供动态接口:private static String className=com.jivesoftware.forum.database.DbForumFactory;你可以使用自己开发的创建 forum 的方法代替com.jivesoftware.forum.database.DbForumFactory 就可以.在上面的一段代码中一共用了三种模式,除了工厂模式外,还有Singleton单态模式,以及proxy 模式,proxy 模式主要用来授权用户对 forum 的访问,因为访问 forum 有两种人:一个是注册用户 一个是游客 guest,那么那么相应的权限就不一样,而且这个权限是贯穿整个系统的,因此建立一个 proxy,类似网关的概念,可以很好的达到这个效果.看看 Java 宠物店中的 CatalogDAOFactory:public class CatalogDAOFactory /*本方法制定一个特别的子类来实现 DAO 模式。*具体子类定义是在 J2EE 的部署描述器中。*/public static CatalogDAO getDAO()throws CatalogDAOSysException CatalogDAO catDao=null;try InitialContext ic=new InitialContext();/动态装入 CATALOG_DAO_CLASS /可以定义自己的 CATALOG_DAO_CLASS,从而在无需变更太多代码 /的前提下,完成系统的巨大变更。String className=(String)ic.lookup(JNDINames.CATALOG_DAO_CLASS);catDao=(CatalogDAO)Class.forName(className).newInstance();catch(NamingException ne)throw new CatalogDAOSysException(CatalogDAOFactory.getDAO:NamingException while getting DAO type:n+ne.getMessage();catch(Exception se)throw new CatalogDAOSysException(CatalogDAOFactory.getDAO:Exception while getting DAO type:n+se.getMessage();return catDao;CatalogDAOFactory 是典型的工厂方法,catDao 是通过动态类装入器 className 获得 CatalogDAOFactory 具体实现子类,这个实现子类在 Java 宠物店是用来操作catalog 数据库,用户可以根据数据库的类型不同,定制自己的具体实现子类,将自己的子类名给与 CATALOG_DAO_CLASS 变量就可以。由此可见,工厂方法确实为系统结构提供了非常灵活强大的动态扩展机制,只要我们更换一下具体的工厂方法,系统其他地方无需一点变换,就有可能将系统功能进行改头换面的变化。设计模式之设计模式之 Singleton(单态单态)单态定义单态定义:Singleton 模式主要作用是保证在 Java 应用程序中,一个类 Class 只有一个实例存在。在很多操作中,比如建立目录 数据库连接都需要这样的单线程操作。还有,singleton 能够被状态化;这样,多个单态类在一起就可以作为一个状态仓库一样向外提供服务,比如,你要论坛中的帖子计数器,每次浏览一次需要计数,单态类能否保持住这个计数,并且能 synchronize 的安全自动加 1,如果你要把这个数字永久保存到数据库,你可以在不修改单态接口的情况下方便的做到。另外方面,Singleton 也能够被无状态化。提供工具性质的功能,Singleton 模式就为我们提供了这样实现的可能。使用 Singleton 的好处还在于可以节省内存,因为它限制了实例的个数,有利于 Java 垃圾回收(garbage collection)。我们常常看到工厂模式中类装入器(class loader)中也用 Singleton 模式实现的,因为被装入的类实际也属于资源。如何使用如何使用?一般 Singleton 模式通常有几种形式:public class Singleton private Singleton()/在自己内部定义自己一个实例,是不是很奇怪?/注意这是 private 只供内部调用 private static Singleton instance=new Singleton();/这里提供了一个供外部访问本 class 的静态方法,可以直接访问 public static Singleton getInstance()return instance;第二种形式:public class Singleton private static Singleton instance=null;public static synchronized Singleton getInstance()/这个方法比上面有所改进,不用每次都进行生成对象,只是第一次 /使用时生成实例,提高了效率!if(instance=null)instancenew Singleton();return instance;使用 Singleton.getInstance()可以访问单态类。上面第二中形式是 lazy initialization,也就是说第一次调用时初始 Singleton,以后就不用再生成了。注意到 lazy initialization 形式中的 synchronized,这个 synchronized 很重要,如果没有 synchronized,那么使用 getInstance()是有可能得到多个 Singleton实例。关于 lazy initialization 的 Singleton 有很多涉及 double-checked locking(DCL)的讨论,有兴趣者进一步研究。一般认为第一种形式要更加安全些。使用使用 Singleton 注意事项注意事项:有时在某些情况下,使用 Singleton 并不能达到 Singleton 的目的,如有多个Singleton 对象同时被不同的类装入器装载;在 EJB 这样的分布式系统中使用也要注意这种情况,因为 EJB 是跨服务器,跨 JVM 的。我们以 SUN 公司的宠物店源码(Pet Store 1.3.1)的 ServiceLocator 为例稍微分析一下:在 Pet Store 中 ServiceLocator 有两种,一个是 EJB 目录下;一个是 WEB 目录下,我们检查这两个 ServiceLocator 会发现内容差不多,都是提供 EJB 的查询定位服务,可是为什么要分开呢?仔细研究对这两种 ServiceLocator 才发现区别:在 WEB 中的ServiceLocator 的采取 Singleton 模式,ServiceLocator 属于资源定位,理所当然应该使用 Singleton 模式。但是在 EJB 中,Singleton 模式已经失去作用,所以ServiceLocator 才分成两种,一种面向 WEB 服务的,一种是面向 EJB 服务的。Singleton 模式看起来简单,使用方法也很方便,但是真正用好,是非常不容易,需要对Java 的类 线程 内存等概念有相当的了解。总之:如果你的应用基于容器,那么 Singleton 模式少用或者不用,可以使用相关替代技术。设计模式之设计模式之 Builder Builder 模式定义模式定义:将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示.Builder 模式是一步一步创建一个复杂的对象,它允许用户可以只通过指定复杂对象的类型和内容就可以构建它们.用户不知道内部的具体构建细节.Builder模式是非常类似抽象工厂模式,细微的区别大概只有在反复使用中才能体会到.为何使用为何使用?是为了将构建复杂对象的过程过程和它的部件部件解耦.注意:是解耦过程过程和部件部件.因为一个复杂的对象,不但有很多大量组成部分,如汽车,有很多部件:车轮 方向盘 发动机还有各种小零件等等,部件很多,但远不止这些,如何将这些部件装配成一辆汽车,这个装配过程也很复杂(需要很好的组装技术),Builder 模式就是为了将部件和组装过程分开.如何使用如何使用?首先假设一个复杂对象是由多个部件组成的,Builder 模式是把复杂对象的创建和部件的创建分别开来,分别用 Builder 类和 Director 类来表示.首先,需要一个接口,它定义如何创建复杂对象的各个部件:public interface Builder /创建部件 A 比如创建汽车车轮 void buildPartA();/创建部件 B 比如创建汽车方向盘 void buildPartB();/创建部件 C 比如创建汽车发动机 void buildPartC();/返回最后组装成品结果(返回最后装配好的汽车)/成品的组装过程不在这里进行,而是转移到下面的 Director 类中进行./从而实现了解耦过程过程和部件部件 Product getResult();用 Director 构建最后的复杂对象,而在上面 Builder 接口中封装的是如何创建一个个部件(复杂对象是由这些部件组成的),也就是说 Director 的内容是如何将部件最后组装成成品:public class Director private Builder builder;public Director(Builder builder)this.builder=builder;/将部件 partA partB partC 最后组成复杂对象 /这里是将车轮 方向盘和发动机组装成汽车的过程 public void construct()builder.buildPartA();builder.buildPartB();builder.buildPartC();Builder 的具体实现 ConcreteBuilder:通过具体完成接口 Builder 来构建或装配产品的部件;定义并明确它所要创建的是什么具体东西;提供一个可以重新获取产品的接口:public class ConcreteBuilder implements Builder Part partA,partB,partC;public void buildPartA()/这里是具体如何构建 partA 的代码 ;public void buildPartB()/这里是具体如何构建 partB 的代码 ;public void buildPartC()/这里是具体如何构建 partB 的代码 ;public Product getResult()/返回最后组装成品结果 ;复杂对象:产品 Product:public interface Product 复杂对象的部件:public interface Part 我们看看如何调用 Builder 模式:ConcreteBuilder builder=new ConcreteBuilder();Director director=new Director(builder);director.construct();Product product=builder.getResult();Builder 模式的应用模式的应用 在 Java 实际使用中,我们经常用到池(Pool)的概念,当资源提供者无法提供足够的资源,并且这些资源需要被很多用户反复共享时,就需要使用池.池实际是一段内存,当池中有一些复杂的资源的断肢(比如数据库的连接池,也许有时一个连接会中断),如果循环再利用这些断肢,将提高内存使用效率,提高池的性能.修改Builder 模式中 Director 类使之能诊断断肢断在哪个部件上,再修复这个部件.设计模式之设计模式之 Prototype 原型模式定义:用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象.Prototype 模式允许一个对象再创建另外一个可定制的对象,根本无需知道任何如何创建的细节,工作原理是:通过将一个原型对象传给那个要发动创建的对象,这个要发动创建的对象通过请求原型对象拷贝它们自己来实施创建。如何使用?因为Java中的提供clone()方法来实现对象的克隆,所以Prototype模式实现一下子变得很简单.以勺子为例:public abstract class AbstractSpoon implements Cloneable String spoonName;public void setSpoonName(String spoonName)this.spoonName=spoonName;public String getSpoonName()return this.spoonName;public Object clone()Object object=null;try object=super.clone();catch(CloneNotSupportedException exception)System.err.println(AbstractSpoon is not Cloneable);return object;有个具体实现(ConcretePrototype):public class SoupSpoon extends AbstractSpoon public SoupSpoon()setSpoonName(Soup Spoon);调用 Prototype 模式很简单:AbstractSpoon spoon=new SoupSpoon();AbstractSpoon spoon2=spoon.clone();当然也可以结合工厂模式来创建 AbstractSpoon 实例。在 Java 中 Prototype 模式变成 clone()方法的使用,由于 Java 的纯洁的面向对象特性,使得在 Java 中使用设计模式变得很自然,两者已经几乎是浑然一体了。这反映在很多模式上,如 Interator 遍历模式。结构模式结构模式 设计模式之设计模式之 Flyweight Flyweight 定义:避免大量拥有相同内容的小类的开销(如耗费内存),使大家共享一个类(元类).为什么使用?面向对象语言的原则就是一切都是对象,但是如果真正使用起来,有时对象数可能显得很庞大,比如,字处理软件,如果以每个文字都作为一个对象,几千个字,对象数就是几千,无疑耗费内存,那么我们还是要求同存异,找出这些对象群的共同点,设计一个元类,封装可以被共享的类,另外,还有一些特性是取决于应用(context),是不可共享的,这也Flyweight 中两个重要概念内部状态 intrinsic 和外部状态 extrinsic 之分.说白点,就是先捏一个的原始模型,然后随着不同场合和环境,再产生各具特征的具体模型,很显然,在这里需要产生不同的新对象,所以 Flyweight 模式中常出现 Factory 模式.Flyweight 的内部状态是用来共享的,Flyweight factory 负责维护一个Flyweight pool(模式池)来存放内部状态的对象.Flyweight模式是一个提高程序效率和性能的模式,会大大加快程序的运行速度.应用场合很多:比如你要从一个数据库中读取一系列字符串,这些字符串中有许多是重复的,那么我们可以将这些字符串储存在Flyweight池(pool)中.如何使用?我们先从 Flyweight 抽象接口开始:public interface Flyweight public void operation(ExtrinsicState state);/用于本模式的抽象数据类型(自行设计)public interface ExtrinsicState 下面是接口的具体实现(ConcreteFlyweight),并为内部状态增加内存空间,ConcreteFlyweight 必须是可共享的,它保存的任何状态都必须是内部(intrinsic),也就是说,ConcreteFlyweight 必须和它的应用环境场合无关.public class ConcreteFlyweight implements Flyweight private IntrinsicState state;public void operation(ExtrinsicState state)/具体操作 当然,并不是所有的 Flyweight 具体实现子类都需要被共享的,所以还有另外一种不共享的 ConcreteFlyweight:public class UnsharedConcreteFlyweight implements Flyweight public void operation(ExtrinsicState state)Flyweight factory 负责维护一个 Flyweight 池(存放内部状态),当客户端请求一个共享 Flyweight 时,这个 factory 首先搜索池中是否已经有可适用的,如果有,factory 只是简单返回送出这个对象,否则,创建一个新的对象,加入到池中,再返回送出这个对象.池 public class FlyweightFactory /Flyweight pool private Hashtable flyweights=new Hashtable();public Flyweight getFlyweight(Object key)Flyweight flyweight=(Flyweight)flyweights.get(key);if(flyweight=null)/产生新的 ConcreteFlyweight flyweight=new ConcreteFlyweight();flyweights.put(key,flyweight);return flyweight;至此,Flyweight 模式的基本框架已经就绪,我们看看如何调用:FlyweightFactory factory=new FlyweightFactory();Flyweight fly1=factory.getFlyweight(Fred);Flyweight fly2=factory.getFlyweight(Wilma);.从调用上看,好象是个纯粹的 Factory 使用,但奥妙就在于 Factory 的内部设计上.Flyweight模式在XML等数据源中应用 我们上面已经提到,当大量从数据源中读取字符串,其中肯定有重复的,那么我们使用Flyweight 模式可以提高效率,以唱片 CD 为例,在一个 XML 文件中,存放了多个 CD 的资料.每个 CD 有三个字段:1.出片日期(year)2.歌唱者姓名等信息(artist)3.唱片曲目(title)其中,歌唱者姓名有可能重复,也就是说,可能有同一个演唱者的多个不同时期 不同曲目的 CD.我们将歌唱者姓名作为可共享的 ConcreteFlyweight.其他两个字段作为UnsharedConcreteFlyweight.首先看看数据源 XML 文件的内容:Another Green World 1978 Eno,Brian Greatest Hits 1950 Holiday,Billie Taking Tiger Mountain(by strategy)1977 Eno,Brian .虽然上面举例 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越多,空间节省也就越大.设计模式之设计模式之 Bridge Bridge 模式定义模式定义:将抽象和行为划分开来,各自独立,但能动态的结合。任何事物对象都有抽象和行为之分,例如人,人是一种抽象,人分男人和女人等;人有行为,行为也有各种具体表现,所以,“人”与“人的行为”两个概念也反映了抽象和行为之分。在面向对象设计的基本概念中,对象这个概念实际是由属性和行为两个部分组成的,属性我们可以认为是一种静止的,是一种抽象,一般情况下,行为是包含在一个对象中,但是,在有的情况下,我们需要将这些行为也进行归类,形成一个总的行为接口,这就是桥模式的用处。为什么使用为什么使用?不希望抽象部分和行为有一种固定的绑定关系,而是应该可以动态联系的。如果一个抽象类或接口有多个具体实现(子类、concrete subclass),这些子类之间关系可能有以下两种情况:1.这多个子类之间概念是并列的,如前面举例,打桩,有两个 concrete class:方形桩和圆形桩;这两个形状上的桩是并列的,没有概念上的重复。2.这多个子类之中有内容概念上重叠.那么需要我们把抽象共同部分和行为共同部分各自独立开来,原来是准备放在一个接口里,现在需要设计两个接口:抽象接口和行为接口,分别放置抽象和行为.例如,一杯咖啡为例,子类实现类为四个:中杯加奶、大杯加奶、中杯不加奶、大杯不加奶。但是,我们注意到:上面四个子类中有概念重叠,可从另外一个角度进行考虑,这四个类实际是两个角色的组合:抽象 和行为,其中抽象为:中杯和大杯;行为为:加奶 不加奶(如加橙汁 加苹果汁).实现四个子类在抽象和行为之间发生了固定的绑定关系,如果以后动态增加加葡萄汁的行为,就必须再增加两个类:中杯加葡萄汁和大杯加葡萄汁。显然混乱,扩展性极差。那我们从分离抽象和行为的角度,使用 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 ext