java编程思想第四版第九章总结
本章非常重要, 里面的内容涉及到了三个设计模式, 以及接口的有点,掌握这些就是掌握了重点 1. 策略设计模式
和模板方法模式的区别:
文章里还有一个例子: ?
备注:我来分解,解释一下这个例子。 将共同的方法定义成了一个接口,在这个接口中并没有这个共同方法的实现。 在Strategy类中,定义了一个方法execute,它的参数是拥有共同方法的接口类。 用户Context在调用Strategy的execute方法时,在定义这个共同的方法。 这样就大大提高了灵活性。因为公共方法也是可以后定义的。而不是在创建类的时候就定义好了。 下面把这个例子敲成代码 package net.mindview.interfaces; //公共方法回调类 @H_301_79@interface SameCallback{ void doTheSame(); } 定义了一个策略类 Strategy { execute(SameCallback sc); } 定义策略实现类 class concreteStrategy1 implements Strategy { @Override public execute(SameCallback sc) { sc.doTheSame(); } } concreteStrategy2 implements Strategy { @Override concreteStrategy3 implements Strategy { @Override execute(SameCallback sc) { sc.doTheSame(); } } Context { static main(String[] args) { Strategy strategy = new concreteStrategy1(); 这个公共方法类 SameCallback sc = SameCallback() { @Override doTheSame() { System.out.println("do same things"); } }; 那个策略需要用到公共方法类,调用即可,如果不用,那就不在方法中小勇 strategy.execute(sc); strategy = concreteStrategy2(); strategy.execute(sc); } } ? ? ? ?
思考: 如果一段代码中, 总会出现很多的if...else或者case,就要考虑使用策略设计模式了
?2. 解耦
? package net.mindview.interfaces.filters2; import net.mindview.interfaces.classprocessor2.Processor; * * 由于Filter中的process的输入输出参数都是Waveform,而Processor中process的输入 * 输出参数都是Object. 因此,写一个适配器,让Filter可以适配Processor类型的接口 * */ FilterAdapter implements Processor{ Filter filter; FilterAdapter(Filter filter){ this.filter = filter; } @Override String name() { filter.name(); } @Override Object process(Object input) { filter.process((Waveform)input); } } 接下来看看Apply类如何处理的? main(String[] args) { System.======处理字符串===========); process(======过滤波===========new FilterAdapter(new LowPass(1.0)),1)"> Waveform()); process(new HighPass(100.0)),1)">new BandPass(1.0,10.0)),1)"> Waveform()); } } 上面这个例子就示范了使用接口的好处,让类和方法解耦。同时还涉及到一个设计模式,叫适配器设计模式。适配器设计模式有三种,确切的说这是对象适配器模式. ?3. 适配器设计模式 参考文章:http://blog.csdn.net/zxt0601/article/details/52848004 定义:适配器模式将某个类的接口转换成客户端期望的另一个接口表示,主的目的是兼容性,让原本因接口不匹配不能一起工作的两个类可以协同工作。其别名为包装器(Wrapper)。
属于结构型模式 主要分为三类:类适配器模式、对象的适配器模式、接口的适配器模式。 本文做以下约定
一句话描述适配器模式:src->adapter->dist,即src以某种形式(类,对象,接口)给到适配器,适配器最终输出dist。 ? 使用 场景
第一类: 类适配器模式 一句话描述使用方法:Adapter类,通过继承src类,实现dist接口,完成src -> dist的适配.? 以充电器为例:充电器本身就是一个适配器。输入的时220v电压, 输出的是5v电压。 首先, 有一个src类,本身是输出220v电压 package net.mindview.interfaces.adaptor; * * 这是目前有的电压 200v * @author samsung * Voltage220 { int output(){ int i = 220; System.这是220v电压); i; } } 然后, 有一个目标使用电压是5v * * 我们需要使用的目标电压是5v * 这是一个5v电压的接口 * @author samsung * Voltage5 { output(); } 定义一个适配器, 将220v电压经过处理后转换为5v电压供用户使用 * * 适配器,将220v电压输出为5v电压 * 适配器 继承src类,实现dist类,实现从src->dist的转换 VoltageAdapter extends Voltage220 implements Voltage5{ @Override output() { int src = super.output(); System.适配器工作开始适配电压int dist = src/44适配完成后输出电压: dist); dist; } } 有一款手机需要充电,充电电压是5v Mobile { chargeing(Voltage5 v5){ if(v5.output() == 5){ System.正常充电); } else { System.秒变note7); } } } 使用手机的时候,需要将家里的220v电压转换为5v输出.调用适配器 main(String[] args) { Mobile m = Mobile(); m.chargeing( VoltageAdapter()); } 输出: ===============类适配器============== 我是220V 适配器工作开始适配电压 适配完成后输出电压: 电压刚刚好5V,开始充电
小结:Java这种单继承的机制,所有需要继承的我个人都不太喜欢。? 但同样由于其继承了src类,所以它可以根据需求重写src类的方法,使得Adapter的灵活性增强了。 第二类: 对象适配器模式 基本思路和类适配器一样,只是将adapter类做了修改,这次不继承src类,而是持有src类实例对象,以解决兼容性问题。 一句话总结使用方法:持有src类,实现dist类接口, 实现src -> dist的适配 (根据"合成复用规则",在系统中尽量使用组合来代替继承) package net.mindview.interfaces.adaptor; * * 持有src类,实现dist类接口, 实现src -> dist的适配 * @author samsung * VoltageAdapter2 implements Voltage5{ Voltage220 voltage220; VoltageAdapter2(Voltage220 voltage220){ this.voltage220 = voltage220; } @Override output() { System.得到220v电压 voltage220.output(); System.经过处理,输出5v电压最终使用的电压是"+dist+v dist; } } 测试结果: 得到220v电压
这是220v电压
经过处理,输出5v电压
最终使用的电压是5v
正常充电
小结:对象适配器和类适配器其实算是同一种思想,只不过实现方式不同。? ? ? 第三类 接口适配器模式 ? 也有文献称之为认适配器模式(Default Adapter Pattern)或缺省适配器模式。? 我们直接进入大家最喜爱的源码撑腰环节: 源码撑腰环节:Android中的属性动画 ValueAnimator valueAnimator = ValueAnimator.ofInt(0,1)">100); valueAnimator.addListener( Animator.AnimatorListener() { @Override onAnimationStart(Animator animation) { } @Override onAnimationEnd(Animator animation) { } @Override onAnimationCancel(Animator animation) { } @Override onAnimationRepeat(Animator animation) { } }); valueAnimator.start(); ? 有时候我们不想实现 ValueAnimator valueAnimator = ValueAnimator.ofInt( AnimatorListenerAdapter() { @Override onAnimationStart(Animator animation) { xxxx具体实现 } }); valueAnimator.start(); 显然,这个 abstract AnimatorListenerAdapter implements Animator.AnimatorListener,Animator.AnimatorPauseListener { @Override onAnimationCancel(Animator animation) { } @Override onAnimationEnd(Animator animation) { } @Override onAnimationRepeat(Animator animation) { } @Override onAnimationStart(Animator animation) { } @Override onAnimationPause(Animator animation) { } @Override onAnimationResume(Animator animation) { } } 可见,它空实现了 AnimatorListener { onAnimationStart(Animator animation); onAnimationEnd(Animator animation); onAnimationCancel(Animator animation); onAnimationRepeat(Animator animation); } ? 类图: 我们程序里的匿名内部类就是Listener1 2 这种具体实现类。 }
}
? 接口适配器模式很好理解,令我们的程序更加简洁明了。 总结我个人理解,三种命名方式,是根据 src是以怎样的形式给到Adapter(在Adapter里的形式)来命名的。? Adapter模式最大的作用还是将原本不兼容的接口融合在一起工作。? Client是Lv Gv Rv ,它们是显示View的类。? ?4. 适配接口 适配接口这一节,主要是再次强调了接口的好处. 可以使用策略设计模式,在方法实际参数中调用接口,用户使用的时候就可以根据实际情况传递实现了该接口的实现类. 灵活性更高. 同时,课本中举了一个例子,Scanner的read方法中调用的就是readable接口。如果我想使用Scanner类(这时java类库中的类),有两种方式。
针对这两个方法,我们来看案例: 案例一(对应的方式一)? package net.mindview.interfaces; import java.io.IOException; import java.nio.CharBuffer; import java.util.Random; import java.util.Scanner; 随机单次 RandomWords implements Readable{ 首先生成一个全局的随机数. 特殊说明:创建一个Random对象的时候可以给定任意一个合法的种子数,种子数只是随机算法的起源数字,和生成的随机数的区 间没有任何关系。其中47就是种子,那么为什么是作者要选择47作为种子呢? 原因如下: 由47做种后,产生的随机数更加体现了随机性。它没有什么具体的意义,只要理解随机数如果有一个种子, 那么会出现比较随机的随机数,而当种子是47的时候,随机率是最大的。 static Random rand = new Random(46); 定义首字母,字母的范围已固定,不会改变,故定义为static final static final char[] captials = ABCDEFGHIJKLMNOPQRSTUVWXYZ.tocharArray(); 小写字母 char[] lowers = abcdefghijklmnopqrstuvwxyz元音字母 char[] vowels = aeIoU.tocharArray(); count; 计数,一共生成几个随机单词 public RandomWords( count){ this.count = count; } @Override read(CharBuffer cb) throws IOException { if(count-- == 0) { return -1; } 生成一个大写字母 cb.append(captials[rand.nextInt(captials.length)]); 生成一个原因一个辅音 for(int i=0; i<4; i++){ cb.append(vowels[rand.nextInt(vowels.length)]); cb.append(lowers[rand.nextInt(lowers.length)]); } cb.append(10;返回字符数 } main(String[] args) { Scanner s = new Scanner(new RandomWords(4)); while(s.hasNext()){ System..println(s.next()); } } } 案例二(对应方式二) 现在有一个写好的了,随机生成浮点数 package net.mindview.interfaces; import java.util.Random; 随机生成浮点数 RandomDoubles { 47 next(){ rand.nextDouble(); } main(String[] args) { RandomDoubles rd = RandomDoubles(); 7 ;i++out.println(rd.next() + ); } } } 下面我们使用适配器设计模式写一个类来进行适配。适配器模式有三种种, 这里是用的是类适配器模式, 还可以使用对象适配器模式 package net.mindview.interfaces; import java.io.IOException; import java.nio.CharBuffer; import java.util.Scanner; AdapterRandomDoubles extends RandomDoubles implements Readable{ count; public AdapterRandomDoubles( count; } @Override ){ ; } String result = Double.toString(next()) + ; cb.append(result); result.length(); } new AdapterRandomDoubles(7(s.hasNextDouble()){ System.out.println(s.nextDouble()+); } } } 下面使用的是对象适配器模式, 和类适配器模式基本相似。 AdapterRandomDoubles implements Readable{ private RandomDoubles rd; public AdapterRandomDoubles(RandomDoubles rd,1)">this.rd = rd; ; } String result = Double.toString(rd.next()) + new AdapterRandomDoubles(new RandomDoubles(),1)">); } } } 总结: 使用适配器模式, 我们可以在任何现有类之上添加类的接口,所以这意味着让方法接收接口类型,是一种让任何类都可以对该方法进行适配的方式。这就是使用接口而不使用类的强大之处。 4.本章设计的最后一个设计模式: 工厂设计模式 参考文章:?https://wenku.baidu.com/view/8293cb68302b3169a45177232f60ddccda38e62c.html### ? ? ? 在面向对象编程中,最通常的方法是一个new操作符产生一个对象实例,new操作符就是用来构造对象实例的。但是在一些情况下,new操作符直接生成对象会带来一些问题。举例来说,许多类型对象的创造需要一系列的步骤: 你可能需要计算或取得对象的初始设置; 选择生成哪个子对象实例; 或在生成你需要的对象之前必须先生成一些辅助功能的对象。 在这些情况,新对象的建立就是一个 “过程”,不仅是一个操作,像一部大机器中的一个齿轮传动。 模式的问题:你如何能轻松方便地构造对象实例,而不必关心构造对象实例的细节和复杂过程呢? 解决方案:建立一个工厂来创建对象 实现: 一、引言 ?? 4)抽象工厂模式时代:随着客户的要求越来越高,宝马车必须配置空调。于是这个工厂开始生产宝马车和需要的空调。 最终是客户只要对宝马的销售员说:我要523i空调车,销售员就直接给他523i空调车了。而不用自己去创建523i空调车宝马车. 这就是工厂模式。 二、分类? 1)简单工厂模式(Simple Factory)? 这三种模式从上到下逐步抽象,并且更具一般性。? ??????? 将简单工厂模式(Simple Factory)看为工厂方法模式的一种特例,两者归为一类。 三、区别?
抽象工厂模式: ?
区别: ? 四、简单工厂模式?
? package net.mindview.interfaces.factory; BMW320 { BMW320(){ } make(){ System.制造-->BMW320); } } BMW523 { BMW523(){} 制造-->BMW523 Customer { main(String[] args) { * * 我现在需要一辆车BMW320 * 在没有工厂的时候,我学要自己制造这辆车 BMW320 bmw320 = BMW320(); bmw320.make(); * * 我现在需要一辆车BMW523 * 在没有工厂的时候,1)"> BMW523 bmw523 = BMW523(); bmw523.make(); } } 客户需要知道怎么去创建一款车,客户和车就紧密耦合在一起了.? 为了降低耦合,就出现了工厂类, 把创建宝马的操作细节都放到了工厂里面去,客户直接使用工厂的创建工厂方法,传入想要的宝马车型号就行了,而不必去知道创建的细节.这就是工业革命了:简单工厂模式 即我们建立一个工厂类方法来制造新的对象。如图: ? ? package net.mindview.interfaces.factory.simplefactory; 产品类 BMW{ } BMW320 extends BMW{ BMW320(){ System.制造-->BMW 320 BMW530 extends BMW{ BMW530(){ System.制造-->BMW530工厂类 Factory { int TYPE_BMW_320 = 320; int TYPE_BMW_530 = 530static BMW make( type){ switch(type){ case : return BMW320(); BMW530(); defaultnull; } } } 我想要一辆车 BMW b320 = Factory.make(Factory.TYPE_BMW_320); BMW b530 = Factory.make(Factory.TYPE_BMW_530); } } 简单工厂模式又称静态工厂方法模式。重命名上就可以看出这个模式一定很简单。它存在的目的很简单:定义一个用于创建对象的接口。??????????????????????????? 先来看看它的组成:? ? ? ? ? ?我们举的例子是最简单的情况,而在实际应用中,很可能产品是一个多层次的树状结构。由于简单工厂模式中只有一个工厂类来对应这些产品,所以这可能会把我们的上帝累坏了,也累坏了我们这些程序员。 ? 五、工厂方法模式?
? ? ? ? 工厂方法模式使用继承自抽象工厂角色的多个子类来代替简单工厂模式中的“上帝类”。正如上面所说,这样便分担了对象承受的压力;而且这样使得结构变得灵活 起来——当有新的产品产生时,只要按照抽象产品角色、抽象工厂角色提供的合同来生成,那么就可以被客户使用,而不必去修改任何已有 的代码。可以看出工厂角色的结构也是符合开闭原则的!? 代码如下:? package net.mindview.interfaces.factory.factoryMethod; 产品 BMW{ } BMW320 implements BMW{ BMW320() { System. BMW530 implements BMW{ BMW530() { System.制造-->BMW 530 BMW680 implements BMW{ BMW680() { System.制造-->BMW 680工厂 BMWFactory{ BMW make(); } BMW320Factory implements BMWFactory{ @Override BMW make() { BMW320(); } } BMW530Factory implements BMWFactory{ @Override BMW530(); } } BMW680Factory implements BMWFactory{ @Override BMW680(); } } 客户类 static BMW serviceCustomer(BMWFactory factory){ factory.make(); } main(String[] args) { BMW bmw320 = serviceCustomer( BMW320Factory()); BMW bmw530 = serviceCustomer( BMW530Factory()); BMW bmw680 = serviceCustomer( BMW680Factory()); } } ?工厂方法模式仿佛已经很完美的对对象的创建进行了包装,使得客户程序中仅仅处理抽象产品角色提供的接口,但使得对象的数量成倍增长。当产品种类非常多时,会出现大量的与之对应的工厂对象,这不是我们所希望的。 本文继续介绍23种设计模式系列之抽象工厂模式。 前面已经介绍过简单工厂模式和工厂方法模式,这里继续介绍第三种工厂模式-抽象工厂模式,还是以汽车的制造为例。 ? 例子背景: 随着客户的要求越来越高,宝马车需要不同配置的空调和发动机等配件。于是这个工厂开始生产空调和发动机,用来组装汽车。这时候工厂有两个系列的产品:空调和发动机。宝马320系列配置A型号空调和A型号发动机,宝马230系列配置B型号空调和B型号发动机。 ? 概念: ?? 抽象工厂模式是工厂方法模式的升级版本,他用来创建一组相关或者相互依赖的对象。比如宝马320系列使用空调型号A和发动机型号A,而宝马230系列使用空调型号B和发动机型号B,那么使用抽象工厂模式,在为320系列生产相关配件时,就无需制定配件的型号,它会自动根据车型生产对应的配件型号A。 ? 针对百度百科上对于抽象工厂模式的简介,结合本例如下: ? 当每个抽象产品都有多于一个的具体子类的时候(空调有型号A和B两种,发动机也有型号A和B两种),工厂角色怎么知道实例化哪一个子类呢?比如每个抽象产品角色都有两个具体产品(产品空调有两个具体产品空调A和空调B)。抽象工厂模式提供两个具体工厂角色(宝马320系列工厂和宝马230系列工厂),分别对应于这两个具体产品角色,每一个具体工厂角色只负责某一个产品角色的实例化。每一个具体工厂类只负责创建抽象产品的某一个具体子类的实例。 ? 抽象工厂模式代码 package net.mindview.interfaces.factory.abstractFactory; Engine{ } Engine320 implements Engine{ Engine320(){ System.我是Engine320发动机 Engine530 implements Engine{ Engine530(){ System.我是Engine530发动机 AirCondition {} AirCondition320 implements AirCondition { AirCondition320(){ System.我是AirCondition320空调 AirCondition530 implements AirCondition { AirCondition530(){ System.我是AirCondition530空调汽车 BMW320(Engine e,AirCondition ma) { System. BMW530(Engine e,1)"> BMWFactory { Engine makeEngine(); AirCondition makeAirCondition(); BMW makeBMW(); } BMWFactory320 implements BMWFactory{ @Override Engine makeEngine() { Engine320(); } @Override AirCondition makeAirCondition() { AirCondition320(); } @Override BMW makeBMW() { BMW320(makeEngine(),makeAirCondition()); } } BMWFactory530 implements BMWFactory{ @Override Engine530(); } @Override AirCondition530(); } @Override BMW530(makeEngine(),makeAirCondition()); } } factory.makeBMW(); } main(String[] args) { BMW b320 = serviceCustomer( BMWFactory320()); BMW b530 = serviceCustomer( BMWFactory530()); } } 运行结果: 我是Engine320发动机 我是AirCondition320空调 制造-->BMW 320 ? ? 关于抽象工厂模式与工厂方法模式的区别,这里就不说了,感觉多看几遍例子就能理解,还有很多提到的产品族、等级结构等概念,说了反而更难理解。 ? ? ? ? (编辑:北几岛) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |