正确理解“桥接模式”

首先分析桥接模式的目的:用于将类的功能层次结构实现层次结构分离开来;使之能相对独立的变化;

功能层次结构:

比如:

//父类
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+,

猜你喜欢

转载自blog.csdn.net/WK_SDU/article/details/82191250