前提
在寫這篇文章前一直在思考,我對建造者模式有了一個大體的理解。但是,有沒有可能會造成過度封裝呢,這里還需要各位看官老爺來評判,如果想看之前的對Toast工具了的封裝可以移步Android 自定義Toast,并且勘誤Android工具類里面的ToastUtils,有不足之處還望指出。
話不多說先上圖
1、先講一下什么是建造者模式
釋義
建造者模式 (BuilderPattern) 又稱為生成器模式,該模式主要用于將一個復雜對象的構建與它的表示分離,向用戶屏蔽復雜對象組成部分的創建細節,使得同樣的構建過程可以創建不同的表示。建造者模式通常包含如下4個角色。
UML圖:
角色介紹
1.Builder:抽象建造者角色,主要為創建產品對象的各組成部分指定抽象接口,一般包含兩類方法,其中 buildPartX() 用于創建復雜對象的各部分,此種方法的數量取決于復雜對象組成部分的多少;getResult() 用于返回復雜對象。
2.ConcreteBuilder:具體建造者角色,繼承自抽象建造者,實現復雜對象各部件的構造和裝配,并返回該對象。
3.Director:指揮者角色,客戶端通常只與該角色交互,通過construct()方法方法得到復雜對象。
4.Product:產品角色(復雜對象),通常定義為一個 POJO,針對其中的每個成員對象都有一組公有的 get() 和 set() 方法。
建造者模式的分類
根據產品創建過程中零件的構造是否具有一致的先后順序,可以將其分為“有設計者” 和 “無設計者”,兩種形式。
有設計者
在現實生活中,建造一個房子,但我們不知道怎么造,就要請負責總體設計的設計師和負責具體施工的工人,設計師只設計圖紙、命令工人干活,不參與施工。工人負責具體細節(窗戶、地板的構建)。最后,我們要從工人手中接過建造好的房子。
對建造者(工人)的規范:
package cn.house;
public interface Builder {
/**
* 建造窗戶
*/
public void mkWindow();
/**
* 建造房屋
*/
public void mkFloor();
/**
* 獲取房間
*/
public Room getRoom();
}
實現了 Builder 接口的工人:
package cn.house;
public class RoomBuilder implements Builder{
private Room room = new Room();
/** 具體創建窗戶 */
public void mkWindow() {
Window window = new Window();
room.setWindow(window);
}
/** 具體創建地板 */
public void mkFloor() {
Floor floor = new Floor();
room.setFloor(floor);
}
/** 交付以創建好的房子 */
public Room getRoom() {
return room;
}
}
設計師:
package cn.house;
public class Designer {
/**
* 命令 Builder
*
* @param builder
*/
public void command(Builder builder) {
// 建造房屋
builder.mkWindow();
// 建造地板
builder.mkFloor();
}
}
測試用例:
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);
}
無設計者
Android 中的 AlertDialog 就屬于無設計者的形式,下面是 AlertDialog 的簡單模擬:
public class AlertDialog {
private String title;
private String message;
private int buttonCount;
private AlertDialog() {
// empty
}
/** 獲取標題 */
public String getTitle() {
return title;
}
/** 獲取信息 */
public String getMessage() {
return message;
}
/** 獲取按鈕數 */
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("必須有上下文");
}
}
/** 設置標題 */
public Builder setTitle(String title) {
entity.title = title;
return this;
}
/** 設置內容 */
public Builder setMessage(String message) {
entity.message = message;
return this;
}
/** 設置按鈕數 */
public Builder setButtonCount(int buttonCount) {
entity.buttonCount = buttonCount;
return this;
}
/** 交付結果 */
public AlertDialog build() {
return entity;
}
}
}
可以看出,AlertDialog 直接命令 Builder ,并沒有涉及到 Designer,所以它是無序的。
建造者模式的應用場景
相同的方法,不同的執行順序,產生不同的執行效果
一個對象可以配置多個不同的零件,產生不同的效果
一個對象,參數方法極多,調用順序不同則效果不同
Android 開源項目中的應用
由于建造者模式本身的優點,極大簡化了對象的創建,一般被用于生成某些配置對象。可以看到下面的代碼是多么的簡潔清晰,一目了然。
2、講解一下我們今天關于Toast的進一步封裝
首先,看一下具體使用
最基本的用例:
new ToastUtil.Builder(this).setMessage("").build();
設置基本參數的用例:
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顯示的內容包括:文字內容、文字大小、文字顏色、圖片內容、圖片大小、還有背景顏色和顯示位置等,那么就要定義這些變量,請各位看官來看代碼(代碼中比較有詳細的解釋,各位看官應該都可以看懂)。
public class ToastUtil {
// 消息內容
private String message;
// 圖標
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;
// 顯示時長
private int duration = 0;
// X軸偏移量
private int floatX;
// Y軸偏移量
private int floatY;
// 圖標大小
private int mImageSize;
//構造函數設置為私有的,不能直接New
private ToastUtil() {
}
/**
* Builder
*
* @author Silence
*
*/
public static class Builder {
ToastUtil mToastUtil = new ToastUtil();
public Builder(Context context) {
mToastUtil.mContext = context;
}
/**
* 消息內容
*
* @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;
}
/**
* 顯示的圖標
*
* @param icon
* @return
*/
public Builder setIcon(int icon) {
mToastUtil.icon = icon;
return this;
}
/**
* 現實時長
*
* @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;
}
/**
* 圖標大小
*
* @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;
}
/**
* 創建
*
* @return
*/
public ToastUtil build() {
mToastUtil.setLayoutView();
return mToastUtil;
}
}
public void setLayoutView() {
if (!mShow) {
mToast = new Toast(mContext);
// 圖標
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);
// 消息內容
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();
}
}
}
最后,再直接創建使用(使用建造者模式是new xx.Builder()使用的,不能用類名.setxx()使用,之前就是用的類名.setxx(),差點被自己蠢死(捂臉))
感謝
感謝博主cfanr的Android 設計模式-建造者模式
感謝博主博弈史密斯的建造者模式(側重Java、Android)
最后啰嗦一句:設計模式在編程中很有用,應該認真思考可以寫出很優雅的代碼,我輩應該奮發圖強,像大神們看齊。