有关于面向对象的继承,我相信大家都已经能熟练的运用了,博主也不例外。在博主日常撸代码过程中,只要可以用继承的地方,我都会用上继承,因为它的功能确实强大!但是也有很多情况下,用继承也会带来麻烦。下面还是借桥接模式之手来阐述一下。
我这里用笔画画作为例子。
比如说,我这里用不同的笔(圆珠笔、铅笔)绘制不同的形状。为了更加形象起见,我这里将不同的情况细化至具体类的方法。
先来看下代码的结构图:
下面来看下具体的实现过程:
1. 创建抽象父类 Pen
public abstract class Pen {
private String name;
protected Pen(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public abstract void draw();
}
2. 分别创建抽象子类 Pencil、BallPen继承抽象父类 Pen
public abstract class Pencil extends Pen {
protected Pencil() {
super("铅笔");
}
}
public abstract class BallPen extends Pen {
protected BallPen() {
super("圆珠笔");
}
}
3. 对应的创建其具体的实现子类 CircleWithPencil、CircleWithBallPen、SquareWithPencil、SquareWithBallPen
public class CircleWithPencil extends Pencil {
@Override
public void draw() {
System.out.println(getName() + "画圆");
}
}
public class CircleWithBallPen extends BallPen {
@Override
public void draw() {
System.out.println(getName() + "画圆");
}
}
public class SquareWithPencil extends Pencil {
@Override
public void draw() {
System.out.println(getName() + "画正方形");
}
}
public class SquareWithBallPen extends BallPen {
@Override
public void draw() {
System.out.println(getName() + "画正方形");
}
}
4. 测试类测试
public class Test {
public static void main(String[] args) {
Pen p;
p = new CircleWithBallPen();
p.draw();
p = new CircleWithPencil();
p.draw();
p = new SquareWithBallPen();
p.draw();
p = new SquareWithPencil();
p.draw();
}
}
其运行结果如下:
圆珠笔画圆
铅笔画圆
圆珠笔画正方形
铅笔画正方形
上述代码一切看似很正常,没什么毛病对不对。但是你会发现一个问题,当我们需要扩展一项内容,比如说,用各种笔来绘制直线,那我们就需要在上方抽象子类 Pencil、BallPen中各自增加 LineWithPencil类和 LineWithBallPen类;或者说,我用彩笔来绘制各种图形,那我们需要新增一个抽象子类 ColourPen类, 然后写出其具体子类 CircleWithColourPen类和 SquareWithColourPen类。
到这里也许你会疑问:这好像也没什么问题啊。
的确,是没什么问题。如果我同时增加多种笔和多种形状,你会看到一个现象:我每增加一个笔的类型,在该笔的具体子类下你就得画出所有已经存在的形状;反之,每增加一种形状,在所有已经存在的笔的类型下你都要去实现这个形状的类。所以,其结果就会导致类成倍的增加。这种设计就使得程序代码变得相当臃肿,可见继承在这里给我们带来的麻烦。
我们再来回顾上述的代码,在这里,变化的主要有两个部分,一个分部是笔,另外一个部分则是图形,针对这种多角度变化的问题,我们则使用桥接模式,来把这种多角度分离出来,让它们独立的变化,减少其之间的耦合。
下面我们来看下桥接模式的做法。
桥接模式: 将抽象部分与它的实现部分分离,使它们都可以独立地变化。
我们对上述实例的代码做一些修改。
先来看下修改后代码的结构图:
下面来看下具体的代码:
1. 创建抽象父类 Shape
public abstract class Shape {
public abstract String draw();
}
2. 创建Shape子类实现类 Circle、Square
public class Circle extends Shape {
@Override
public String draw() {
return "画圆";
}
}
public class Square extends Shape {
@Override
public String draw() {
return "画正方形";
}
}
3. 创建抽象父类 Pen
public abstract class Pen {
private String name;
private Shape shape;
protected Pen(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Shape getShape() {
return shape;
}
public void setShape(Shape shape) {
this.shape = shape;
}
protected String drawWhat() {
return shape.draw();
}
public abstract void draw();
}
4. 创建Pen子类实现类Pencil、BallPen
public class Pencil extends Pen {
public Pencil() {
super("铅笔");
}
@Override
public void draw() {
System.out.println(getName() + drawWhat());
}
}
public class BallPen extends Pen {
public BallPen() {
super("圆珠笔");
}
@Override
public void draw() {
System.out.println(getName() + drawWhat());
}
}
5. 创建桥接测试类测试下
public class BridgeTest {
public static void main(String[] args) {
Pen p;
p = new BallPen();
p.setShape(new Circle());
p.draw();
p.setShape(new Square());
p.draw();
p = new Pencil();
p.setShape(new Circle());
p.draw();
p.setShape(new Square());
p.draw();
}
}
运行的结果如下:
圆珠笔画圆
圆珠笔画正方形
铅笔画圆
铅笔画正方形
1. Abstraction: 定义抽象类的接口。维护一个指向Implementor类型对象的指针。类似上述的Pen类。
2. RefinedAbstraction: 扩充由Abstraction定义的接口。类似上述的Pencil、BallPen类。
3. Implementor: 定义实现类的接口,该接口不一定要与Abstraction的接口完全的一致。类似上述的Shape类。
4. ConcreteImplementor: 实现Implementor接口并定义它的具体实现。类似上述的Circle、Square类。
1. 抽象和实现的分离。
2. 优秀的扩展能力。
3. 实现细节对客户透明。
桥接模式通过使用封装、聚合以及继承等行为来让不同的类承担起不同的责任,将抽象与行为的实现分离开来,保持各部分的独立性,以便其扩展。
其实桥接模式并不难,只是我自己写的有点啰嗦,还请各位多担待。