Android建造者模式初探(Toast工具類的進(jìn)一步封裝)

前提

在寫這篇文章前一直在思考,我對(duì)建造者模式有了一個(gè)大體的理解。但是,有沒有可能會(huì)造成過度封裝呢,這里還需要各位看官老爺來評(píng)判,如果想看之前的對(duì)Toast工具了的封裝可以移步Android 自定義Toast,并且勘誤Android工具類里面的ToastUtils,有不足之處還望指出。

話不多說先上圖

Toast基本顯示.png
帶圖標(biāo)和改變字體圖標(biāo)大小的toast.png
改變背景顏色的toast.png
改變字體顏色和位置的toast.png

1、先講一下什么是建造者模式

釋義

建造者模式 (BuilderPattern) 又稱為生成器模式,該模式主要用于將一個(gè)復(fù)雜對(duì)象的構(gòu)建與它的表示分離,向用戶屏蔽復(fù)雜對(duì)象組成部分的創(chuàng)建細(xì)節(jié),使得同樣的構(gòu)建過程可以創(chuàng)建不同的表示。建造者模式通常包含如下4個(gè)角色。

UML圖:


建造者模式圖

角色介紹

1.Builder:抽象建造者角色,主要為創(chuàng)建產(chǎn)品對(duì)象的各組成部分指定抽象接口,一般包含兩類方法,其中 buildPartX() 用于創(chuàng)建復(fù)雜對(duì)象的各部分,此種方法的數(shù)量取決于復(fù)雜對(duì)象組成部分的多少;getResult() 用于返回復(fù)雜對(duì)象。

2.ConcreteBuilder:具體建造者角色,繼承自抽象建造者,實(shí)現(xiàn)復(fù)雜對(duì)象各部件的構(gòu)造和裝配,并返回該對(duì)象。

3.Director:指揮者角色,客戶端通常只與該角色交互,通過construct()方法方法得到復(fù)雜對(duì)象。

4.Product:產(chǎn)品角色(復(fù)雜對(duì)象),通常定義為一個(gè) POJO,針對(duì)其中的每個(gè)成員對(duì)象都有一組公有的 get() 和 set() 方法。

建造者模式的分類

根據(jù)產(chǎn)品創(chuàng)建過程中零件的構(gòu)造是否具有一致的先后順序,可以將其分為“有設(shè)計(jì)者” 和 “無設(shè)計(jì)者”,兩種形式。

有設(shè)計(jì)者

在現(xiàn)實(shí)生活中,建造一個(gè)房子,但我們不知道怎么造,就要請(qǐng)負(fù)責(zé)總體設(shè)計(jì)的設(shè)計(jì)師和負(fù)責(zé)具體施工的工人,設(shè)計(jì)師只設(shè)計(jì)圖紙、命令工人干活,不參與施工。工人負(fù)責(zé)具體細(xì)節(jié)(窗戶、地板的構(gòu)建)。最后,我們要從工人手中接過建造好的房子。

對(duì)建造者(工人)的規(guī)范:

package cn.house;

public interface Builder {

  /**
   * 建造窗戶
   */
  public void mkWindow();

  /**
   * 建造房屋
   */
  public void mkFloor();

  /**
   * 獲取房間
   */
  public Room getRoom();
}

實(shí)現(xiàn)了 Builder 接口的工人:

package cn.house;

 public class RoomBuilder implements Builder{
 private Room room = new Room();

  /** 具體創(chuàng)建窗戶 */
   public void mkWindow() {

  Window window = new Window();
 room.setWindow(window);
   }

   /** 具體創(chuàng)建地板 */
   public void mkFloor() {
     Floor floor = new Floor();
    room.setFloor(floor);
}

 /** 交付以創(chuàng)建好的房子 */
 public Room getRoom() {
  return room;
}
 }

設(shè)計(jì)師:

package cn.house;

public class Designer {

  /**
   * 命令 Builder
   * 
   * @param builder
   */
 public void command(Builder builder) {
   // 建造房屋
   builder.mkWindow();

   // 建造地板
   builder.mkFloor();
 }
}

測(cè)試用例:

 public static void main(String[] args) {

Builder builder = new RoomBuilder();
Designer design = new Designer();
design.command(builder);

Room room = builder.getRoom();
Window window = room.getWindow();
Floor floor = room.getFloor();

System.out.println(window);
System.out.println(floor);
  }

無設(shè)計(jì)者

Android 中的 AlertDialog 就屬于無設(shè)計(jì)者的形式,下面是 AlertDialog 的簡(jiǎn)單模擬:

public class AlertDialog {
  private String title;
  private String message;
  private int buttonCount;

 private AlertDialog() {
// empty
 }

/** 獲取標(biāo)題 */
public String getTitle() {
return title;
}

/** 獲取信息 */
public String getMessage() {
return message;
}

/** 獲取按鈕數(shù) */
public int getButtonCount() {
return buttonCount;
}

/** 顯示 */
public void show() {
System.out.println("show");
}

/** 建造者 */
public static class Builder {
private AlertDialog entity = new AlertDialog();

public Builder(boolean isContext) {
  if (!isContext) {
    throw new RuntimeException("必須有上下文");
  }
}

/** 設(shè)置標(biāo)題 */
public Builder setTitle(String title) {
  entity.title = title;
  return this;
}

/** 設(shè)置內(nèi)容 */
public Builder setMessage(String message) {
  entity.message = message;
  return this;
}

/** 設(shè)置按鈕數(shù) */
public Builder setButtonCount(int buttonCount) {
  entity.buttonCount = buttonCount;
  return this;
}

/** 交付結(jié)果 */
public AlertDialog build() {
  return entity;
}
}
}

可以看出,AlertDialog 直接命令 Builder ,并沒有涉及到 Designer,所以它是無序的。

建造者模式的應(yīng)用場(chǎng)景

相同的方法,不同的執(zhí)行順序,產(chǎn)生不同的執(zhí)行效果

一個(gè)對(duì)象可以配置多個(gè)不同的零件,產(chǎn)生不同的效果

一個(gè)對(duì)象,參數(shù)方法極多,調(diào)用順序不同則效果不同

Android 開源項(xiàng)目中的應(yīng)用

由于建造者模式本身的優(yōu)點(diǎn),極大簡(jiǎn)化了對(duì)象的創(chuàng)建,一般被用于生成某些配置對(duì)象。可以看到下面的代碼是多么的簡(jiǎn)潔清晰,一目了然。

2、講解一下我們今天關(guān)于Toast的進(jìn)一步封裝

首先,看一下具體使用
最基本的用例:

new ToastUtil.Builder(this).setMessage("").build();

設(shè)置基本參數(shù)的用例:

new ToastUtil.Builder(this).setMessage("123456")
                .setTextColor("#F2F2FF").setBackgroudColor(R.color.yellow)
                .setTextSise(48).setIcon(R.drawable.ic_launcher)
                .setImageSize(128).setGrivaty(Gravity.CENTER).build();

其次,讓我們考慮一下,上面圖中Toast顯示的內(nèi)容包括:文字內(nèi)容、文字大小、文字顏色、圖片內(nèi)容、圖片大小、還有背景顏色和顯示位置等,那么就要定義這些變量,請(qǐng)各位看官來看代碼(代碼中比較有詳細(xì)的解釋,各位看官應(yīng)該都可以看懂)。

public class ToastUtil {
// 消息內(nèi)容
private String message;
// 圖標(biāo)
private int icon;
// 字體大小
private int textSize = 0;
// 字體顏色
private String textColor;
// 背景顏色
private int bgColor = 0;
// 上下文
private Context mContext;
// 是否顯示
private boolean mShow = false;
// Toast
private Toast mToast;
// 布局
private LinearLayout mLayout;
// 位置
private int gravity = 0;
// ImageView
private ImageView mImgView;
// TextView
private TextView mTxtContent;
// 顯示時(shí)長(zhǎng)
private int duration = 0;
// X軸偏移量
private int floatX;
// Y軸偏移量
private int floatY;
// 圖標(biāo)大小
private int mImageSize;

//構(gòu)造函數(shù)設(shè)置為私有的,不能直接New
private ToastUtil() {
}

/**
 * Builder
 * 
 * @author Silence
 * 
 */
public static class Builder {
    ToastUtil mToastUtil = new ToastUtil();

    public Builder(Context context) {
        mToastUtil.mContext = context;

    }

    /**
     * 消息內(nèi)容
     * 
     * @param message
     * @return
     */
    public Builder setMessage(String message) {
        mToastUtil.message = message;
        return this;
    }

    /**
     * Toast顯示位置
     * 
     * @param gravity
     * @return
     */
    public Builder setGrivaty(int gravity) {
        mToastUtil.gravity = gravity;
        return this;
    }

    /**
     * 顯示的圖標(biāo)
     * 
     * @param icon
     * @return
     */
    public Builder setIcon(int icon) {
        mToastUtil.icon = icon;
        return this;
    }

    /**
     * 現(xiàn)實(shí)時(shí)長(zhǎng)
     * 
     * @param duration
     * @return
     */
    public Builder setDuration(int duration) {
        mToastUtil.duration = duration;
        return this;
    }

    /**
     * 顯示的字體顏色
     * 
     * @param textColor
     * @return
     */
    public Builder setTextColor(String textColor) {
        mToastUtil.textColor = textColor;
        return this;
    }

    /**
     * 顯示的字體大小
     * 
     * @param textSize
     * @return
     */
    public Builder setTextSise(int textSize) {
        mToastUtil.textSize = textSize;
        return this;
    }

    /**
     * X軸偏移量
     * 
     * @param floatX
     * @return
     */
    public Builder setFloatX(int floatX) {
        mToastUtil.floatX = floatX;
        return this;
    }

    /**
     * Y軸偏移量
     * 
     * @param floatY
     * @return
     */
    public Builder setFloatY(int floatY) {
        mToastUtil.floatY = floatY;
        return this;
    }

    /**
     * 圖標(biāo)大小
     * 
     * @param imageSize
     * @return
     */
    public Builder setImageSize(int imageSize) {
        mToastUtil.mImageSize = imageSize;
        return this;
    }

    /**
     * 顯示的背景顏色
     * 
     * @param bgColor
     * @return
     */
    public Builder setBackgroudColor(int bgColor) {
        mToastUtil.bgColor = bgColor;
        return this;
    }

    /**
     * 創(chuàng)建
     * 
     * @return
     */
    public ToastUtil build() {
        mToastUtil.setLayoutView();
        return mToastUtil;
    }

}

public void setLayoutView() {
    if (!mShow) {
        mToast = new Toast(mContext);
        // 圖標(biāo)
        mImgView = new ImageView(mContext);
        LinearLayout.LayoutParams lParams = new LinearLayout.LayoutParams(
                mImageSize, mImageSize);
        mImgView.setImageResource(icon);
        lParams.gravity = Gravity.CENTER_HORIZONTAL
                | Gravity.CENTER_VERTICAL;
        lParams.setMargins(5, 5, 5, 5);
        mImgView.setLayoutParams(lParams);

        // 消息內(nèi)容
        mTxtContent = new TextView(mContext);
        LinearLayout.LayoutParams lParams1 = new LinearLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        if (!TextUtils.isEmpty(textColor)) {
            mTxtContent.setTextColor(Color.parseColor(textColor));
        }
        if (textSize != 0) {
            mTxtContent.setTextSize(textSize);
        }
        mTxtContent.setLayoutParams(lParams1);
        // 布局
        mLayout = new LinearLayout(mContext);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
        mLayout.setOrientation(LinearLayout.HORIZONTAL);
        mLayout.setLayoutParams(params);
        mLayout.addView(mImgView);
        mLayout.addView(mTxtContent);
        if (bgColor != 0) {

            mLayout.setBackgroundResource(bgColor);
        }
        if (gravity != 0) {
            mToast.setGravity(gravity, floatX, floatY);
        }
        mToast.setView(mLayout);
        if (duration != 0) {
            mToast.setDuration(duration);
        }
        if (!TextUtils.isEmpty(message)) {
            mTxtContent.setText(message);
        }
        mToast.show();
    }
}
}

最后,再直接創(chuàng)建使用(使用建造者模式是new xx.Builder()使用的,不能用類名.setxx()使用,之前就是用的類名.setxx(),差點(diǎn)被自己蠢死(捂臉))

感謝

感謝博主cfanrAndroid 設(shè)計(jì)模式-建造者模式
感謝博主博弈史密斯建造者模式(側(cè)重Java、Android)
最后啰嗦一句:設(shè)計(jì)模式在編程中很有用,應(yīng)該認(rèn)真思考可以寫出很優(yōu)雅的代碼,我輩應(yīng)該奮發(fā)圖強(qiáng),像大神們看齊。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,741評(píng)論 25 709
  • 沒有人買車會(huì)只買一個(gè)輪胎或者方向盤,大家買的都是一輛包含輪胎、方向盤和發(fā)動(dòng)機(jī)等多個(gè)部件的完整汽車。如何將這些部件組...
    justCode_閱讀 1,987評(píng)論 1 6
  • PS:轉(zhuǎn)載請(qǐng)注明出處作者: TigerChain地址: http://www.itdecent.cn/p/300c...
    TigerChain閱讀 1,870評(píng)論 1 18
  • 面向?qū)ο蟮牧笤瓌t 單一職責(zé)原則 所謂職責(zé)是指類變化的原因。如果一個(gè)類有多于一個(gè)的動(dòng)機(jī)被改變,那么這個(gè)類就具有多于...
    JxMY閱讀 1,017評(píng)論 1 3
  • 1、 老王很丑。但很有錢。 其實(shí)老王不老,就是長(zhǎng)得有點(diǎn)著急。于是班上的同學(xué)都喊他老王。 真的,開學(xué)第一天看到老王,...
    念清芷閱讀 3,705評(píng)論 80 115

友情鏈接更多精彩內(nèi)容