前言
六大設(shè)計原則中有一條叫“開閉原則”,意思就是修改對外關(guān)閉,擴(kuò)展對外開放胖烛,是啥意思呢?就是我寫的產(chǎn)品功能诅迷,我不希望在擴(kuò)展功能還要修改內(nèi)部邏輯佩番,而是可以通過擴(kuò)展外部來實現(xiàn)功能的擴(kuò)展,這么說可能并不是很好理解罢杉,舉個例子吧趟畏,你實現(xiàn)了一個圖片濾鏡功能,這個濾鏡可以把圖片處理成黑白滩租,你把圖片處理的算法寫在了功能內(nèi)部拱镐,然后有一天,你又寫了另一個濾鏡可以把圖片處理成素描樣式持际,于是你想把這個濾鏡算法加進(jìn)去沃琅,那怎么區(qū)分是哪個濾鏡呢,因為你算法都寫在功能類內(nèi)部蜘欲,所以你只能通過if else or switch case來處理不同的濾鏡益眉,開始你感覺很好,我可以切換兩種濾鏡姥份,慢慢的你發(fā)現(xiàn)郭脂,你寫了幾十個濾鏡,你要維護(hù)一個有幾十個if else的代碼澈歉,很顯然這不是一個好的方案展鸡,而這個時候,合理的使用設(shè)計模式去設(shè)計你的代碼結(jié)構(gòu)就顯得非常重要埃难,那應(yīng)該怎么辦呢莹弊,你可以使用策略模式或者工廠模式來提供不同的算法涤久,這篇文章就介紹一下工廠方法模式如何在不修改內(nèi)部的情況下對功能進(jìn)行擴(kuò)展,當(dāng)然這也是簡物中的項目實踐
簡物中的工廠方法模式
工廠模式分好幾類忍弛,基礎(chǔ)的有簡單工廠模式/靜態(tài)工廠模式响迂,接著就是工廠方法模式,再應(yīng)用更復(fù)雜的場景還有抽象工廠模式细疚,不同的模式對于不同的應(yīng)用場景是不一樣的蔗彤,那這里就暫時不對各種工廠模式進(jìn)行介紹,重點聊聊簡物中的工廠方法模式
簡物中的應(yīng)用場景
在前面我寫了一篇簡物中高仿Pinterest交互的實現(xiàn)思路的文章疯兼,在高仿Pinterest交互中然遏,展開的圖標(biāo)大小是有共同屬性的,圖標(biāo)都是具有icon吧彪、標(biāo)題啦鸣、like圖標(biāo)還有收藏和未收藏的值,后面甚至有考慮給圖標(biāo)加選中動畫来氧,而這一切我不希望把它捆綁在Pinterest交互內(nèi)部去做诫给,因為我要考慮以后更好的去擴(kuò)展,所以我決定用工廠方法模式啦扬,因為這里就單一一個產(chǎn)品功能中狂,不需要用到抽象工廠模式
那我們來看一下,菜單圖標(biāo)需要哪些屬性呢扑毡?
從圖上可以看到一些基本屬性選中前圖標(biāo)胃榕、選中后圖標(biāo)、圖標(biāo)文字瞄摊、還有圖標(biāo)的擺放順序勋又,當(dāng)然還有動畫,不過現(xiàn)在動畫都是統(tǒng)一换帜,內(nèi)部調(diào)用暫未使用楔壤,那我們按照這些屬性來給它設(shè)計一個接口模型
public interface IPinterestView {
int ITEM_SHOPPING_CART = 0x00010;
int ITEM_LIKE = 0x00020;
int LIKE = 1;
int CART = 2;
/**
* 菜單id
* @return
*/
int getItemId();
/**
* 菜單坐標(biāo)
* @return
*/
int getImageIndex();
/**
* 正常圖標(biāo)資源
* @return
*/
int getImageResNormal();
/**
* 按下后圖標(biāo)資源
* @return
*/
int getImageResPress();
/**
* 菜單標(biāo)題
* @return
*/
String getImageTitle();
/**
* 圖標(biāo)動畫
* @return
*/
Animation getAnimation();
}
這個接口模型帶有這些菜單的基本屬性,當(dāng)然也可以提供一些行為方法惯驼,供實現(xiàn)類去實現(xiàn)蹲嚣。我們現(xiàn)在有兩個菜單,一個Like一個Cart祟牲,我們讓這兩個具體菜單實現(xiàn)這個產(chǎn)品模型接口隙畜,然后返回具體菜單參數(shù),不過這里我先寫一個菜單父類實現(xiàn)這個接口模型说贝,把一些暫時不用待實現(xiàn)方法的或者需要封裝的方法寫上去议惰,以減少子類復(fù)寫的內(nèi)容
public class PinterestView implements IPinterestView {
/**
* 主要用于Like菜單返回圖標(biāo)
*/
boolean like;
@Override
public int getItemId() {
return 0;
}
@Override
public int getImageIndex() {
return 0;
}
@Override
public int getImageResNormal() {
return 0;
}
@Override
public int getImageResPress() {
return 0;
}
@Override
public String getImageTitle() {
return null;
}
public boolean isLike() {
return like;
}
@Override
public Animation getAnimation() {
return null;
}
public PinterestView withLike(boolean like) {
this.like = like;
return this;
}
}
那我們可以來寫Like菜單了
public class LikePinterestView extends PinterestView {
@Override
public int getItemId() {
return ITEM_LIKE;
}
@Override
public int getImageIndex() {
return IPinterestView.LIKE;
}
@Override
public int getImageResNormal() {
return isLike() ? R.mipmap.ic_unlike : R.mipmap.ic_like;
}
@Override
public int getImageResPress() {
return isLike() ? R.mipmap.ic_unlike_press : R.mipmap.ic_like_press;
}
@Override
public String getImageTitle() {
return isLike() ? "Unlike" : "Like";
}
}
Like菜單好了,那我們應(yīng)該要有一個能生產(chǎn)這類菜單模型的工具乡恕,而可能生產(chǎn)這類模型的工具中不一定能完全用同一個工具言询,比如我們已經(jīng)設(shè)計好了一個主板模型俯萎,已經(jīng)可以按照這類主板模型生產(chǎn)的不同的主板,可是造主板的材料不一定是木質(zhì)倍试,可能是別的材料讯屈,那我們怎么辦呢蛋哭,我們可以設(shè)計一個造主板的抽象工具县习,讓造不同的主板用不同的工具材料就好了
public interface PinterestViewFactory {
IPinterestView create();
}
那我們現(xiàn)在要生產(chǎn)Like菜單,可以寫一個具體的生產(chǎn)類,而Like菜單是需要傳參數(shù)的谆趾,所以我們可以在工廠里面設(shè)置這些參數(shù)
public class LikePinterestViewFactory implements PinterestViewFactory {
private boolean mIsLike;
@Override
public IPinterestView create() {
return new LikePinterestView().withLike(mIsLike);
}
public LikePinterestViewFactory withLike(boolean like){
this.mIsLike = like;
return this;
}
}
那我們Pinterest交互功能怎么用呢躁愿,我們只需要傳入一個IPinterestView接口模型,然后內(nèi)部功能調(diào)用接口方法就好了沪蓬,不用操心具體實現(xiàn)類彤钟,你要改這個方法的參數(shù),你在外面改跷叉,不要動我里面逸雹,你只要按照這個接口模型給我提供參數(shù)和具體方法執(zhí)行就行,我給你修改擴(kuò)展的權(quán)利云挟,這就是“開閉原則”
new PinterestSelector.Builder()
...
.addITouchView(new LikePinterestViewFactory().withLike(getAdapter().getItem(position).isLike()).create())
...
.create()
.onTouch(v, event);
內(nèi)部接收到了IPinterestView之后梆砸,接收參數(shù),或者在功能運行過程中調(diào)用接口方法
IPinterestView mITouchView;
public void initParams(){
setVisibility(View.GONE);
mBaseDegree = 0;
mItemId = mITouchView.getItemId()
mNormalResId = mITouchView.getImageResNormal();
mPressResId = mITouchView.getImageResPress();
mTitle = mITouchView.getImageTitle();
mIndex = mITouchView.getImageIndex();
setImageResource(getNormalResId());
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(getIconWidth(),getIconHeight());
/**
* 高
*/
mHeight = Math.abs((int)(Math.sin(getAngle(getAngle())) * getR()));
/**
* 寬
*/
mWidth = Math.abs((int)(Math.cos(getAngle(getAngle())) * getR()));
mIndexX = getTouchX() - mWidth - getIconWidth() / 2;
mIndexY = getTouchY() - mHeight - getIconHeight() / 2 - getStatusBarHeight();
params.setMargins(getIndexX(), getIndexY(), 0, 0);
setLayoutParams(params);
}
看到?jīng)]园欣,內(nèi)部功能的運行并沒有直接和菜單的具體參數(shù)和運行方法掛鉤帖世,外部只要按照這個接口模型生產(chǎn)對應(yīng)的菜單就好了,這就是多態(tài)的妙處
過了不久沸枯,我上線了一個購物車功能日矫,我需要彈出的菜單有購物車,那怎么辦绑榴?繼續(xù)在外部生產(chǎn)唄哪轿,內(nèi)部不做任何變動,Cart菜單來了
public class CartPinterestView extends PinterestView {
public CartPinterestView(){
super();
}
@Override
public int getItemId() {
return ITEM_SHOPPING_CART;
}
@Override
public int getImageIndex() {
return IPinterestView.CART;
}
@Override
public int getImageResNormal() {
return R.mipmap.ic_shopping_cart;
}
@Override
public int getImageResPress() {
return R.mipmap.ic_shopping_cart_press;
}
@Override
public String getImageTitle() {
return "Cart";
}
}
生產(chǎn)Cart
public class CartPinterestViewFactory implements PinterestViewFactory {
@Override
public IPinterestView create() {
return new CartPinterestView();
}
}
投入運轉(zhuǎn)
@Override
public boolean onTouch(View v, MotionEvent event) {
if(!v.isClickable()){
return false;
}
new PinterestSelector.Builder()
.show((View)v.getParent())
.scroll(getBindId())
.backgroundColor("#f0ffffff")
.dialogMode()
/**
* 我在這里
*/
.addITouchView(new CartPinterestViewFactory().create())
.addITouchView(new LikePinterestViewFactory().withLike(getAdapter().getItem(position).isLike()).create())
.setOnLongClickListener(new PinterestSelector.OnLongClickListener() {
@Override
public void onLongClick(View v) {
handleLongClick();
}
})
.setOnCancelListener(new PinterestSelector.OnCancelListener() {
@Override
public void onCancel() {
}
@Override
public void onSyncCancel() {
handleSyncCancel();
}
})
.setOnItemSelectListener(new PinterestSelector.OnItemSelectListener() {
@Override
public void onItemSelect(int index) {
switch (index){
case IPinterestView.LIKE:
handleCollectSelect();
break;
case IPinterestView.CART:
handleSlideToCart();
break;
}
}
})
.create()
.onTouch(v, event);
return super.onTouch(v, event);
}
好了翔怎,殺青