Jianwoo中的设计模式(4) — 工厂方法模式

前言

六大设计原则中有一条叫“开闭原则”,意思就是修改对外关闭,扩展对外开放,是啥意思呢?就是我写的产品功能,我不希望在扩展功能还要修改内部逻辑,而是可以通过扩展外部来实现功能的扩展,这么说可能并不是很好理解,举个例子吧,你实现了一个图片滤镜功能,这个滤镜可以把图片处理成黑白,你把图片处理的算法写在了功能内部,然后有一天,你又写了另一个滤镜可以把图片处理成素描样式,于是你想把这个滤镜算法加进去,那怎么区分是哪个滤镜呢,因为你算法都写在功能类内部,所以你只能通过if else or switch case来处理不同的滤镜,开始你感觉很好,我可以切换两种滤镜,慢慢的你发现,你写了几十个滤镜,你要维护一个有几十个if else的代码,很显然这不是一个好的方案,而这个时候,合理的使用设计模式去设计你的代码结构就显得非常重要,那应该怎么办呢,你可以使用策略模式或者工厂模式来提供不同的算法,这篇文章就介绍一下工厂方法模式如何在不修改内部的情况下对功能进行扩展,当然这也是简物中的项目实践

简物中的工厂方法模式

工厂模式分好几类,基础的有简单工厂模式/静态工厂模式,接着就是工厂方法模式,再应用更复杂的场景还有抽象工厂模式,不同的模式对于不同的应用场景是不一样的,那这里就暂时不对各种工厂模式进行介绍,重点聊聊简物中的工厂方法模式

简物中的应用场景

在前面我写了一篇简物中高仿Pinterest交互的实现思路的文章,在高仿Pinterest交互中,展开的图标大小是有共同属性的,图标都是具有icon、标题、like图标还有收藏和未收藏的值,后面甚至有考虑给图标加选中动画,而这一切我不希望把它捆绑在Pinterest交互内部去做,因为我要考虑以后更好的去扩展,所以我决定用工厂方法模式,因为这里就单一一个产品功能,不需要用到抽象工厂模式
那我们来看一下,菜单图标需要哪些属性呢?

高仿Pinterest交互

从图上可以看到一些基本属性选中前图标、选中后图标、图标文字、还有图标的摆放顺序,当然还有动画,不过现在动画都是统一,内部调用暂未使用,那我们按照这些属性来给它设计一个接口模型

public interface IPinterestView {

    int ITEM_SHOPPING_CART = 0x00010;
    int ITEM_LIKE = 0x00020;

    int LIKE = 1;
    int CART = 2;

    /**
     * 菜单id
     * @return
     */
    int getItemId();

    /**
     * 菜单坐标
     * @return
     */
    int getImageIndex();

    /**
     * 正常图标资源
     * @return
     */
    int getImageResNormal();

    /**
     * 按下后图标资源
     * @return
     */
    int getImageResPress();

    /**
     * 菜单标题
     * @return
     */
    String getImageTitle();

    /**
     * 图标动画
     * @return
     */
    Animation getAnimation();

}

这个接口模型带有这些菜单的基本属性,当然也可以提供一些行为方法,供实现类去实现。我们现在有两个菜单,一个Like一个Cart,我们让这两个具体菜单实现这个产品模型接口,然后返回具体菜单参数,不过这里我先写一个菜单父类实现这个接口模型,把一些暂时不用待实现方法的或者需要封装的方法写上去,以减少子类复写的内容

public class PinterestView implements IPinterestView {

    /**
     * 主要用于Like菜单返回图标
     */
    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菜单好了,那我们应该要有一个能生产这类菜单模型的工具,而可能生产这类模型的工具中不一定能完全用同一个工具,比如我们已经设计好了一个主板模型,已经可以按照这类主板模型生产的不同的主板,可是造主板的材料不一定是木质,可能是别的材料,那我们怎么办呢,我们可以设计一个造主板的抽象工具,让造不同的主板用不同的工具材料就好了

public interface PinterestViewFactory {

    IPinterestView create();

}

那我们现在要生产Like菜单,可以写一个具体的生产类,而Like菜单是需要传参数的,所以我们可以在工厂里面设置这些参数

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接口模型,然后内部功能调用接口方法就好了,不用操心具体实现类,你要改这个方法的参数,你在外面改,不要动我里面,你只要按照这个接口模型给我提供参数和具体方法执行就行,我给你修改扩展的权利,这就是“开闭原则”

        new PinterestSelector.Builder()
                ...
                .addITouchView(new LikePinterestViewFactory().withLike(getAdapter().getItem(position).isLike()).create())
                ...
                .create()
                .onTouch(v, event);

内部接收到了IPinterestView之后,接收参数,或者在功能运行过程中调用接口方法

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

看到没,内部功能的运行并没有直接和菜单的具体参数和运行方法挂钩,外部只要按照这个接口模型生产对应的菜单就好了,这就是多态的妙处

过了不久,我上线了一个购物车功能,我需要弹出的菜单有购物车,那怎么办?继续在外部生产呗,内部不做任何变动,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";
    }

}

生产Cart

public class CartPinterestViewFactory implements PinterestViewFactory {

    @Override
    public IPinterestView create() {
        return new CartPinterestView();
    }

}

投入运转

    @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);
    }

好了,杀青

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容