设计模式--工厂方法模式【Factory Method Pattern】

声明:本文从网络书籍整理而来,并非原创。

女娲造人(第一次尝试)

女娲在补了天后,下到凡间一看,哇塞,风景太优美了,天空是湛蓝的,水是清澈的,空气是清新的,太美丽了,然后就待时间长了就有点寂寞了,没有动物,这些看的到都是静态的东西呀,怎么办?

别忘了是神仙呀,没有办不到的事情,于是女娲就架起了八卦炉(技术术语:建立工厂)开始创建人,具体过程是这样的:先是泥巴捏,然后放八卦炉里烤,再扔到地上成长,但是意外总是会产生的:

第一次烤泥人,兹兹兹兹~~,感觉应该熟了,往地上一扔,biu~,一个白人诞生了,没烤熟!

第二次烤泥人,兹兹兹兹兹兹兹兹~~,上次都没烤熟,这次多烤会儿,往地上一扔,嘿,熟过头了,黑人哪!

第三次烤泥人,兹~兹~兹~,一边烤一边看着,嘿,正正好,Perfect!优品,黄色人类!

这个过程还是比较有意思的,先看看类图:

用程序来体现,先定义一个人类的总称:

public interface IHuman {
    public void laugh();
    public void cry();
    public void talk();
}

然后定义具体的人类:

public class YellowHuman implements IHuman {
    public void laugh(){
        System.out.println("黄色人类会大笑,幸福呀!");
    }
    public void cry(){
        System.out.println("黄色人类会哭");
    }
    public void talk(){
        System.out.println("黄色人类会说话,一般说的都是双字节");
    }
}

public class WhiteMan implements IHuman {
    public void laugh(){
        System.out.println("白色人类会大笑,侵略的笑声");
    }
    public void cry(){
        System.out.println("白色人类会哭");
    }
    public void talk(){
        System.out.println("白色人类会说话,一般都是但是单字节!");
    }
}

public class BlackMan implements IHuman {
    public void laugh(){
        System.out.println("黑人会笑");
    }
    public void cry(){
        System.out.println("黑人会哭");
    }
    public void talk(){
        System.out.println("黑人可以说话,一般人听不懂");
    }
}

人类也定义完毕了,那我们把八卦炉(工厂)定义出来:

public class HumanFactory {
    public static Human createHuman(Class c) {
        Human  human=null;  //定义一个类型的人类
        try{
            human  =  (Human)Class.forName(c.getName()).newInstance();   //产生一个人类
        }catch (InstantiationException e){//你要是不说个人类颜色的话,没法烤,要白的黑,你说话了才好烤
            System.out.println("必须指定人类的颜色");
        }catch  (IllegalAccessException  e){  //定义的人类有问题,那就烤不出来了,这是...
            System.out.println("人类定义错误!");
        }catch (ClassNotFoundException e){ //你随便说个人类,我到哪里给你制造去?!
            System.out.println("混蛋,你指定的人类找不到!");
        }  

        return human;
    }
}

然后我们再把女娲声明出来:

public class NvWa {
    public static void main(String[] args) {
        //女娲第一次造人,试验性质,少造点,火候不足,缺陷产品
        System.out.println("------------造出的第一批人是这样的:白人-----------------");
        Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);
        whiteHuman.cry();
        whiteHuman.laugh();
        whiteHuman.talk(); 

        //女娲第二次造人,火候加足点,然后又出了个次品,黑人
        System.out.println("\n\n------------造出的第二批人是这样的:黑人-----------------");
        Human blackHuman = HumanFactory.createHuman(BlackHuman.class);
        blackHuman.cry();
        blackHuman.laugh();
        blackHuman.talk(); 

        //第三批人了,这次火候掌握的正好,黄色人类
        System.out.println("\n\n------------造出的第三批人是这样的:黄色人类-----------------");
        Human yellowHuman = HumanFactory.createHuman(YellowHuman.class);
        yellowHuman.cry();
        yellowHuman.laugh();
        yellowHuman.talk() ;
    }
}

这样这个世界就热闹起来了,人也有了,但是这样创建太累了,神仙也会累的,那怎么办?

女娲造人(第二次尝试)

神仙就想了:我塞进去一团泥巴,随机出来一群人,管他是黑人、白人、黄人,只要是人就成(你看看,神仙都偷懒,何况是我们人) ,先修改类图:

然后看我们的程序修改,先修改 HumanFactory.java,增加了createHuman()方法:

public class HumanFactory {
    public static Human createHuman(Class c) {
        Human  human=null;  //定义一个类型的人类
        try{
            human  =  (Human)Class.forName(c.getName()).newInstance();   //产生一个人类
        }catch (InstantiationException e){//你要是不说个人类颜色的话,没法烤,要白的黑,你说话了才好烤
            System.out.println("必须指定人类的颜色");
        }catch  (IllegalAccessException  e){  //定义的人类有问题,那就烤不出来了,这是...
            System.out.println("人类定义错误!");
        }catch (ClassNotFoundException e){ //你随便说个人类,我到哪里给你制造去?!
            System.out.println("混蛋,你指定的人类找不到!");
        }  

        return human;
    } 

    //女娲生气了,把一团泥巴塞到八卦炉,哎产生啥人类就啥人类
    public static Human createHuman(){
        Human  human=null;  //定义一个类型的人类 

        //首先是获得有多少个实现类,多少个人类
        List<Class> concreteHumanList = ClassUtils.getAllClassByInterface(Human.class);  //定义了多少人类
        //八卦炉自己开始想烧出什么人就什么人
        Random random = new Random();
        int rand = random.nextInt(concreteHumanList.size());
        human  = createHuman(concreteHumanList.get(rand));
        return human;
    }
}

然后看女娲是如何做的:

public class NvWa {
    public static void main(String[] args) {
        //女娲第一次造人,试验性质,少造点,火候不足,缺陷产品
        System.out.println("------------造出的第一批人是这样的:白人-----------------");
        Human whiteHuman = HumanFactory.createHuman(WhiteHuman.class);
        whiteHuman.cry();
        whiteHuman.laugh();
        whiteHuman.talk(); 

        //女娲第二次造人,火候加足点,然后又出了个次品,黑人
        System.out.println("\n\n------------造出的第二批人是这样的:黑人-----------------");
        Human blackHuman = HumanFactory.createHuman(BlackHuman.class);
        blackHuman.cry();
        blackHuman.laugh();
        blackHuman.talk(); 

        //第三批人了,这次火候掌握的正好,黄色人类
        System.out.println("\n\n------------造出的第三批人是这样的:黄色人类-----------------");
        Human yellowHuman = HumanFactory.createHuman(YellowHuman.class);
        yellowHuman.cry();
        yellowHuman.laugh();
        yellowHuman.talk() ;

        //女娲烦躁了,爱是啥人类就是啥人类,烧吧
        for(int i=0;i<10000000000;i++){
            System.out.println("\n\n------------随机产生人类了-----------------" + i);
            Human human = HumanFactory.createHuman();
            human.cry();
            human.laugh();
            human.talk();
        } 

    }
}

哇,这个世界热闹了! ,不过还没有完毕,这个程序你跑不起来,还要有这个类:

@SuppressWarnings("all")
public class ClassUtils { 

    //给一个接口,返回这个接口的所有实现类
    public static List<Class> getAllClassByInterface(Class c){
        List<Class> returnClassList = new ArrayList<Class>();  //返回结果 

        //如果不是一个接口,则不做处理
        if(c.isInterface()){
            String packageName = c.getPackage().getName();  //获得当前的包名
            try {
                List<Class> allClass  = getClasses(packageName);  //获得当前包下以及子包下的所有类
                //判断是否是同一个接口
                for(int i=0;i<allClass.size();i++){
                    if(c.isAssignableFrom(allClass.get(i))){  //判断是不是一个接口
                        if(!c.equals(allClass.get(i))){   //本身不加进去
                            returnClassList.add(allClass.get(i));
                        }
                    }
                }
            }catch (ClassNotFoundException e) {
                e.printStackTrace();
            }catch (IOException e) {
                e.printStackTrace();
            }
        } 

        return returnClassList;
    } 

    //从一个包中查找出所有的类,在jar包中不能查找
    private static List<Class> getClasses(String packageName) throws ClassNotFoundException, IOException {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        String path = packageName.replace(‘.‘, ‘/‘);
        Enumeration<URL> resources = classLoader.getResources(path);
        List<File> dirs = new ArrayList<File>();
        while (resources.hasMoreElements()) {
            URL resource = resources.nextElement();
            dirs.add(new File(resource.getFile()));
        }
        ArrayList<Class> classes = new ArrayList<Class>();
        for (File directory : dirs) {
            classes.addAll(findClasses(directory, packageName));
        }
        return classes;
    } 

    private static List<Class> findClasses(File directory, String packageName) throws ClassNotFoundException {
        List<Class> classes = new ArrayList<Class>();
        if (!directory.exists()) {
            return classes;
        }
        File[] files = directory.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                assert !file.getName().contains(".");
                classes.addAll(findClasses(file, packageName + "." + file.getName()));
            } else if (file.getName().endsWith(".class")) {
                classes.add(Class.forName(packageName + ‘.‘ +
                file.getName().substring(0, file.getName().length() - 6)));
            }
        }
        return classes;
    }
}

告诉你了,这个 ClassUtils 可是个宝,用处可大了去了,可以由一个接口查找到所有的实现类,也可以由父类查找到所有的子类,这个要自己修改一下,动脑筋想下,简单的很!完整的类图如下:

我们来总结一下,特别是增加了 createHuman()后,是不是这个工厂的扩展性更好了?你看你要再加一个人类,只要你继续集成 Human 接口成了,然后啥都不用修改就可以生产了,具体产多少,那要八卦炉说了算,简单工厂模式就是这么简单,那我们再引入一个问题:人是有性别的呀,有男有女,你这怎么没区别,别急,这个且听下回分解!

工厂方法模式还有一个非常重要的应用,就是延迟始化(Lazy initialization),什么是延迟始化呢?一个对象初始化完毕后就不释放,等到再次用到得就不用再次初始化了,直接从内存过中拿到就可以了,怎么实现呢,很简单,看例子:

@SuppressWarnings("all")
public class HumanFactory {
    //定义一个MAP,初始化过的Human对象都放在这里
    private static HashMap<String,Human> humans = new HashMap<String,Human>(); 

    //定一个烤箱,泥巴塞进去,人就出来,这个太先进了
    public static Human createHuman(Class c){
        Human  human=null;  //定义一个类型的人类 

        try {
            //如果MAP中有,则直接从取出,不用初始化了
            if(humans.containsKey(c.getSimpleName())){
                human  = humans.get(c.getSimpleName());
            }else{
                human  =  (Human)Class.forName(c.getName()).newInstance();
                //放到MAP中
                humans.put(c.getSimpleName(), human);
            }
        } catch (InstantiationException e) {//你要是不说个人类颜色的话,没法烤,要白的黑,你说话了才好烤
            System.out.println("必须指定人类的颜色");
        } catch (IllegalAccessException e) { //一定定义的人类有问题,那就烤不出来了,这是...
            System.out.println("人类定义错误!");
        } catch (ClassNotFoundException e) { //你随便说个人类,我到哪里给你制造去?!
            System.out.println("混蛋,你指定的人类找不到!");
        } 

        return human;
    }
} 

这个在类初始化很消耗资源的情况比较实用,比如你要连接硬件,或者是为了初始化一个类需要准备比较多条件(参数) ,通过这种方式可以很好的减少项目的复杂程度。

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-07-30 10:17:57

设计模式--工厂方法模式【Factory Method Pattern】的相关文章

设计模式-03工厂方法模式(Factory Method Pattern)

插曲.简单工厂模式(Simple Factory Pattern) 介绍工厂方法模式之前,先来做一个铺垫,了解一下简单工厂模式,它不属于 GoF 的 23 种经典设计模式,它的缺点是增加新产品时会违背"开闭原则". 1.模式动机 考虑一个简单的软件应用场景,一个软件系统可以提供多个外观不同的按钮(如圆形按钮.矩形按钮.菱形按钮等),这些按钮都源自同一个基类,不过在继承基类后不同的子类有不同的实现方式从而使得它们可以呈现不同的外观,如果我们希望在使用这些按钮时,不需要知道这些具体按钮类的

工厂方法模式(Factory Method Pattern)

2. 工厂方法模式(Factory Method Pattern) 2.1. 模式动机 现在对该系统进行修改,不再设计一个按钮工厂类来统一负责所有产品的创建,而是将具体按钮的创建过程交给专门的工厂子类去完成,我们先定义一个抽象的按钮工厂类,再定义具体的工厂类来生成圆形按钮.矩形按钮.菱形按钮等,它们实现在抽象按钮工厂类中定义的方法.这种抽象化的结果使这种结构可以在不修改具体工厂类的情况下引进新的产品,如果出现新的按钮类型,只需要为这种新类型的按钮创建一个具体的工厂类就可以获得该新按钮的实例,这一

php设计模式——工厂方法模式(Factory Method)

二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 1 <?php 2 /* 3 * php设计模式——工厂方法模式(Factory Method) 4 */ 5 6 7 /* 8 * IAp

Android设计模式——工厂方法模式(Factory Method)

二十三种设计模式分为三大类: 创建型模式,共五种:工厂方法模式.抽象工厂模式.单例模式.建造者模式.原型模式. 结构型模式,共七种:适配器模式.装饰器模式.代理模式.外观模式.桥接模式.组合模式.享元模式. 行为型模式,共十一种:策略模式.模板方法模式.观察者模式.迭代子模式.责任链模式.命令模式.备忘录模式.状态模式.访问者模式.中介者模式.解释器模式. 1 package com.example.main; 2 3 import android.app.Activity; 4 import

工厂模式--工厂方法模式(Factory Method Pattern)

2.1. 模式动机 现在对该系统进行修改,不再设计一个按钮工厂类来统一负责所有产品的创建,而是将具体按钮的创建过程交给专门的工厂子类去完成,我们先定义一个抽象的按钮工厂类,再定义具体的工厂类来生成圆形按钮.矩形按钮.菱形按钮等,它们实现在抽象按钮工厂类中定义的方法.这种抽象化的结果使这种结构可以在不修改具体工厂类的情况下引进新的产品,如果出现新的按钮类型,只需要为这种新类型的按钮创建一个具体的工厂类就可以获得该新按钮的实例,这一特点无疑使得工厂方法模式具有超越简单工厂模式的优越性,更加符合"开闭

设计模式之六:工厂方法模式(Factory method Pattern)

工厂方法(Factory Method)模式就是定义一个创建对象的工厂接口,将实际创建工作推迟到子类当中. 核心工厂类不再负责具体产品的创建,仅提供了具体工厂子类必须实现的接口,这样核心类成为一个抽象工厂角色,这样做的好处是工厂方法模式可以使系统在不修改具体工厂角色的情况下进行引进新的产品. 在Factory Method模式中,工厂类与产品类往往具有平行的等级结构,它们之间一一对应. 从上图可以看出,工厂方法模式有四个角色: 抽象工厂角色(ICreator):是工厂方法模式的核心,与应用程序无

工厂方法模式 Factory Method

工厂方法(Factory Method)模式的意义是定义一个创建产品对象的工厂接口,将实际创建工作推迟到子类当中.核心工厂类不再负责产品的创建,这样核心类成为一个抽象工厂角色,仅负责具体工厂子类必须实现的接口,这样进一步抽象化的好处是使得工厂方法模式可以使系统在不修改具体工厂角色的情况下引进新的产品. #include <iostream> using namespace std; class Fruit{ public: virtual void show()=0; }; class App

3.工厂方法模式(Factory Method)

using System; using System.Reflection; namespace ConsoleApplication1 { class Program { static void Main(string[] args) { //需要什么食物,创建什么工厂 FactoryCreate _factorycreate = new YaJiaFactoryCreate(); Food _food = _factorycreate.Create(); _food.MakeFood();

Yii2 设计模式——工厂方法模式

工厂方法模式 模式定义 工厂方法模式(Factory Method Pattern)定义了一个创建对象的接口,但由子类决定要实例化的类是哪一个.工厂方法让类吧实例化推迟到子类. 什么意思?说起来有这么几个要点: 对象不是直接new产生,而是交给一个类方法去完成.比如loadTableSchema()方法 这个方法是抽象的,且必须被子类所实现 这个提供实例的抽象方法需要参与到其他逻辑中,去完成另一项功能.比如loadTableSchema()方法出现在getTableSchema()方法中,参与实

工厂方法(Factory Method)模式

一.工厂方法(Factory Method)模式 工厂方法(FactoryMethod)模式是类的创建模式,其用意是定义一个创建产品对象的工厂接口,将实际创建工作推迟的子类中. 工厂方法模式是简单工厂模式的进一步抽象和推广.由于使用了多态性,工厂方法模式保持了简单工厂模式的有点,而且客服了它的缺点. 在工厂方法模式中,核心的工厂类不再负责所有产品的创建,而是将具体创建工作交给予子类去做.这个核心类仅仅负责给出具体工厂必须实现的接口,而不接触哪一个产品类被实例化这种细节.这使得工厂方法模式可以允许