记一次学设计模式的经历:探索结构性模式
组合各种对象。组合与运行期的动态组合。
1 适配器
Adapter模式可以将一个A接口转换为B接口,使得新的对象符合B接口规范。
public BAdapter implements B {
private A a;
public BAdapter(A a) {
this.a = a;
}
public void b() {
a.a();
}
}
在Adapter内部将B接口的调用“转换”为对A接口的调用。
2 桥接
桥接模式实现比较复杂,实际应用也非常少,但它提供的设计思想值得借鉴,即不要过度使用继承,而是优先拆分某些部件,使用组合的方式来扩展功能。
// 抽象类,内部组件是引用一个接口
public abstract class Car {
// 引用Engine:
protected Engine engine;
public Car(Engine engine) {
this.engine = engine;
}
public abstract void drive();
}
// 引擎接口,每一种引擎实现接口
public interface Engine {
void start();
}
// 修正的抽象类,可以定义额外操作
public abstract class RefinedCar extends Car {
public RefinedCar(Engine engine) {
super(engine);
}
public void drive() {
this.engine.start();
System.out.println("Drive " + getBrand() + " car...");
}
public abstract String getBrand();
}
// 实现类继承 修正的抽象类
public class BossCar extends RefinedCar {
public BossCar(Engine engine) {
super(engine);
}
public String getBrand() {
return "Boss";
}
}
// 客户端通过自己选择一个品牌,再配合一种引擎,得到最终的Car
RefinedCar car = new BossCar(new HybridEngine());
car.drive();
┌───────────┐
│ Car │
└───────────┘
▲
│
┌───────────┐ ┌─────────┐
│RefinedCar │ ─ ─ ─>│ Engine │
└───────────┘ └─────────┘
▲ ▲
┌────────┼────────┐ │ ┌──────────────┐
│ │ │ ├─│ FuelEngine │
┌───────┐┌───────┐┌───────┐ │ └──────────────┘
│BigCar ││TinyCar││BossCar│ │ ┌──────────────┐
└───────┘└───────┘└───────┘ ├─│ElectricEngine│
│ └──────────────┘
│ ┌──────────────┐
└─│ HybridEngine │
└──────────────┘
3 组合
树结构。叶子节点统一到一个接口。
┌───────────┐
│ Node │
└───────────┘
▲
┌────────────┼────────────┐
│ │ │
┌───────────┐┌───────────┐┌───────────┐
│ElementNode││ TextNode ││CommentNode│
└───────────┘└───────────┘└───────────┘
使用node 操作
Node root = new ElementNode("school");
root.add(new ElementNode("classA")
.add(new TextNode("Tom"))
.add(new TextNode("Alice")));
root.add(new ElementNode("classB")
.add(new TextNode("Bob"))
.add(new TextNode("Grace"))
.add(new CommentNode("comment...")));
System.out.println(root.toXml());
4 装饰器
把核心功能和附加功能给分开了。在运行期动态给某个对象的实例增加功能的方法。
IO
- 最顶层的Component是接口,对应到IO的就是InputStream这个抽象类。
- ComponentA、ComponentB是实际的子类,对应到IO的就是FileInputStream、ServletInputStream这些数据源。
- Decorator是用于实现各个附加功能的抽象装饰器,对应到IO的就是FilterInputStream。
- 从Decorator派生的就是一个一个的装饰器,它们每个都有独立的功能,对应到IO的就是BufferedInputStream、GZIPInputStream等。
┌───────────┐
│ Component │
└───────────┘
▲
┌────────────┼─────────────────┐
│ │ │
┌───────────┐┌───────────┐ ┌───────────┐
│ComponentA ││ComponentB │... │ Decorator │
└───────────┘└───────────┘ └───────────┘
▲
┌──────┴──────┐
│ │
┌───────────┐ ┌───────────┐
│DecoratorA │ │DecoratorB │...
└───────────┘ └───────────┘
与桥接的区别:
装饰器增加的是功能,功能不是单一的,可以同时存在。所以功能可以一层一层的附加。桥接增加的是某个组件的属性,多种可选一个,是确定的。
装饰器和桥接的目的都是降低继承中衍生的子类的数量,
桥接是通过把一个组件及其子类作为另一总体的字段引用实现功能组合,也可以用多个组件来拼合总体
装饰器则在大类下创建一个装饰器的子族,不管是主要部件还是装饰器都隶属于这个大类,所以装饰器可以不断嵌套
看起来桥接和装饰器的使用都是层层调用,但是两者的功能不同,桥接的子类是负责总体的局部功能,是构成性的
而装饰器则是对已经具有了完整功能的总体进行修饰,是附加性的。
相当于雪中送炭和锦上添花的区别。
5 外观
中介。客户端只和中介打交道。
创建一个中介类。中介类去走所有的流程。
6 享元
一经创建就不可变的对象实例,直接向调用方返回一个共享的实例就行
包装类型如Byte
、Integer
都是不变类
享元模式就是通过工厂方法创建对象,在工厂方法内部,很可能返回缓存的实例,而不是新创建实例,从而实现不可变实例的复用。
在实际应用中,享元模式主要应用于缓存,即客户端如果重复请求某些对象,不必每次查询数据库或者读取文件,而是直接返回内存中缓存的数据。
7 代理
代理模式通过封装一个已有接口,并向调用方返回相同的接口类型,能让调用方在不改变任何代码的前提下增强某些功能(例如,鉴权、延迟加载、连接池复用等)。
使用Proxy模式要求调用方持有接口,作为Proxy的类也必须实现相同的接口类型。
和外观中介的区别
和适配器的区别:还是把A接口转成A接口,加入额外代码以实现权限检查
用Proxy实现这个权限检查,我们可以获得更清晰、更简洁的代码:
- A接口:只定义接口;
- ABusiness类:只实现A接口的业务逻辑;
- APermissionProxy类:只实现A接口的权限检查代理。
jbdc这块看不懂……
推荐阅读