首先分析桥接模式的目的:用于将类的功能层次结构与实现层次结构分离开来;使之能相对独立的变化;
两个名词的解释:
实现层次结构:
比如你定义了一个接口(或者抽象类)Flyable,内部定义了void fly(),你是用FlyWithSwing这个类去实现了这个接口,并未增加相对Flyable的新的增强的方法,关于这一点,什么事增强方法,不是随便在实现类里边定义一个接口没声明的方法(往往是public的)就叫增强方法,而是你实现的功能是在原接口之上的实现的增强,以Flyable为例,FlyWithSwing代表使用翅膀去飞,只是接口的实现,你要是实现了void flyStably(),或者void flyFaster(),这叫功能上增强了
功能层次结构:如上,新的继承类实现了相对原接口的实现方法,就叫功能层次结构;
一般的说,实现层次结构的相关的类和功能层次结构的类不应当为一个(如果你实现的功能十分简单并且可以预料未来不会对其进行修改或者扩充),这也是为什么往往需要先定义一个接口,然后在定义一个接口实现类而不是直接给一个相关接口实现的原因之一;你得考虑到未来的改变,维护
比如:
//父类
public abstract class Something {
abstract void doSomething();
}
public abstract class SomethingBetter extends Something {
//新增加的方法,相当于增加功能,这个子类与父类构成功能层次结构
public void doSomethingBetter(){}
}
public class SomethingImpl extends Something {
//对父类的一种实现,可能会有多种实现,属于实现层次结构
@Override
void doSomething() {
//do something in here
}
}
为了简单,将父类成为Father 将实现功能的子类成为SubImpl,将用以扩展的子类称为SubEx;
当增加新功能时,为了不修改之前的代码(开放-关闭原则),你只能去继承SubEx(你要是去继承Father再增加SubEx中的功能做不到代码复用,当然了你认为是可以容忍的,这么做也没任何坏处),为了适应新功能,对每个SubImpl也必须去建立子类(代码复用)而且去实现SubEx的子类(到这你就必须使用组合了,因为java不允许多继承),做法相当混乱,代码结构也很混乱,当然你要是说你能容忍,好吧,那所有的设计模式几乎对你没任何作用,等着被后面人或者你后来维护代码的时候叫苦吧
经上分析,你不可避免的使用组合,那为什么不好好看看桥接模式呢,他正是用以解决此类问题
桥接模式的类图是大约是这样
拓展思路的关键是在父类中安插一个父类对象,代表当前的类的实现层次结构做法,对该属性往往还提供setter方法,以实现动态更换实现;当想要增加新功能时,只需要在类的功能层次结构一侧增加类即可,而不必对“类的实现层次结构”做修改了,而且增加的功能可以为“所有的实现使用”;
与其他像策略,装饰者等等类似的,该模式也是使用组合来达到目的,不同的是策略封装的是做某个动作的一组算法,从实现上看桥接方法与装饰者类图极为相似,但是区别在于:
1。桥接模式中所说的分离,其实是指将结构与实现分离(当结构和实现有可能发生变化时)或属性与基于属性的行为进行分离;而装饰者只是对基于属性的行为进行封闭成独立的类。
2。桥接中的行为是横向的行为,行为彼此之间无关联;而装饰者模式中的行为具有可叠加性,其表现出来的结果是一个整体,一个各个行为组合后的一个结果。
稍微记录下泛型的一点理解:
public class SomethingImpl {
public <T> T func(Class clazz) throws IllegalAccessException, InstantiationException {
return (T) clazz.newInstance();
}
public <T> T funcss(Class<T> clazz) throws IllegalAccessException, InstantiationException {
return (T)clazz.newInstance();
}
public <T> T funcs(Class<T> clazz) throws IllegalAccessException, InstantiationException {
return clazz.newInstance();
}
}
注意到该类是可以编译通过的,但是这两种做法区别是什么呢? 方法前的<T>又是什么意思?
之前查帖子说方法前的<T>是告诉编译器你要使用泛型方法(好蠢的解释,没到跟上),事实上没准是有这个解释也说不定,但是我觉得并不是,这个<T>是用来捕获你所写的泛型实例,这句话可能不太好理解,想想看类声明的时候,会有类似这样的声明
public class SomethingImpl<T>
做了什么事情呢,编译器检查到<T>会捕获你实际在<>号里面放的类型,并将其它T替换成这个来进行编译期检查,在你所有代码中必须有个地方声明捕获<T>,否则将无法使用泛型,因为编译器无法处理你莫名其妙的T(<>比较特殊,是个符号,语法规则上能实现,你写个T啥意思,难不成你有个名字为T的类?)
一旦明确声明<T> 你声明的T就可以使用了;
现在有问题了,你可以将<T>声明在类上,表示该类中所有声明T的地方搜使用<T>中的T代替,可是我想进一步缩小范围怎么办,我想只用在方法级别上,也成,在方法前面声明<T>表示该方法所有声明T的地方都使用<T>中的T代替,那么现在就有问题了
1.咋用啊(client怎么使用该方法啊,毕竟你只在源代码处写了,调用代码处没真正使用<T>去捕获某某类型啊),编译器怎么处理的?
请看上面的泛型示例代码,注意看其中差别,方法一的使用方法;
SomethingImpl impl=new SomethingImpl();
try {
Class stuClass=Student.class;
//方法一
Student student=impl.func(stuClass);
//与上面的区别是参数上,一个带有泛型,一个不带,但是没报编译错误
Student student1=impl.func(Student.class);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
有上面分析知,你可能必须声明一个<RealType>来捕获泛型,但是上面仍可编译通过,说明一个问题,就是泛型方法不能从参数中捕获时,将会对调用方法的返回值(实际上是=左侧声明)进行捕获;这就涉及到了捕获次序问题,是先捕获方法参数还是返回值的泛型呢?,进一步探索发现
SomethingImpl impl=new SomethingImpl();
try {
//方法二 报错了,右侧红线,麻痹csdn显示不出来
Something something=impl.funcs(Student.class); //Student.class是带泛型的
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
说明是优先捕获方法参数<>的泛型,没有的时候才会捕获返回值
但是要是在类层次<>捕获了,又在泛型方法上进行捕获时会发生什么?
新的SomethingImpl代码
public class SomethingImpl<T> {
public T funcss(Class<T> clazz) throws IllegalAccessException, InstantiationException {
return (T)clazz.newInstance();
}
public <T> T funcs(Class<T> clazz) throws IllegalAccessException, InstantiationException {
return clazz.newInstance();
}
}
源码声明是没问题的
调用代码如下:
public static void main(String[] args) {
SomethingImpl<Student> impl=new SomethingImpl();
try {
Class clazz=Student.class;
//第一个方法,报错了
Student student1=impl.funcs(clazz);
//报错
Student student4=impl.funcs(SomethingImpl.class);
//此时第一个,第二个均不会报错
Student student2=impl.funcs(Student.class);
Student student3=impl.funcss(Student.class);
//第二个方法,不报错
Student student= impl.funcss(clazz);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
说明一件事,就是<T>在整个类的调用时期只能捕获一次且必须显式声明捕获,即有<T>标志,顺序是参数>类>返回值(=右侧)
关于泛型类型推断需要java7+,
-----------------------------------------------------------------------分割线--------------------------------------------------------------------------
上述有关泛型的讨论是java语言规范(java language specification,简称JLS)的内容,关键字是类型推导,是我学艺不精。。。。