Frage

I want to build a multi-layered buffer:

public class Buffer{
    protected void onReadComplete(int read){
        //...
        // throw new RuntimeException(...);
    }
    public void read(...){
        //...
        //...
        onReadCompleted(some);// we can throw RuntimeExceptions here without corrupt the field mAlreadyRead.
        mAlreadyRead+=some;
        return;
    }
    protected void checkRead(...) throws Exception{
        //...
    }
}

In the first layer(subclass), you do this:

Buffer lowlevel=new Buffer(){
    @Override
    protected void onReadComplete(int read){
        super.onReadComplete(read);
        //...
    }
    @Override
    Protected void checkRead(){
        //...
    }
};

Then whenever instance lowlevel is accessed, you will get notified through the overridden lowlevel.onReadComplete routine. But then I want to pass this Buffer to another part of my program. The thing here is “the other part of my program” can’t just new an instance and override methods in the Buffer, it only gets an instance of the Buffer. How can I override the methods in that instance to add more layers? What I want is:

Buffer higherLevel=lowlevel{
    @Override
    protected void onReadComplete(int read){
        super.onReadComplete(read);
        //...
    }
}

But of course java won’t get me that...

However, java does provide Proxy. But I want to have a clean, unified, low-overhead, performance-concerned way of doing this.

And we can’t add a call-back interface:

public class BufferWrapper{
    IEventBuffer event;
    public read(...){
        //...
        event.onReadComplete(...);
        //...
    }
    public BufferWrapper(Buffer buffer,IEventBuffer event){
        buffer.event=event;
    }
}

When accessed through higherLevel, we trigger onReadComplete on both, but when accessed through lowlevel, we trigger lowlevel.onReadComplete only.

And we can’t use this approach:

Buffer higherLevel=new Buffer(lowlevel){
    @Override
    protected void onReadComplete(int read){
        super.onReadComplete(read);
        //...
    }
}

Because the onReadComplete is happen in the middle of the read(...) and I don’t want to implement the read all-over again.

Forgive me about my poor English, I don’t know if I’ve made my point:

...I've made an illustration pic, but I can't post images... -->a means access through the instance a

<pre>
-->lo---------------------------------->lo.onXXX
---------->  hi1-->hi1.onXXX----------->lo.onXXX
---------->  hi2-->hi2.onXXX----------->lo.onXXX
-->  hi3-->  hi2-->  hi3.onXXX,hi2.onXXX->lo.onXXX
</pre>
War es hilfreich?

Lösung

After some tries and fails, I have no options but to "final" all the implementing methods. Leave only call-backs protected and subclassed.

Anyway, I'll put the source here. It's still an alpha version, I'm now working on some performance tunning.

public class Buffer {
    private class BufferImpl{
        public int mCapacity;
        public int mMask;
        public byte[] mBacking;
        public long mRead,mWrite;
        @Override
        public String toString() {
            int ca=mCapacity;
            char[] cs=new char[ca+ca+ca+2];
            for(int i=0;i<cs.length;i++)
                cs[i]=' ';
            byte[] bk=mBacking;
            cs[ca]='\n';
            cs[ca+ca+1]='\n';
            for(int i=0;i<ca;i++)
                cs[i]=bk[i]=='\n'||bk[i]=='\r'?'.':(char)bk[i];
            int r=(int)mRead&mMask;
            int w=(int)mWrite&mMask;
            if(mRead==mWrite)return new String(cs);
            if(r<w){
                for(int i=r;i<w;i++)
                    cs[i+ca+1]=bk[i]=='\n'||bk[i]=='\r'?'.':(char)bk[i];
                return new String(cs);
            }else{
                for(int i=r;i<ca;i++)
                    cs[i+ca+1]=bk[i]=='\n'||bk[i]=='\r'?'.':(char)bk[i];
                for(int i=0;i<w;i++)
                    cs[i+ca+ca+2]=bk[i]=='\n'||bk[i]=='\r'?'.':(char)bk[i];
                return new String(cs);
            }
        }
        public BufferImpl(int capacity){
            checkAndSetCapacity(capacity);
            mBacking=new byte[capacity];
        }
        public BufferImpl(byte[] backing){
            checkAndSetCapacity(backing.length);
            mBacking=backing;
        }
        private void checkAndSetCapacity(int capacity){
            if((mMask&capacity)!=0)
                throw new IllegalArgumentException("capacity "+capacity+" is not a power of 2");
            mMask=capacity-1;
            mCapacity=capacity;
        }
    }
    private BufferImpl impl;
    private Buffer lower;
    @Override
    public String toString() {
        return impl.toString();
    }
    public Buffer(int capacity){
        impl=new BufferImpl(capacity);
    }
    public Buffer(byte[] backing){
        impl=new BufferImpl(backing);
    }
    public Buffer(Buffer lower){
        this.lower=lower;
        this.impl=lower.impl;
    }

    public int availableRead(){
        if(lower!=null)
            return lower.availableRead();
        return (int)(impl.mWrite-impl.mRead);
    }
    public int availableWrite(){
        if(lower!=null)
            return lower.availableWrite();
        return capacity()-(int)(impl.mWrite-impl.mRead);
    }
    public int capacity(){
        if(lower!=null)
            return lower.capacity();
        return impl.mCapacity;
    }
    protected void onReset(){
        if(lower!=null)
            lower.onReset();
    }
    public final void reset(){
        onReset();
        impl.mRead=impl.mWrite=0;
    }
    protected void onReadComplete(int read){
        if(lower!=null)
            lower.onReadComplete(read);
    }
    public final void confirmRead(int read){
        onReadComplete(read);
        impl.mRead+=read;
    }
    public final int peek(byte[] out, int offset, int length){
        if(length==-1) length=out.length-offset;
        int effectiveLen=availableRead();
        if(length<effectiveLen) effectiveLen=length;
        if(effectiveLen>0){
            byte[] backing=impl.mBacking;
            int pos=(int)(impl.mRead & impl.mMask);
            int capacity=capacity();
            if(pos+effectiveLen>capacity){
                int left=effectiveLen-(capacity-pos);
                do{out[offset++]=backing[pos++];
                }while(pos<capacity);pos=0;
                do{out[offset++]=backing[pos++];
                }while(pos<left);
            }else{
                int lim=pos+effectiveLen;
                do{out[offset++]=backing[pos++];
                }while(pos<lim);
            }
        }return effectiveLen;
    }
    public final int peek(Buffer out, int length){
        if(length==-1) length=out.availableWrite();
        int effectiveLen=availableRead();
        if(length<effectiveLen) effectiveLen=length;
        if(effectiveLen>0){
            byte[] backing=impl.mBacking;
            int pos=(int)(impl.mRead & impl.mMask);
            int capacity=capacity();
            if(pos+effectiveLen>capacity){
                int p1=capacity-pos;
                int p2=effectiveLen-p1;
                out.write(backing, pos, p1);
                out.write(backing, 0, p2);
            }else out.write(backing, pos, effectiveLen);
        }return effectiveLen;
    }
    public final int read(byte[] out, int offset, int length){
        int len=peek(out, offset, length);
        confirmRead(len);
        return len;
    }
    public final int read(Buffer out, int length){
        int len=peek(out, length);
        confirmRead(len);
        return len;
    }
    protected void onWriteComplete(int written){
        if(lower!=null)
            lower.onWriteComplete(written);
    }
    public final int write(byte[] in, int offset, int length){
        if(length==-1) length=in.length-offset;
        int effectiveLen=availableWrite();
        if(length<effectiveLen) effectiveLen=length;
        if(effectiveLen>0){
            byte[] backing=impl.mBacking;
            int pos=(int)(impl.mWrite & impl.mMask);
            int capacity=capacity();
            if(pos+effectiveLen>capacity){
                int left=effectiveLen-(capacity-pos);
                do{backing[pos++]=in[offset++];
                }while(pos<capacity);pos=0;
                do{backing[pos++]=in[offset++];
                }while(pos<left);
            }else{
                int lim=pos+effectiveLen;
                do{backing[pos++]=in[offset++];
                }while(pos<lim);
            }onWriteComplete(effectiveLen);
            impl.mWrite+=effectiveLen;
            return effectiveLen;
        }onWriteComplete(effectiveLen);
        return effectiveLen;
    }
    public static void main(String[] args) {
        Buffer lower=new Buffer(16){
            @Override
            protected void onWriteComplete(int written) {
                System.out.println("lower.onWriteComplete  "+written);
                super.onWriteComplete(written);
            }
        };
        Buffer higher=new Buffer(lower){
            @Override
            protected void onWriteComplete(int written) {
                System.out.println("higher.onWriteComplete "+written);
                super.onWriteComplete(written);
            }
        };
        higher.write(new byte[0], 0, -1);
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top