从手工打造到工厂设计模式的演变历程

原创 2017年09月14日 10:23:42

自定义View系列教程00–推翻自己和过往,重学自定义View
自定义View系列教程01–常用工具介绍
自定义View系列教程02–onMeasure源码详尽分析
自定义View系列教程03–onLayout源码详尽分析
自定义View系列教程04–Draw源码分析及其实践
自定义View系列教程05–示例分析
自定义View系列教程06–详解View的Touch事件处理
自定义View系列教程07–详解ViewGroup分发Touch事件
自定义View系列教程08–滑动冲突的产生及其处理


探索Android软键盘的疑难杂症
深入探讨Android异步精髓Handler
详解Android主流框架不可或缺的基石
站在源码的肩膀上全解Scroller工作机制


Android多分辨率适配框架(1)— 核心基础
Android多分辨率适配框架(2)— 原理剖析
Android多分辨率适配框架(3)— 使用指南


版权声明


开篇语

前不久,在写工厂设计模式时,我还是期望用一个例子来阐述它的原理和应用。可是,当我写完之后才发现:单凭一个示例很难梳理出工厂模式。换句话说,就是之前的套路不好使了。嗯哼,既然原来的方式行不通,那就另辟蹊径:我们从手工打造开始讲起,一步步演变过度到现在的工厂设计模式。

我想能看到这篇博客的人,都会有一部属于自己的手机;它出现于你的裤兜,办公桌,写字台,饭桌,枕头边;当然更多的时候它就在你手掌。既然,大家对这个玩意这么熟悉,我们就用生产手机为例子来学习和了解工厂设计模式。


手工打造

我们先利用时光机回退到大概二十年前:你需要一部手机给你在远方的女朋友打电话。但是,你没有手机啊,市面上也没有卖的啊,可是相思之情难以抑制,每当傍晚都会涌上心头。于是,你开始自己造手机。

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class HWMobile{

    public HWMobile() {
        System.out.println("自己动手生产一部华为手机,好累啊");
    }

    public void call(String number) {
        System.out.println("利用华为手机拨打电话:"+number);
    }

    public void sendMessage(String message) {
        System.out.println("利用华为手机发送短信:"+message);
    }


}

嗯哼,我们自己制造了一部华为手机,虽然颜值不高,但是可以打电话,发短信了!

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {

    public static void main(String[] args) {
        HWMobile hwMobile=new HWMobile();
        hwMobile.call("95279527");
        hwMobile.sendMessage("I miss you");
    }

}

二话不说,拿起手机给妹子打电话,泡妹的事业又可以继续了;但是累啊!自己动手一点一滴地生产组装一个手机,累得直吐血啊!


这里写图片描述

怎么办呢?有没有其他工厂来帮我们生产手机呢?有啊,必须有啊,比如全球最大的代工厂穷士康!


简单工厂模式(Simple Factory)

好了,既然有工厂,那我们就请工厂代劳。

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public interface Mobile {
    public void call(String number);
    public void sendMessage(String message);
}


//标配版手机
class HWStandardMobile implements Mobile {

    public HWStandardMobile() {

        System.out.println("工厂生产了一部标配版华为手机");
    }

    @Override
    public void call(String number) {
        System.out.println("利用华为手机拨打电话:"+number);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("利用华为手机发送短信:"+message);
    }

}


//高配版手机
class HWProfessionalMobile implements Mobile {

    public HWProfessionalMobile() {
        System.out.println("工厂生产了一部高配版华为手机");
    }

    @Override
    public void call(String number) {
        System.out.println("利用华为手机拨打电话:"+number);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("利用华为手机发送短信:"+message);
    }

}

穷士康说:既然要造手机,我就给你造两个,一个高配版,一个标准版,想用哪个自己挑就是了。好嘞,我们来看看工厂是怎么制造手机的:

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class MobileFactory {
    public final static String STANDARE="standard";
    public final static String PROFESSIONAL="professional";
    public static Mobile createMobile(String type) {
        Mobile mobile = null;
        switch (type) {
            case STANDARE:
                mobile=new HWStandardMobile();
                break;
            case PROFESSIONAL:
                mobile=new HWProfessionalMobile();
                break;
            default:
                break;
        }
        return mobile;
    }
}

只要我们告诉工厂生产什么手机,它就会自动帮我们制造。而且,工厂会根据CPU的不同自动区分是建造标准版的手机还是高配版的手机。既然这么方便,那就赶紧试一把:

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {

    public static void main(String[] args) {
        MobileFactory.createMobile("standard");
        MobileFactory.createMobile("professional");
    }

}

在此,让工厂生产两部手机,一部高配,一部标配!这样是不是省事好多?爽了吧?是的,你是爽了,舒服了;可是,工厂不愿意了!为啥呢?假设哪天你不愿意用标配和高配的手机了,你想用更高级的钻石级华为手机,那么这个工厂就得进行大的改动:

    switch (type) {
            case STANDARE:
                mobile=new HWStandardMobile();
                break;
            case PROFESSIONAL:
                mobile=new HWProfessionalMobile();
                break;
            default:
                break;
    }

更加确切地说:这个switch语句得新加case了!为了生成新系列的手机就得对工厂进行伤筋动骨的改造,人家穷士康当然不愿意了!再从软件工程的角度来看,这也违背了开闭原则。所以,简单工厂模式并不是真正的设计模式,23种设计模式里并没有它的一席之地。


工厂方法模式(Factory Method)

虽然全球最大的代工厂穷士康不愿意为了新产品大刀阔斧地改造原来的工厂但是也不愿意订单花落他家;生意还是要做,钱还是要赚的。那怎么办?干脆建立一个工厂模型,每当有新产品的制造需求时按照这个模型新建一个工厂就行啦!

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public interface Mobile {
    public void call(String number);
    public void sendMessage(String message);
}


//标配版手机
class HWStandardMobile implements Mobile {

    public HWStandardMobile() {
        System.out.println("工厂生产了一部标配版华为手机");
    }

    @Override
    public void call(String number) {
        System.out.println("利用华为手机拨打电话:"+number);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("利用华为手机发送短信:"+message);
    }

}


//高配版手机
class HWProfessionalMobile implements Mobile {

    public HWProfessionalMobile() {
        System.out.println("工厂生产了一部高配版华为手机");
    }

    @Override
    public void call(String number) {
        System.out.println("利用华为手机拨打电话:"+number);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("利用华为手机发送短信:"+message);
    }

}

手机还是原来的两部,没有变化;但是工厂和之前不一样了,我们来瞅瞅:

/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;


//抽象的手机工厂
public interface MobileFactory {
    public abstract Mobile createMobile();
}


//生成华为标配手机的工厂
class HWStandardMobileFactory implements MobileFactory {

    @Override
    public Mobile createMobile() {
        HWStandardMobile mobile=new HWStandardMobile();
        return mobile;
    }

}


//生成华为高配手机的工厂
class HWProfessionalMobileFactory implements MobileFactory {

    @Override
    public Mobile createMobile() {
        HWProfessionalMobile mobile=new HWProfessionalMobile();
        return mobile;
    }

}

先创立了一个抽象工厂,然后建立一个生产工厂标配版的华为手机,再专门建立一个工厂生产高配版本的手机。如果有新的华为手机(例如比高配版还要牛气的钻石系列)的生产需求,那么再建立一个对应的工厂就行啦!

工厂方法模式小结:

1、具体产品均实现了自抽象产品接口。
比如,高配版和标准版的华为手机都implements Mobile

2、具体工厂均实现了抽象工厂接口。
比如,生产标配手机的工厂和生产高配手机的工厂都implements MobileFactory

3、当有新产品需求时,只需要新建工厂进行生产,而不必去修改原来的已经存在的工厂代码。
所以,该方式是符合开闭原则的。

好了,我们来利用工厂方法模式生产手机吧:

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {

    public static void main(String[] args) {
        MobileFactory mobileFactory=null;

        mobileFactory=new HWStandardMobileFactory();
        mobileFactory.createMobile();

        mobileFactory=new HWProfessionalMobileFactory();
        mobileFactory.createMobile();

    }

}

嗯哼,我们只需要用与产品对应的工厂生产手机就行啦。讲到这里,咋们的工厂方法模式,是不是就可以结束了呢?貌似是可以完结了,但是那些好学的学霸就有疑问了:这种写法是不是太啰嗦了呢?我们真的需要建立这么多具体的工厂类么?

听到学霸的疑问,学渣也开始思考了:与简单工厂模式比起来,工厂方法模式虽然遵守了开闭原则,但是建立了很多具体的工厂类。这样的代码有些臃肿,不便于维护。

听到童鞋们这么说,穷士康也开始鼓噪了:就是嘛,建这么多工厂多费劲啊,花了我们这么多的本钱!

好了,既然这样的实现大家都觉得不太好;那么该怎么优化呢?请注意我们之前的小结: 具体产品均实现了自抽象产品接口;具体工厂均实现了抽象工厂接口。看来,我们可以在在工厂这方面做做文章,争取一个工厂就可以生产所有的手机。那么,该怎么优化呢?请继续往下看。

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public interface Mobile {
    public void call(String number);
    public void sendMessage(String message);
}


//标配版手机
class HWStandardMobile implements Mobile {

    public HWStandardMobile() {
        System.out.println("工厂生产了一部标配版华为手机");
    }

    @Override
    public void call(String number) {
        System.out.println("利用华为手机拨打电话:"+number);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("利用华为手机发送短信:"+message);
    }

}


//高配版手机
class HWProfessionalMobile implements Mobile {

    public HWProfessionalMobile() {
        System.out.println("工厂生产了一部高配版华为手机");
    }

    @Override
    public void call(String number) {
        System.out.println("利用华为手机拨打电话:"+number);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("利用华为手机发送短信:"+message);
    }

}

对于手机,我们不做改变;它与之前一样没有任何变化。

/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

//抽象的手机工厂
public interface MobileAbstractFactory {
    public <T extends Mobile> T createMobile(Class<T> clazz);
}

抽象的手机工厂和以往不大一样,最大的差异就是此处使用了泛型。

/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

//具体的手机工厂
public class MobileConcreteFactory implements MobileAbstractFactory {

    @SuppressWarnings("unchecked")
    @Override
    public <T extends Mobile> T createMobile(Class<T> clazz) {
        Mobile mobile=null;
        String className=clazz.getName();
        try {
             mobile=(Mobile) Class.forName(className).newInstance();
        }catch(Exception e) {
            e.printStackTrace();
        }
        return (T) mobile;
    }

}

这是具体的手机工厂的核心代码。在该具体工厂中采用反射的方式生产不同的手机;从而避免建立众多的工厂。好了,来测试一下:

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {

    public static void main(String[] args) {
        MobileAbstractFactory mobileFactory=new MobileConcreteFactory();
        mobileFactory.createMobile(HWStandardMobile.class);
        mobileFactory.createMobile(HWProfessionalMobile.class);
    }

}

哇哈,我们只用创建一个工厂就可以生产不同的手机!想生产说明手机直接告诉厂商手机类型就行啦!穷士康也高兴坏了,真爽,节约了一大笔资金!


抽象工厂模式(Abstract Factory)

华为手机上市一段时间之后,用户普遍反应:日常生活中的不小心导致手机经常摔坏!穷士康听到这个消息后,就开始琢磨了:给手机配备手机保护套!也就是说:为高配版的手机生产与之对应的真皮保护套;至于标配版的手机就整个塑料材质的保护套。毕竟是生意人啊,想得真细致,太精打细算了!

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public interface Mobile {
    public void call(String number);
    public void sendMessage(String message);
}


//标配版手机
class HWStandardMobile implements Mobile {

    public HWStandardMobile() {
        System.out.println("工厂生产了一部标配版华为手机");
    }

    @Override
    public void call(String number) {
        System.out.println("利用华为手机拨打电话:"+number);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("利用华为手机发送短信:"+message);
    }

}


//高配版手机
class HWProfessionalMobile implements Mobile {

    public HWProfessionalMobile() {
        System.out.println("工厂生产了一部高配版华为手机");
    }

    @Override
    public void call(String number) {
        System.out.println("利用华为手机拨打电话:"+number);
    }

    @Override
    public void sendMessage(String message) {
        System.out.println("利用华为手机发送短信:"+message);
    }

}

这是手机,与之前一模一样。

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public interface PhoneCase {
    public abstract void protectMobile();
}


//用于保护标准版手机的保护套
class HWStandardPhoneCase implements PhoneCase{

    public HWStandardPhoneCase() {
        System.out.println("工厂生产StandardPhoneCase用于保护标准版的华为手机");
    }

    @Override
    public void protectMobile() {
        System.out.println("HWStandardPhoneCase protectMobile()");

    }

}


//用于保护高配版手机的保护套
class HWProfessionalPhoneCase implements PhoneCase{

    public HWProfessionalPhoneCase() {
        System.out.println("工厂生产ProfessionalPhoneCase用于保护高配版的华为手机");
    }

    @Override
    public void protectMobile() {
        System.out.println("HWProfessionalPhoneCase protectMobile()");
    }

}

这是为手机配备的保护套用于不同配置的华为手机。

/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;

//抽象的手机工厂
public interface MobileAbstractFactory {
    public abstract Mobile createMobile();
    public abstract PhoneCase createPhoneCase();
}

抽象工厂除了制造手机,还要制造手机保护套。

/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;


//具体的手机工厂
public class StandardMobileConcreteFactory implements MobileAbstractFactory {

    @Override
    public Mobile createMobile() {
        Mobile mobile=new HWStandardMobile();
        return mobile;
    }

    @Override
    public PhoneCase createPhoneCase() {
        PhoneCase phoneCase=new HWStandardPhoneCase();
        return phoneCase;
    }



}

该具体的工厂负责生产标配的手机及其保护套。

/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
package cn.com;


//具体的手机工厂
public class ProfessionalMobileConcreteFactory implements MobileAbstractFactory {

    @Override
    public Mobile createMobile() {
        Mobile mobile=new HWProfessionalMobile();
        return mobile;
    }

    @Override
    public PhoneCase createPhoneCase() {
        PhoneCase phoneCase=new HWProfessionalPhoneCase();
        return phoneCase;
    }


}

该具体的工厂负责生产高配的手机及其保护套。

package cn.com;
/**
 * 原创作者:谷哥的小弟
 * 博客地址:http://blog.csdn.net/lfdfhl
 */
public class Test {

    public static void main(String[] args) {
        MobileAbstractFactory mobileFactory=null;
        mobileFactory=new StandardMobileConcreteFactory();
        mobileFactory.createMobile();
        mobileFactory.createPhoneCase();

        mobileFactory=new ProfessionalMobileConcreteFactory();
        mobileFactory.createMobile();
        mobileFactory.createPhoneCase();
    }

}

嗯哼,我们来测试一下。要生产不同的手机及其与之匹配的保护套只需要建立不同的工厂就行啦;至于工厂怎么生产的细节问题就不用管了,而且工厂生产出来的手机和保护套必然是相互匹配的,不会出现错误的搭配!

到了这里,我想不少人都有疑问了:这不和工厂方法模式基本完全一样么?无非是工厂里多了一个方法而已!嗯哼,从表面来看是这样的。那么,工厂方法模式和抽象工厂模式有什么区别么?关于这一点,不少书里都提到了产品,产品树,产品族,产品等级的概念。这些类似于八股文的东西看起来还是挺头疼的,理不清,斩不断!其实,不用过分纠结概念,我们只要理解其中的道理就行了。

嗯哼,在刚才这个抽象工厂模式的示例中我有意无意地在反复强调一点:手机要与手机保护套匹配,不能乱对应。比如:高配手机搭配一个标配的手机保护套是错误的。那么,我们怎么来杜类似的错误呢?我们让客户来选择么?这个不太现实,谁也不能保证客户不会犯错!既然这样,那么我们把手机和与之对应的保护套放到同一个工厂里;并建立起几个不同的工厂。于是,我们就可以让用户选”套餐”了,它选了什么套餐,我们就启用与之对应的工厂进行生成就行。比如,华为告诉穷士康:我要造100W部高配手机,并且每个手机配一个高档的保护套。穷士康接到这个订单之后把任务交给ProfessionalMobileConcreteFactory即可。从我们的代码也可以看出来:ProfessionalMobileConcreteFactory生成出来的比如是高配手机和高配的手机保护套,绝对不会乱套!好了,说了这么多大白话,我们再来看抽象工厂模式的定义:

Provide an interface for creating families of related or dependent objects without specifying their concrete classes

这句英文的主要含义是:抽象工厂模式为创建一组相关或相互依赖的对象提供一个接口!请问:什么叫做“相关或相互依赖的对象”?不就是我们这里的手机和与之对应的手机保护套么?!嗯哼,这么说应该就清楚多了!明白这点之后,我们趁热打铁,思考一下:如果抽象工厂模式里工厂里的方法只有一个的话,那么不就又变成了工厂方法模式了么? 所以说:抽象工厂模式和工厂方法模式的区别就在于需要创建的对象的复杂程度上。而且,在简单工厂模式,工厂方法模式,抽象工厂模式中这三者中抽象工厂模式是最为抽象、最具一般性的。


小感悟

很多刚开始做开发的童鞋喜欢拿着一本厚厚的设计模式在角落里默默地啃。学习的劲头很足,态度也很端正,配得上10086个赞。在此,我也想提醒一下小伙伴们:学习态度和努力程度固然非常重要,但是我们也要注意学习方法。抛开实际应用和业务逻辑单纯地看设计模式是很难理解其精髓的。我们不妨将设计模式和自己的实际工作结合起来学习,比如做Android开发的小伙伴可结合Android源码或者非常流行的第三方库来深入地研究设计模式,在此推荐一篇《Retrofit分析-漂亮的解耦套路》供大家学习参考


参考资料

版权声明:本文原创作者:谷哥的小弟 http://blog.csdn.net/lfdfhl

相关文章推荐

设计模式

1、FACTORY—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅...

Spring Cloud在国内中小型公司能用起来吗?

今天吃完饭休息的时候瞎逛知乎,突然看到这个一个问题Spring Cloud在国内中小型公司能用起来吗?,吸引了我的注意。仔细的看了题主的问题,发现这是一个好问题,题主经过了一番思考,并且用图形全面的将...

最短路径算法(下)——弗洛伊德(Floyd)算法

概述在这篇博客中我主要讲解最短路径算法中的Floyd算法,这是针对多源最短路径的一个经典算法。对于单源最短路径算法请详见我的另一篇博客:最短路径算法(上)——迪杰斯特拉(Dijikstra)算法弗洛伊...

Spring JDBC-混合框架的事务管理

概述 问题 解决方案 示例Hibernate Spring JDBC概述Spring 抽象的 DAO 体系兼容多种数据访问技术,它们各有特色,各有千秋。 Hibernate 是非常优秀的 ORM 实...

Spring Aop(五)——给Advice传递参数

5 给Advice传递参数Advice除了可以接收JoinPoint(非Around Advice)或ProceedingJoinPoint(Around Advice)参数外,还可以直接接收与切入点...

史上最简单的Hibernate入门简介

其实Hibernate本身是个独立的框架,它不需要任何web server或application server的支持。然而,大多数的Hibernate入门介绍都加入了很多非Hibernate的东西,...

Android 屏幕适配方案

转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/45460089; 本文出自:【张鸿洋的博客】 1、概述大家在Andr...

推翻自己和过往,重学自定义View

关于自定义View以前看了很多资料看,从博客园到CSDN,从stackoverflow到EOE论坛,从百草园到三味书屋,搜了一大筐,沮丧的发现这些文章大同小异:只举个简单的例子,很少研究为什么;人云亦...
  • lfdfhl
  • lfdfhl
  • 2016-06-14 13:49
  • 26757
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)