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();