设计模式-工厂模式之简单工厂模式

 一、方式一

  简单工厂模式定义:定义一个工厂类,它可以根据参数的不同返回不同类的实例,被创建的实例通常都具有共同的父类。因为在简单工厂模式中用于创建实例的方法是静态(static)方法,因此简单工厂模式又被称为静态工厂方法(Static Factory Method)模式,它属于类创建型模式。

  适用场景

    (1) 工厂类负责创建的对象比较少,由于创建的对象较少,不会造成工厂方法中的业务逻辑太过复杂。

    (2) 客户端只知道传入工厂类的参数,对于如何创建对象并不关心。

抽象产品接口:

/**
 * 接口形式-抽象产品类
 * (1)每一个方法,都需要在具体实现类中实现。
 * (2)接口中只能定义方法,不能有具体的实现的方法。
 */
public interface Shape {
    public void draw();
    public void seeDraw();
}

具体产品实现

/**
 * 圆形
 */
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("打印circle中draw方法!!!");
    }
    @Override
    public void seeDraw() {
    }
}
/**
 * 方形
 */
public class Square implements Shape {
    @Override
    public void draw() {
        System.out.println("打印Square中draw方法!!!");
    }
    @Override
    public void seeDraw() {
    }
}
/**
 * 三角形
 */
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("打印Rectangle中draw方法!!!");
    }
    @Override
    public void seeDraw() {
    }
}

产品创建工厂类:

/**
 * 简单工厂模式
 */
public class Factory {
    //获取类的方法,使用静态方法
    public static Shape getInstance(String shape){
        if(shape==null){
            return null;
        }
        switch (shape.toLowerCase()) {
        case "circle":
            return new Circle();
        case "rectangle":
            return new Rectangle();
        case "square":
            return new Square();
        default:
            return null;
        }
    }
}

测试:

public class FactoryTest {
    public static void main(String[] args) {
        //类Factory中方法getInstance时静态方法
        long begin0 = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            Factory.getInstance("circle");
        }
        long end0 = System.currentTimeMillis();  
        System.out.println("总耗时0:" + (end0 - begin0));

        Shape shape2=Factory.getInstance("rectangle".toUpperCase());
        shape2.draw();
        Shape shape3=Factory.getInstance("SQuare");
        shape3.draw();
    }
}

  总结:(1)所有产品类公共的代码移至抽象产品类,并在抽象产品类中声明一些抽象方法,以供不同的具体产品类来实现。(2) 简单工厂模式的工厂类一般是使用静态方法,通过接收的参数的不同来返回不同的对象实例。

  弊端:(1)每次添加的新产品,都需要在工厂类中添加相应的对象创建,违反“开闭原则”,无法扩展。

设计中涉及角色类型:

  工厂角色:主要用于创建所需的对象。

  抽象产品角色:简单工厂模式所创建的所有对象的父类。注意,这里的父类可以是接口也可以是抽象类,它负责描述所创建实例共有的公共接口。

  具体产品角色: 简单工厂所创建的具体实例对象(具体业务实现内容),这些具体的产品往往都拥有共同的父类。

二、方式二

  利用反射方式实现工厂模式:

  首先说明什么是反射?

  答:Java反射(Java Reflection)是指在程序运行时获取已知名称的类或已有对象的相关信息的一种机制,包括类的方法、属性、父类等信息,还包括实例的创建和实例类型的判断等。在反射中使用最多的类是Class,Class类的实例表示正在运行的Java应用程序中的类和接口,其forName(String className)方法可以返回与带有给定字符串名的类或接口相关联的 Class对象,再通过Class对象的newInstance()方法创建此对象所表示的类的一个新实例,即通过一个类名字符串得到类的实例。

  举个简单的反射例子

//通过类名生成实例对象并将其返回  
Class c=Class.forName("String");  
Object obj=c.newInstance();  
return obj;

  反射方式设计的模式:

/**
 * 简单工厂模式
 * 利用反射方式:解决每次增加一个产品时,都需要增加一个对象实现工厂的缺点
 * 但是每次实例化对象,需要进行强制转换
 */
public class FactoryReflect {
    /**
     * 方法一
     */
    //?类型通配符,限定类型上限是shape,这里已经给出类型转换的限制,
    //所以工厂生成的类型必须是shape接口,或实现shape接口的子类
    //使用的时候,必须使用采用强制转换
    public static Object getClass1(Class<? extends Shape> shape){
        Object obj=null;
        try {
            //利用反射技术
            obj = Class.forName(shape.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return obj;
    }
    /**
     * 方法二
     */
    //?类型通配符,限定类型上限使用泛型T
    //省略类型强制转换,支持多态
    @SuppressWarnings("unchecked")
    public static <T> T getClass2(Class<? extends T> classz){
        T obj=null;
        try {
            //利用反射技术
            obj = (T) Class.forName(classz.getName()).newInstance();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return obj;
    }
    /**
     * 方法三
     */
    //针对多个接口实现一个公共的工厂类,没有类型限制
    //使用的时候,必须使用采用强制转换
    public static <T> Object getClass3(Class<T> classz){
        if(classz==null){
            return null;
        }
        Object obj = null;
        try {
            //利用反射技术
            obj = Class.forName(classz.getName()).newInstance();
        } catch (InstantiationException | IllegalAccessException |ClassNotFoundException e) {
            e.printStackTrace();
        }
        return obj;
    }
}

  注意:反射效率很低,在高并发的条件下,反射绝对不是一个良好的选择。

  java反射之所以慢,最主要的就是就是编译器没法对反射相关的代码做优化。

三、方式三

  简单模式的扩展(使用xml文件):

  (1)简单工厂模式中存在的问题:每更换一个Shape对象都需要修改客户端代码中静态工厂方法的参数,客户端代码将要重新编译

  将静态工厂方法的参数存储在XML或properties格式的配置文件中,如下config.xml所示:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <factoryBean1>circle</factoryBean1>
    <factoryBean2>rectangle</factoryBean2>
    <factoryBean3>square</factoryBean3>
</config>

解析xml实现工厂模式:

/**
 * XML文件存储工厂类,需要解析文件,性能、效率会受到影响
 */
public class XMLFactory {
    public static String getType(String tag){
        String type=null;
        try {
            DocumentBuilderFactory dFactory=DocumentBuilderFactory.newInstance();
            DocumentBuilder dbuilder=dFactory.newDocumentBuilder();
            Document doc=dbuilder.parse(new File("src/Factory.xml"));
            //获取包含图表类型的文本节点
            NodeList nolist=doc.getElementsByTagName(tag);
            Node node=nolist.item(0).getFirstChild();
            type=node.getNodeValue().trim();
            
        } catch (Exception e) {
            e.printStackTrace();
        }
        return type;
    }
}

(2)延迟初始化

  延迟初始化(Lazy initialization):一个对象被使用完毕后,并不立刻释放,工厂类保持其初始状态,等待再次被使用。也是工厂模式的一个扩展应用。

  实现方式:工厂类负责产品类对象的创建工作,并且通过Map变量产生一个缓存,对需要再次被重用的对象保留。

/**
 * 通过定义一个Map容器,容纳所有产生的对象,每次在new一个对象的时候先判断Map中有没有,有就不用再new了,直接取。
 * 另外,每次new过一个对象后,也将其放入Map中方便下次调用。
 */
public class MapFactory {
    private static final Map<String,Object> premap=new HashMap<String,Object>();
    @SuppressWarnings("unchecked")
    public static synchronized <T> T createFactory(String type){
        T factory=null;
        //map中存在改对象
        if(premap.containsKey(type)){
            factory=(T) premap.get(type);
        }else{
            if(type.toLowerCase().equals("circle")){
                factory=(T) new Circle();
            }else if(type.toLowerCase().equals("square")){
                factory=(T) new Square();
            }else{
                factory=(T) new Rectangle();
            }
        }
        return factory;
    }
}

  说明:通过定义一个Map容器,容纳所有产生的对象,每次在new一个对象的时候先判断Map中有没有,有就不用再new了,直接取。另外,每次new过一个对象后,也将其放入Map中方便下次调用。

  实例场景:延迟加载时很有用的,比如JDBC连接数据库,会要求设置一个MaxConnections最大连接数量,该数量就是内存中最大实例化的数量。

四、方式四

抽象接口方式:

  抽象产品类:

/**
 * 抽象形式-抽象产品
 * (1)以abstract关键字实现的抽象类,其方法只能有2中形式:abstract定义的方法,或者具体方法体实现。
 * (2)子类继承抽象方法,只实现abstract定义的方法体
 */
public abstract class AShape {
    //非公共方法,需要子类具体实现 
    public abstract void draw();
    //产品类的公共方法,已经实现 
    public void seeDraw(){
        //实现了公共的逻辑 
    };
}

  具体产品实现类:

public class Circle2 extends AShape{
    @Override
    public void draw() {
        System.out.println("打印出Ashape类实现的方法类!!!");
    }
}

  以上所有代码的测试:

public class FactoryTest {
    public static void main(String[] args) {
        //类Factory中方法getInstance时静态方法
        long begin0 = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            Factory.getInstance("circle");
        }
        long end0 = System.currentTimeMillis();  
        System.out.println("总耗时0:" + (end0 - begin0));
//        Shape shape1=Factory.getInstance("circle");
//        shape1.draw();
        
        Shape shape2=Factory.getInstance("rectangle".toUpperCase());
        shape2.draw();
        
        Shape shape3=Factory.getInstance("SQuare");
        shape3.draw();
        //利用反射
        long begin = System.currentTimeMillis();
        for (int i = 0; i < 100; i++) {
            FactoryReflect.getClass1(Circle.class);
        }
//        Shape circle=(Shape) FactoryReflect.getClass1(Circle.class);
        long end = System.currentTimeMillis();  
        System.out.println("总耗时1:" + (end - begin));  
//        circle.draw();
        
        Shape rectangle=(Shape) FactoryReflect.getClass1(Rectangle.class);
        rectangle.draw();
        //利用反射,并使用范围T
        Shape square2=FactoryReflect.getClass2(Square.class);
        square2.draw();
        //利用反射,并使用范围T
        Shape square3=(Shape) FactoryReflect.getClass3(Rectangle.class);
        square3.draw();

        //xml文件获取对象
//        String type=XMLFactory.getType("factoryBean1");
//        String type=XMLFactory.getType("factoryBean2");
        String type=XMLFactory.getType("factoryBean3");
        Shape circle2=Factory.getInstance(type);
        circle2.draw();
        
        Shape circle4=MapFactory.createFactory("circle");
        circle4.draw();
    }
}

 

posted @ 2018-03-27 22:41  小码农成长记  阅读(199)  评论(0)    收藏  举报