→23种设计模式大纲
设计模式之结构型上篇
设计模式之结构型——装饰器模式、代理模式、外观模式
1)装饰者模式
装饰者模式:Decorator Pattern
UML类图
装饰者模式中的角色
- 抽象接口:所有实现类的公共接口,表示基本的功能抽象
- 具体实现:被装饰的类,实现了抽象接口,有基本的方法逻辑
- 装饰类:装饰类是装饰者模式中最重要的部分,和具体实现一样,共同 继承抽象接口,持有一个公共接口的引用。
- 具体装饰类:继承或实现装饰类,书写具体的代码逻辑,因为持有公共接口,所以可以 调用该引用的实现,并加以增强。
代码案例
抽象接口与实现类
//抽象接口
public interface Component {
Integer operate();
}
//具体实现类,可以有多个,此处案例只写了一个
class ConcreteComponent implements Component {
@Override
public Integer operate() {
System.out.println("base content:" + 100);
return 100;
}
}
装饰类
//装饰类:持有抽象接口的引用
abstract class Decorator implements Component {
protected Component component;
public Decorator(Component component) {
this.component = component;
}
}
具体装饰类
//装饰类A
class ConcreteDecoratorA extends Decorator {
public ConcreteDecoratorA(Component component) {
super(component);
}
@Override
public Integer operate() {
System.out.println("ConcreteDecoratorA add " + 10);
//调用被装饰类的方法,并加以增强
return component.operate() + 10;
}
}
//装饰类B
class ConcreteDecoratorB extends Decorator {
public ConcreteDecoratorB(Component component) {
super(component);
}
@Override
public Integer operate() {
System.out.println("ConcreteDecoratorB add " + 1);
//调用被装饰类的方法,并加以增强
return component.operate() + 1;
}
}
class Test {
public static void main(String[] args) {
Component component = new ConcreteDecoratorB(new ConcreteDecoratorA(new ConcreteComponent()));
System.out.println();
System.out.println(component.operate());
}
}
----结果----
ConcreteDecoratorB add 1
ConcreteDecoratorA add 10
base content:100
111
总结
在案例中,被装饰类返回值为10, 通过装饰类的多层装饰,可以不断的增添 +1 或者 +10的操作。
装饰者模式有很多种应用场景,最常见的比如 **Java IO 流: **
- 有提供最基本功能的文件流,可以被缓冲流装饰,二者都直接或间接继承实现了基础的输入输出流。
装饰者模式的核心在于装饰者和被装饰着具有相同的接口,并且装饰者类持有公共接口的引用,可以不断的增强(装饰)
2)代理模式
代理模式分为静态代理和动态代理
主要用户类的增强。
静态代理
//被代理类
public interface People {
void say();
}
class Boy implements People {
@Override
public void say() {
System.out.println("boy say hello");
}
}
//代理类
//代理类
class BoyProxy implements People {
Boy boy;
public BoyProxy() {
boy = new Boy();
}
@Override
public void say() {
System.out.println(" boy say before");
boy.say();
System.out.println(" boy say after");
}
}
class Test{
public static void main(String[] args) {
People boyProxy=new BoyProxy();
boyProxy.say();
}
}
针对代理类的UML图就不展示了,实际情况也比较简单。静态代理的实现主要有以下几点:
- 被代理类和代理类实现相同的接口;
- 代理类在无参构造里面实例化被代理类;
- 代理类可在被代理类 调用前后加额外的逻辑;
调用某个类的方法前后增加额外的逻辑,实现类的增强。
静态代理需要编写代理类,即BoyProxy,需要编译器编译为class文件的,相对不那么灵活。
其他:静态代理和装饰者模式
从结构上来看二者十分相似。理念也很相似。细微之处还是有一些区别的。
- 而这都需要两个类实现同一个接口,并且持有该接口的引用。但是静态代理是内部实例化,装饰者模式需要外部传入。
- 装饰者模式存在着链式的调用,即可能由装饰类的子类甚至孙子类去完成某项功能。而静态代理则是直接增强原类,着重点在于直接增强。
动态代理
动态代理主要是两种实现:Java基础实现和CGLIB实现
Java基础实现
核心内容:基于接口(interface)实现代理。
public interface Paper {
String color();
String desc();
}
class Book implements Paper {
@Override
public String color() {
System.out.println("-------------------------黄皮书");
return "黄皮书";
}
@Override
public String desc() {
System.out.println("-------------------------教科书");
return "教科书";
}
}
class BookProxy implements InvocationHandler {
Object origin;
BookProxy(Object origin) {
this.origin = origin;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before");
Object invoke = method.invoke(origin, args);
System.out.println("after");
return invoke;
}
}
class Test {
public static void main(String[] args) {
Book book=new Book();
Paper bookProxy = (Paper) Proxy.newProxyInstance(Book.class.getClassLoader(), book.getClass().getInterfaces(), new BookProxy(book));
bookProxy.color();
System.out.println();
bookProxy.desc();
}
}
before
-------------------------黄皮书
after
before
-------------------------教科书
after
这种方式基于jdk的反射技术实现,通过Proxy来动态生成代理类,通过实现InvocationHandler来定义切面前后的操作。
局限性较大(被代理类必须实现某一接口,代理接口定义的方法)
CGLIB实现
核心内容:基于父子类继承实现代理。
public class Paper {
public void m1(){
System.out.println("m1");
}
public void m2(){
System.out.println("m2");
}
}
class Test{
public static void main(String[] args) {
Paper paper=new Paper();
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(paper.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before");
Object invoke = methodProxy.invokeSuper(o, objects);
System.out.println("after");
return invoke;
}
});
Paper o = (Paper) enhancer.create();
o.m1();
o.m2();
}
}
基于CGLIB的动态代理实现基于父子继承,不需要被代理类继承某个接口,但是需要被代理类为非final类(final类的方法不能被重写)
CGLIB又是基于动态创建子类的方法,所以两种实现方式各有限制。
3)外观模式(门面模式)
外观模式也被叫做门面模式:Facade Pattern
UML类图
各个子模块
//模块A
class ModuleA{
public void operate(){
System.out.println("调用模块A的方法");
}
}
//模块B
class ModuleB{
public void operate(){
System.out.println("调用模块B的方法");
}
}
//模块C
class ModuleC{
public void operate(){
System.out.println("调用模块C的方法");
}
}
门面
public class Facade {
private ModuleA moduleA;
private ModuleB moduleB;
private ModuleC moduleC;
public Facade(){
moduleA=new ModuleA();
moduleB=new ModuleB();
moduleC=new ModuleC();
}
public void operateA(){
moduleA.operate();
moduleB.operate();
}
public void operateB(){
moduleB.operate();
moduleC.operate();
}
}
总结
门面模式是一种很简单的设计模式。
- 通过一个门面(即一个统筹类),定义一系列方法来调用各个子模块来“完成某个组合动作”。
- 避免客户端直接调用各个模块,造成功能不明确,维护困难得问题。
- 用户不关心子模块如何调用,只需要通过门面来实现功能,维护则统一交给门面类。
注意:不必将所有的子模块调用都放在门面里。
即用户可以通过门面调用子模块也可以直接调用子模块。门面类最好是一种像是工具类的存在。