vxh.viet
6/6/2017 - 5:28 AM

Abstract Builder Pattern or getThis() trick.

Abstract Builder Pattern or getThis() trick.

Source: StackOverflow, AngelikaLanger, Java Generics and Collections

Question: We have 2 stype of stickers

{
  type: image 
  position: 0.5, 0.5
  width: 0.1
  height: 1
  angle: 0
  name: “image_name”
}

{
  type: text
  positionX: 0.5
  positionY: 0.5
  width: 0.1
  height: 1
  angle: 0
  text: “hahaha”
  font: “sanfrancisco”
  color: red
  background_image: {
        image_name: “some_name”
        slice_insets: 5, 5, 10, 10
        text_insets: 5, 10, 10, 20
    }
}

How can we effienctly model this?

Answer:

We do this using a BaseStickerData model that hold all the common attributes. There are two models ImageStickerData and TextStickerData that hold extra attributes. To efficiently create the object we will use an AbstractBuilder like this:

BaseStickerData

public class BaseStickerData {
    @StringDef({
            Type.IMAGE,
            Type.TEXT
    })@Retention(RetentionPolicy.SOURCE)
    protected @interface Type{
        String IMAGE = "Image";
        String TEXT = "Text";
    }

    private @BaseStickerData.Type String mType;
    private float mPosX;
    private float mPosY;
    private float mWidth;
    private float mHeight;
    private float mAngle;

    /**
     * Dont use this to init the object use {@link AbstractBuilder} instead.
     * @param b
     */
    protected BaseStickerData(AbstractBuilder b) {
        mType = b.mType;
        mPosX = b.mPosX;
        mPosY = b.mPosY;
        mWidth = b.mWidth;
        mHeight = b.mHeight;
        mAngle = b.mAngle;
    }

    public String getType() {
        return mType;
    }

    public float getPosX() {
        return mPosX;
    }

    public float getPosY() {
        return mPosY;
    }

    public float getWidth() {
        return mWidth;
    }

    public float getHeight() {
        return mHeight;
    }

    public float getAngle() {
        return mAngle;
    }


    public static abstract class AbstractBuilder<T extends AbstractBuilder<T>> {
        private @BaseStickerData.Type String mType;
        private float mPosX;
        private float mPosY;
        private float mWidth;
        private float mHeight;
        private float mAngle;

        public T setType(@Type String type){
            mType = type;
            return getThis();
        }

        public T setPosX(float posX){
            mPosX = posX;
            return getThis();
        }

        public T setPosY(float posY){
            mPosY = posY;
            return getThis();
        }

        public T setWidth(float width){
            mWidth = width;
            return getThis();
        }

        public T setHeight(float height){
            mHeight = height;
            return getThis();
        }

        public T setAngle(float angle){
            mAngle = angle;
            return getThis();
        }

        protected abstract T getThis();
    }
}

ImageStickerData

public class ImageStickerData extends BaseStickerData {
    @DrawableRes
    private int mDrawableRes;

    private ImageStickerData(Builder b) {
        super(b);
        mDrawableRes = b.mDrawableRes;
    }

    public int getDrawableRes() {
        return mDrawableRes;
    }

    //in case we need to subsclass this child, see page 139 of Java Generics and Collections
    public static final class Builder extends AbstractBuilder<Builder> {
        @DrawableRes
        private int mDrawableRes;

        public Builder setDrawableRes(@DrawableRes int drawableRes) {
            mDrawableRes = drawableRes;
            return this;
        }

        @Override
        protected Builder getThis() {
            return this;
        }

        public ImageStickerData build() {
            return new ImageStickerData(this);
        }
    }
}

TextStickerData

public class TextStickerData extends BaseStickerData {

    private String mText;
    private String mFont;
    private int mColor;
    private @DrawableRes int mDrawableRes;

    private TextStickerData(Builder b) {
        super(b);
        mText = b.mText;
        mFont = b.mFont;
        mColor = b.mColor;
        mDrawableRes = b.mDrawableRes;
    }
    
    public String getText() {
        return mText;
    }

    public String getFont() {
        return mFont;
    }

    public int getColor() {
        return mColor;
    }

    public int getDrawableRes() {
        return mDrawableRes;
    }

    //in case we need to subsclass this child, see page 139 of Java Generics and Collections
    public static final class Builder extends AbstractBuilder<Builder> {
        private String mText;
        private String mFont;
        private int mColor;
        private @DrawableRes int mDrawableRes;

        public Builder setText(String text){
            mText = text;
            return this;
        }

        public Builder setFont(String font){
            mFont = font;
            return this;
        }

        public Builder setColor(int color){
            mColor = color;
            return this;
        }

        public Builder serDrawableRes(@DrawableRes int drawableRes){
            mDrawableRes = drawableRes;
            return this;
        }

        public TextStickerData build(){
            return new TextStickerData(this);
        }

        @Override
        protected Builder getThis() {
            return this;
        }
    }
}

Use like this:

private ImageStickerData t = new ImageStickerData.Builder()
            .setType(BaseStickerData.Type.IMAGE)
            .setPosX(0.5f)
            .setPosY(0.5f)
            .setWidth(0.1f)
            .setHeight(1f)
            .setAngle(90f)
            .setDrawableRes(R.drawable.sticker_set_01_top_left)
            .build();