Android开发之深入理解工厂(Factory)模式

2017-08-04 15:27 评论 0 条

摘要:

你是否明白什么是工厂模式,什么时候使用工厂模式,使用工厂模式包含哪几种方式?这篇文章将带你深入理解工厂(Factory)模式,文章以线程池创建线程为例子,线程池以缓存线程池,即Executors.newCachedThreadPool()为代表,一个APP项目中可能需要多个线程,多则几十个,如果使用new逐一创建,会显得有点繁琐、麻烦,这个时候可以考虑工厂模式。

一、工厂模式应用于线程池实例

Android多线程开发中,线程池在TeachCourse的印象中包含四个方面:1.执行器,2.工作任务,3.线程池,4.线程,它们之间的关系是:执行器从线程池已有的线程中取出一条线程执行已提交的工作任务,它们的UML类图如下:

线程池

该实例涉及到的类包括:ExecutorExecutorsRunnableThreadFactoryThread等,Executor是一个接口,还包含其实现类及其子类,Executors是对Executor及其子类的封装,Runnable表示需要执行的工作任务,ThreadFactory表示创建线程的工厂,Thread表示线程,具体的用法如下:

public class DefaultConfigurationFactory {
    /**创建执行器对象*/
    public static Executor createTaskDistributor() {
        return Executors.newCachedThreadPool(createThreadFactory(Thread.NORM_PRIORITY, "uil-pool-d-"));
    }
    /**创建线程工厂对象*/
    private static ThreadFactory createThreadFactory(int threadPriority, String threadNamePrefix) {
        return new DefaultConfigurationFactory.DefaultThreadFactory(threadPriority, threadNamePrefix);
    }
    /**重写线程工厂*/
    private static class DefaultThreadFactory implements ThreadFactory {

        private static final AtomicInteger poolNumber = new AtomicInteger(1);

        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
        private final int threadPriority;

        DefaultThreadFactory(int threadPriority, String threadNamePrefix) {
            this.threadPriority = threadPriority;
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
            namePrefix = threadNamePrefix + poolNumber.getAndIncrement() + "-thread-";
        }

        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
            if (t.isDaemon()) t.setDaemon(false);
            t.setPriority(threadPriority);
            return t;
        }
    }
}

在上面的代码中,有两个地方使用到工厂模式,第一个地方是调用newThread()返回Thread对象,第二个地方是调用createThreadFactory()返回ThreadFactory对象,接下来我们可以使用创建的执行器Executor执行提交的工作任务Runnable,具体的用法如下:

       DefaultConfigurationFactory.createTaskDistributor().execute(new LoadTask(textView,handler));

在一个APP项目中会出现多个工作任务,使用线程池后不要担心创建线程的问题,只需要将工作任务按照上述的方法提交给Executor执行器即可,比如:TeachCourse创建一个加载和显示图片的工作任务LoadAndDisplayImageTask,在需要的地方,执行如下代码:

       DefaultConfigurationFactory.createTaskDistributor().execute(new LoadAndDisplayImageTask(imageView,handler));

到这里,我们学习了工厂模式在线程池中的使用,通过线程池减少线程对象创建、销毁的开销,工厂模式的一个特点:将创建相同对象的过程封装成方法,调用同一个方法完成相同对象的创建

二、什么是工厂模式?

通过工厂模式应用于线程池的例子的学习,首先让读者对工厂模式有一个大概的认识,对上面的例子不是很理解,不影响你对后面工厂模式的学习,毕竟工厂模式应用于线程池的例子涉及的知识点包括多线程、消息队列,这些知识在本篇文章中不做过多介绍,需要读者具备一定的基础,而本篇文章重点学习工厂模式。

工厂模式的特点:将创建相同对象的过程封装成方法,调用同一个方法完成相同对象的创建

现实中的工厂,负责生产厂品,工厂模式的工厂负责创建对象,创建一个对象会为对象分配一块独立的内存,创建对象的过程就是不断分配内存的过程。生活的农村TeachCourse对工厂印象深刻,以前村里没有工厂生产棉衣,家里人会买一些毛衣的回来,家人更多时候手工织毛衣,织一件毛衣可能需要有两个月甚至更长,大部分是满足家庭所需;但过了有段时间后,一些领导干部决定带动村里经济发展,着手发展一些第三产业,开始招商引资,吸引了商家创建在村周边创办了一些工厂,让工厂负责生产日常所需的毛衣,同时将多余的产品销售全国各地,原本有两个月完成一件的毛衣,现在可以一天生产几十、几百甚至上千。从这个例子体现出工厂的高效率。

手动生产毛衣的过程,如下图:

工厂模式

在面向对象的编程语言中,将织毛衣转化成Sweater类,代码如下:

public class Sweater {
    private String size;
    private String style;
    private String material;

    public Sweater(String size, String style, String material) {
        this.size = size;
        this.style = style;
        this.material = material;
    }

    public void makeSweater() {
        System.out.println("当前生产的毛衣:");
        System.out.println("尺寸:" + size);
        System.out.println("样式:" + style);
        System.out.println("材料:" + material);
    }
}

工厂生产毛衣的过程,准备好毛衣的尺寸、材料、样式等基本信息,制作成一份模板,然后开始批量生产,所以工厂生产出来某批量的毛衣在尺寸、材料和样式上没有很大的差异。

工厂生产毛衣的过程,如下图:

工厂模式

将毛衣工厂转化成SweaterFactory类,类里面包含对应的属性、方法,代码如下:

public class SweaterFactory {

    public static Sweater createSweater(String size,String style,String material){
           return new Sweater(size,style,material);
    }
}

总结:工厂模式是将创建相同对象的过程封装成方法,调用同一个方法完成相同对象的创建。

三、 什么时候使用工厂模式?

如果你想要生产相同尺寸、样式和材料没有差异的一批毛衣,就应该考虑通过工厂进行生产;如果你只想织一两件日常所需的毛衣,暂时就不用考虑工厂模式。在上面线程池的例子中,因为考虑线程池可能不止一两条线程,所以考虑通过线程工厂负责创建线程,可以满足线程池需要多少创建多少的要求。

总结:在考虑创建一批没有很大差异性的对象时,可以考虑使用工厂模式

四、工厂模式的几种方式

我们生产的毛衣款式按年龄段划分可以有:童装、成人装、老年装;不同年龄段有分为男装、女装,按照上面所述代码,每次创建对象都需要输入:毛衣的尺寸、样式和材质,能不能将每一种款式封装成一份模板,每次只需要定义调用createSweater()方法,而不用关心传入的参数呢?

开始对上面的代码,进行重构,首先声明一个产品的接口Product,代码如下:

public interface Product {
    void makeSweater();
}

根据毛衣款式的不同,分别实行Product接口,童装具体类,代码如下:

public class ChildrenSweater implements Product {
    @Override
    public void makeSweater() {
        System.out.println("当前生产的毛衣:");
        System.out.println("尺寸:" + "童装大小");
        System.out.println("样式:" + "松绿色");
        System.out.println("材料:" + "纯棉体恤");
    }
}

成人装具体类,代码如下:

public class AdultSweater implements Product {
    @Override
    public void makeSweater() {
        System.out.println("当前生产的毛衣:");
        System.out.println("尺寸:" + "成人大小");
        System.out.println("样式:" + "夏装男士圆领净色纯色短T男装");
        System.out.println("材料:" + "纯棉体恤");
    }
}

老年装具体类,代码如下:

public class OldSweater implements Product {
    @Override
    public void makeSweater() {
        System.out.println("当前生产的毛衣:");
        System.out.println("尺寸:" + "老人大小");
        System.out.println("样式:" + "中老年男装夹克2017春季新款");
        System.out.println("材料:" + "锦纶");
    }
}

第一种方式:

工厂不能像上面一样只负责生产一种款式的毛衣,现在要根据生产的需求,工厂要能批量生产童装、成人装和老年装的产品,开始对SweaterFactory进行重构,代码如下:

public class SweaterFactory {

    public static <T extends Product> T createProduct(Class<T> clz){
        Product product=null;
        try {
            product=(Product) Class.forName(clz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return (T) product;

    }
}

接下来工厂就可以批量生产不同款式的毛衣,代码如下:

        /**批量生产童装*/
        Product product=SweaterFactory.createProduct(ChildrenSweater.class);
        product.makeSweater();
        /**批量生产成人装*/
        product=SweaterFactory.createProduct(AdultSweater.class);
        product.makeSweater();
        /**批量生产老年装*/
        product=SweaterFactory.createProduct(OldSweater.class);
        product.makeSweater();

这是一种动态批量生产对象的方式,需要哪一个类的对象传入那一个类,然后调用相同的makeSweater()方法,特点:使用反射知识,理解比较困难,但代码简洁

第二种方式:

不使用反射的知识,通过类型识别生产哪一款毛衣,创建对应毛衣对象,特点:简单明了,依赖性强,代码如下:

public class SweaterFactory {

    public static Product createProduct(String type) {
        Product product
        switch (type) {
            case "children":

                product = new ChildrenSweater();

                break;
            case "adult":

                product = new AdultSweater();

                break;
            case "old":

                product = new OldSweater();

                break;
            default:

                product = new ChildrenSweater();

                break;
        }
        return product;
    }
}

第三种方式:

根据程序的单一职责,一个工厂负责生产一种款式的毛衣,ChildrenFactory负责生产童装,AdultFactory负责生产成人装,OldFactory负责生产老年装,然后再进行细分,童装还可以划分成不同年龄段的男孩、女孩款式,成人装进一步划分高矮胖瘦的男人、女人款式,老年装划分不同年龄身高的男款、女款,多个工厂的特点:职责单一,方便扩展

重构SweaterFactory为抽象工厂,代码如下:

public abstract class SweaterFactory {

    public abstract Product createProduct();
}

负责生产童装的工厂,代码如下:

public class ChildrenFactory extends SweaterFactory {
    @Override
    public Product createProduct() {
        return new ChildrenSweater();
    }
}

负责生产成人装的工厂,代码如下:

public class AdultFactory extends SweaterFactory {
    @Override
    public Product createProduct() {
        return new AdultSweater();
    }
}

负责生产老年装的工厂,代码如下:

public class OldFactory extends SweaterFactory {
    @Override
    public Product createProduct() {
        return new OldSweater();
    }
}

第三种方式又叫做抽象工厂模式,属于一种特殊的工厂模式,同时复合工厂模式的特点,工厂模式的特点:将创建相同对象的过程封装成方法,调用同一个方法完成相同对象的创建

在实际的开发中,把握住了工厂模式的特点,然后进行重构,还会有其他的方式,创建相同对象的过程可以封装成实例方法或类方法,都是可以的,文章开头线程池的实例中就包含实例方法和类方法。

五、总结

本篇文章从什么是工厂模式、什么时候使用工厂模式以及工厂模式几种方式入手,认真分析了工厂模式的特点:将创建相同对象的过程封装成方法,调用同一个方法完成相同对象的创建,把符合该特点的设计模式称为工厂模式,最后列举几种工厂模式的方式,深入理解工厂模式。

当前文章价值3.88元,扫一扫支付后添加微信提供帮助!(如不能解决您的问题,可以申请退款)

你可能感兴趣的文章

来源:每日教程每日一例,深入学习实用技术教程,关注公众号TeachCourse
转载请注明出处: https://www.teachcourse.cn/2459.html ,谢谢支持!

资源分享

如何使用SVN提交项目备份? 如何使用SVN提交项目备份?
隐式意图获取所有符合的Activity 隐式意图获取所有符合的Activit
sql server存储过程基础语法 sql server存储过程基础语法
VirtualBox如何安装Windows 7操作系统 VirtualBox如何安装Windows 7