设计模式-工厂模式(Factory)

工厂模式:创建型设计模式

意图:定义一个创建对象的接口,让其子类自己决定实例化哪一个工厂类,工厂模式使其创建过程延迟到子类进行。

主要解决:接口选择的问题。

何时使用:我们明确计划不同条件下创建不同实例。

如何解决:让子类实现工厂接口,返回的也是一个抽象的产品。

关键代码:创建过程在其子类执行。

优点:

    1.一个调用者想要创建一个对象,只需要知道其名称即可。

    2.扩展性高,如果想增加一个产品,只要扩展一个工厂类即可。

    3.屏蔽产品的具体实现,调用者只关心产品的接口。

缺点:

    每次增加一个产品,都需要增加一个具体类和对象实现工厂,使得系统中类的个数成倍增加,在一定程度上增加了系统的复杂度,同时也增加了系统具体类的依赖。

应用场景:

    1.日志记录器:记录可能记录到本地磁盘、系统事件、远程服务器等,用户可以选择记录日志到什么地方。

    2.数据库访问:当用户不知道系统采用哪一类数据库,以及数据库可能有变化时

    3.设计一个连接服务器的框架,需要三个协议,“POP3”、“IMAP”、“HTTP”,可以把这三个作为产品类,共同实现一个接口。

代码示例开始

工厂接口:

package com.yuxx.factory;

/**
 * 图形接口
 */
public interface Shape {
    void draw();
}

具体产品类:

package com.yuxx.factory;

/**
 * 长方形实现类
 */
public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("draw a rectangle");
    }
}
package com.yuxx.factory;

/**
 * 正方形实现类
 */
public class Square implements  Shape {
    @Override
    public void draw() {
        System.out.println("draw a square");
    }
}
package com.yuxx.factory;

/**
 * 圆形实现类
 */
public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("draw a circle");
    }
}

工厂类:

package com.yuxx.factory;

public class ShapeFactory {

    public Shape getShape(String shapeType){
        if(shapeType == null){
            return null;
        }
        switch (shapeType){
            case "CIRCLE":
                return new Circle();
            case "RECTANGLE":
                return new Rectangle();
            case "SQUARE":
                return new Square();
            default:
                    return null;
        }
    }
}

测试调用类:

package com.yuxx.factory;

import org.junit.Test;

public class FactoryPatternDemo {

    @Test
    public void test(){
        ShapeFactory shapeFactory = new ShapeFactory();

        //获取圆形的对象,并调用draw方法
        Shape circle = shapeFactory.getShape("CIRCLE");
        circle.draw();

        //获取长方形的对象,并调用draw方法
        Shape rectangle = shapeFactory.getShape("RECTANGLE");
        rectangle.draw();

        //获取正方形的对象,并调用draw方法
        Shape square = shapeFactory.getShape("SQUARE");
        square.draw();
    }
}

由上面代码可以看到,工厂模式的实现逻辑就是:先创建一个工厂接口类,然后由不同的产品类去实现这个工厂接口。然后创建一个工厂类,工厂类中根据产品名称分别实现不同产品对象的构造方法。调用时,也是需要先创建工厂类的对象,然后根据产品名称创建产品类对象,并进行方法调用。

在学习工厂模式之前,在工作中遇到一个需求,我觉得正好可以用工厂模式。需求是这样的:

    这个业务流程是关于体育赛事报名的,困难之处在于,每个比赛项目的运动员报名人数限制和工作人员人数等等都有限制(实际报名规定一两句说不清,此处也不赘述,总之比较复杂)。这些限制都是需要再报名记录进入数据库之前一一进行判断。按照以往的做法,就是流水式写法,根据用户的要求一点一点把报名规定的要求加上。这样的后果就是,代码机构冗余复杂,可读性低,可维护性低。

然后我为同事出的意见就是,采用工厂模式的写法,去把每个比赛项目的规定限制分开判断。即:会有一个工厂接口CheckEvent,里面定义一个“check”的接口。然后把田径、游泳、篮球等项目的校验分别用实现工厂接口的类去实现(TrackAndField implement CheckEvent、Swim implement CheckEvent、Basketball implement CheckEvent)。然后创建一个工厂类(CheckEventFactory),等到报名的时候,通过工厂类去调用每个项目判断类的校验方法。

下面会介绍Spring与工厂模式的关系。但是在说Spring中的工厂模式前,我们先补充一下工厂模式的动态实现。

Shape接口和Rectangle、Square、Cicle这三个产品类保持不变,将工厂类中获取产品实例的方法改为通过类加载器获取类对象,再由类对象获取其实例:

package com.yuxx.factory;

public class DynamicShapeFactoryBean {
    
    //入参className此时须是类的全路径
    public static Shape getShapge(String className){
        Shape shape = null;
        try{
            shape = (Shape) Class.forName(className).newInstance();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return shape;
    }

}

动态工厂调用:

package com.yuxx.factory;

import org.junit.Test;

public class DynamicFactoryPatternDemo {

    @Test
    public void test() {
        Shape rectangle = DynamicShapeFactoryBean.getShapge("com.yuxx.factory.Rectangle");
        rectangle.draw();

        Shape square = DynamicShapeFactoryBean.getShapge("com.yuxx.factory.Square");
        square.draw();

        Shape circle = DynamicShapeFactoryBean.getShapge("com.yuxx.factory.Circle");
        circle.draw();
    }
}

Spring与工厂模式:

说到Spring中的工厂模式,可以说是无处不见了。我们知道,Spring中Bean的管理权交由Spring IOC容器。殊不知,Spring容器就是一个超级大的工厂,它动态管理着所有Bean实例。

由于Spring的DI机制(与IOC是同一个事),我们在Spring框架中使用Bean实例时,不再需要自己去创建Bean的实例,Spring容器就能帮我们创建好,然后注入到我们定义的引用中。虽然我们不用去写创建过程的代码,但是需要通过xml配置文件或者Java配置类去装配Bean:

<bean class="com.yuxx.bean.Person" id="person" scope="singleton">
    <property name="age" value="18"></property>
    <property name="name" value="张三"></property>
</bean>
@Scope("singleton")
@Bean("person")
public Person person(){
	System.out.println("向容器中添加Person...");
	return new Person("张三",25);
}

要知道,Spring只是一个框架,框架中封装的东西也都是具有普遍性的,所以Spring其实并不知道我们想要创建的是哪个类的实例。所以一定是通过反射或者类加载器一类的底层逻辑实现。

下班,后续继续补充Spring中工厂模式的痕迹

转载于:https://my.oschina.net/alexjava/blog/1843844

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值