淘先锋技术网

首页 1 2 3 4 5 6 7

装饰器模式被大量地使用在各种框架的源码里面,真正学会了对看源码和设计软件受益匪浅。

1、装饰者和被装饰者继承同一个接口

2、装饰者给被装饰者动态修改行为

 

首先我们一生活中的例子来看一看装饰器模式:

/**
 * @see io.netty.buffer.WrappedByteBuf;
 * @see io.netty.buffer.UnreleasableByteBuf
 * @see io.netty.buffer.SimpleLeakAwareByteBuf
 */
public class Decorate {

    // 优惠方案
    public interface OnSalePlan {
        float getPrice(float oldPrice);
    }

    // 无优惠
    public static class NonePlan implements OnSalePlan {
        static final OnSalePlan INSTANCE = new NonePlan();

        private NonePlan() {

        }

        public float getPrice(float oldPrice) {
            return oldPrice;
        }
    }

    // 立减优惠
    public static class KnockPlan implements OnSalePlan {
        // 立减金额
        private float amount;

        public KnockPlan(float amount) {
            this.amount = amount;
        }

        public float getPrice(float oldPrice) {
            return oldPrice - amount;
        }
    }


    // 打折优惠
    public static class DiscountPlan implements OnSalePlan {
        // 折扣
        public int discount;
        private OnSalePlan previousPlan;

        public DiscountPlan(int discount, OnSalePlan previousPlan) {
            this.discount = discount;
            this.previousPlan = previousPlan;
        }

        public DiscountPlan(int discount) {
            this(discount, NonePlan.INSTANCE);
        }

        public float getPrice(float oldPrice) {
            return previousPlan.getPrice(oldPrice) * discount / 10;
        }
    }

    public static void main(String[] args) {
        DiscountPlan simpleDiscountPlan = new DiscountPlan(5);
        System.out.println(simpleDiscountPlan.getPrice(100));

        KnockPlan previousPlan = new KnockPlan(50);
        DiscountPlan complexDiscountPlan = new DiscountPlan(5, previousPlan);
        System.out.println(complexDiscountPlan.getPrice(100));
    }

}

我们以OnSalePlan 作为接口,装饰者或者被装饰者都继承或者实现这个接口。

NonePlan是一种实现方案、KnockPlan也是一种实现方案、DiscountPlan也是一种实现方案,但是它可以实现我们的装饰器模式。

在main函数里面,我们首先实现了一个简单的打折方案,这里没有用到装饰器模式,打印出50;重点在第二个方案,首先实例出一个立减方案,立减方案KnockPlan作为被装饰者传入到DiscountPlan打折方案里面,DiscountPlan就是装饰者,它装饰了KnockPlan的行为,使得它的行为得以改变。

在netty里面,我们以WrappedByteBuf、UnreleasableByteBuf 、SimpleLeakAwareByteBuf这三个类讲解分析里面用到的设计模式。

WrappedByteBuf是所有装饰器的基类,它继承自ByteBuf

class WrappedByteBuf extends ByteBuf {

    protected final ByteBuf buf;

    protected WrappedByteBuf(ByteBuf buf) {
        if (buf == null) {
            throw new NullPointerException("buf");
        }
        this.buf = buf;
    }

    @Override
    public final boolean hasMemoryAddress() {
        return buf.hasMemoryAddress();
    }

    @Override
    public final long memoryAddress() {
        return buf.memoryAddress();
    }
    ...
}

首先看构造函数,传入一个ByteBuf实例,这个传入的实例就是被装饰者,它的行为可以被当前这个类,也就是WrappedByteBuf(也就是装饰者)动态改变。因为这个WrappedByteBuf它只是装饰器的基类,所以他只对传入的被装饰者的行为做简单的返回,没做任何修改,...后面是更多的方法,都是直接调用被装饰者的方法。

 

接下来看WrappedBuf的一个子类UnreleasableByteBuf,这个类一看就是什么什么不释放的类,具体什么我们先不管,我们找到源代码:

final class UnreleasableByteBuf extends WrappedByteBuf {

    private SwappedByteBuf swappedBuf;

    UnreleasableByteBuf(ByteBuf buf) {
        super(buf);
    }

    ...

    @Override
    public boolean release() {
        return false;
    }

    ...
}

首先调用父类WrappedByteBuf的构造方法,前面我们分析过,就是把被装饰者传进来,以供以后使用。然后看里面有一行 public boolean release() { return false;},我们不管它release释放了什么,反正它release就是返回false,装饰或者说是改变了被装饰者buf的行为。

另外一个WrappedByteBuf的子类SimpleLeakAwareByteBuf,这个类顾名思义就是内存泄漏自动感知的一个ByteBuf,源码:

final class SimpleLeakAwareByteBuf extends WrappedByteBuf {

    private final ResourceLeak leak;

    SimpleLeakAwareByteBuf(ByteBuf buf, ResourceLeak leak) {
        super(buf);
        this.leak = leak;
    }

    ...

    @Override
    public boolean release() {
        boolean deallocated =  super.release();
        if (deallocated) {
            leak.close();
        }
        return deallocated;
    }

    ...
}

构造器还是调用父类的方法,在release 这个方法里面,如果发现内存泄漏了,就执行leak.close()这个方法,然后在返回,其实也是修饰了被装饰者,动态改变了被装饰着的行为。

当然WrappedByteBuf还有很多子类的,我们目前只分析这两个来实现分析netty装饰器模式。