Hycz's Blog

Life is a game. Why so serious?

Cassandra 0.8.0 源码分析——数据类型

一、Introduction

Cassandra中用到了一些可排序的数据类型,也就是放在db.marshal包中的那些类,他们实际上就是将一些基本类型,比如String,BigInteger,Long,ByteBuffer,UUID等,封装起来,最重要的是实现了Comparator接口,让这些封装后的数据类型可以用于排序集(例如SortedSet,TreeSet等),封装的过程中还加入了一些特有的方法。这些类的关系如下图(此外还有个异常类,不太重要):

二、设计特点

首先看看处于顶端的抽象类,AbstractType<T>,这个类虽然是抽象类,但是每次调用它的子类的构造器时总是会调用到它的构造器,这是其中的一个设计特点,先来看一个典型的AbstractType<T>的子类的域和构造器部分:

public class BytesType extends AbstractType<ByteBuffer>
{
    public static final BytesType instance = new BytesType();

    BytesType() {} // singleton
    ...
}

可以看到,这里使用了设计模式中的Singleton Pattern。仅有的那个静态常量是instance,当第一次调用到ByteType时,instance被初始化,实际上,instance成为了唯一的对外实例化ByteType的手段,而且成为了唯一的ByteType实例。这里用到的是传统的Singleton Pattern实现方法,一旦类被初始化,那么就会实例化,更好的是Bill Pugh的方案,不过已经不在本文讨论范围内。然而,可以看到,构造器中并无代码,于是,无疑是其父类的构造器被调用了,也就是AbstractType<T>中的构造器,那么,来看看AbstractType<T>的域和构造器部分:

public abstract class AbstractType<T> implements Comparator<ByteBuffer>
{
    public final Comparator<IndexInfo> indexComparator;
    public final Comparator<IndexInfo> indexReverseComparator;
    public final Comparator<IColumn> columnComparator;
    public final Comparator<IColumn> columnReverseComparator;
    public final Comparator<ByteBuffer> reverseComparator;

    protected AbstractType()
    {
        indexComparator = new Comparator<IndexInfo>()
        {
            public int compare(IndexInfo o1, IndexInfo o2)
            {
                return AbstractType.this.compare(o1.lastName, o2.lastName);
            }
        };
        indexReverseComparator = new Comparator<IndexInfo>()
        {
            public int compare(IndexInfo o1, IndexInfo o2)
            {
                return AbstractType.this.compare(o1.firstName, o2.firstName);
            }
        };
        columnComparator = new Comparator<IColumn>()
        {
            public int compare(IColumn c1, IColumn c2)
            {
                return AbstractType.this.compare(c1.name(), c2.name());
            }
        };
        columnReverseComparator = new Comparator<IColumn>()
        {
            public int compare(IColumn c1, IColumn c2)
            {
                return AbstractType.this.compare(c2.name(), c1.name());
            }
        };
        reverseComparator = new Comparator<ByteBuffer>()
        {
            public int compare(ByteBuffer o1, ByteBuffer o2)
            {
                if (o1.remaining() == 0)
                {
                    return o2.remaining() == 0 ? 0 : -1;
                }
                if (o2.remaining() == 0)
                {
                    return 1;
                }

                return -AbstractType.this.compare(o1, o2);
            }
        };
    }
    ...
}

可以看到,其实这里并没有做很多特别的操作,仅仅是初始化了5个Comparator,以供以后的子类调用,由于这5个对象仅仅实现了一个Comparator接口,所以它们存在的意义也就仅仅是提供一个compare方法,然后就可以作为参数填入。

除了上述的工作,剩下的就是实例化的意义。一旦实例化之后,就相当于得到了一个实现了Comparator接口的实例,于是在很多方法的调用中都可以将其填入。此外,Singleton的意义在于这个实例成为了某种特定的类型的一系列操作工具的入口,而不是什么保存某个特定类型数据的地方。

三、基本类型与封装后类型的关系

这里有一个问题,就是实现的接口Comparator<T>中,为什么填入的是ByteBuffer,这里有一篇关于Java中的Buffer的文章(http://zcdxzsz.iteye.com/blog/310917),ByteBuffer的存在相当于是所有其他基本Buffer的综合,然后在Buffer中以Byte的形式进行操作。然后,Comparator<ByteBuffer>迫使所有的最终子类实现一个方法,作为排序的依据:

    public int compare(ByteBuffer o1, ByteBuffer o2)

实现Comparator<ByteBuffer>的意义也就止于此。至于上面提到的5个Comparator,我的理解是他们没有独特的操作,连写一个子类的价值都没有,所以就只有5个变量而已 。

下一个让人关注的问题就是泛型AbstractType<T>是怎么工作的,自然,T的填入是根据需要封装的类型而决定的,具体来说,是下面几个:String,BigInteger,Long,ByteBuffer,UUID。填入的具体的T有什么作用呢?来看一看AbstractType<T>中是怎么定义的:

    public abstract T compose(ByteBuffer bytes);

    public abstract ByteBuffer decompose(T value);

    /** get a string representation of a particular type. */
    public abstract String toString(T t);

    /** get a string representation of the bytes suitable for log messages */
    public abstract String getString(ByteBuffer bytes);

    /** get a byte representation of the given string.
     *  defaults to unsupportedoperation so people deploying custom Types can update at their leisure. */
    public ByteBuffer fromString(String source) throws MarshalException
    {
        throw new UnsupportedOperationException();
    }

    /* validate that the byte array is a valid sequence for the type we are supposed to be comparing */
    public abstract void validate(ByteBuffer bytes) throws MarshalException;

    /** @deprecated; use reverseComparator field instead */
    public Comparator<ByteBuffer> getReverseComparator()
    {
        return reverseComparator;
    }

    /* convenience method */
    public String getString(Collection<ByteBuffer> names)
    {
        StringBuilder builder = new StringBuilder();
        for (ByteBuffer name : names)
        {
            builder.append(getString(name)).append(",");
        }
        return builder.toString();
    }

    /* convenience method */
    public String getColumnsString(Collection<IColumn> columns)
    {
        StringBuilder builder = new StringBuilder();
        for (IColumn column : columns)
        {
            builder.append(column.getString(this)).append(",");
        }
        return builder.toString();
    }

    public boolean isCommutative()
    {
        return false;
    }

    /** returns the class this AbstractType represents. */
    public abstract Class<T> getType();

    //
    // JDBC metadata
    //

    public abstract boolean isSigned();
    public abstract boolean isCaseSensitive();
    public abstract boolean isCurrency();
    public abstract int getPrecision(T obj);
    public abstract int getScale(T obj);
    public abstract int getJdbcType();
    public abstract boolean needsQuotes();

可见,关于T的重要的是compose()和decompose()方法,涉及到如何被封装的类型如何与ByteBuffer之间进行转换,其他涉及到T的方法,toString(),getType(),getPrecision(),getScale(),都是不甚重要的,甚至有些子类都不会使用getPrecision()和getScale(),估计是历史遗留问题。

于是,看完这些之后,就明白,marshal包中的类的作用就是

  1. 提供ByteBuffer中的compare()方法,使其能在排序集中使用;
  2. 提供被封装的类型与ByteBuffer之间的转换;
  3. 封装原始类型到具体的自定义类型,提供各类型的特有方法。
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: