1、概述
工厂方法模式是一种创建型设计模式, 其在父类中提供一个创建对象的方法, 允许子类决定实例化对象的类型。
2、适用场景
1)不确定对象的类别、个数或者依赖关系。
2)需要扩展工具库或者内部组件。
3)需要重复使用的对象(例如资源池等)。
3、实例
有以下业务场景:一个商店,出售多种货物,包括汽车car,船ship,飞机plane。
3.1 不使用工厂模式
定义三个实体类:
import lombok.Data;
/**
 * Car
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Data
public class Car {
    private String name;
    private String price;
    public void run() {
        System.out.println("the car is running");
    }
}
import lombok.Data;
/**
 * Plane
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Data
public class Plane {
    private String name;
    private String price;
    public void run() {
        System.out.println("the Plane is running");
    }
}
import lombok.Data;
/**
 * Ship
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Data
public class Ship {
    private String name;
    private String price;
    public void run() {
        System.out.println("the Ship is running");
    }
}
定义接口和实现:

如上图所示:一个接口IShop,有一个实现类ShopImpl,获取三种产品的方法在这个接口中定义了三次。
/**
 * IShop
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
public interface IShop {
    /**
     * 获取汽车
     * @date: 2020/12/29
     * @param
     * @return com.cloud.bssp.designpatterns.factorymethod.withoutdesign.factory.Car
     * @author weirx
     * @version 3.0
     */
    Car getCar();
    /**
     * 获取船
     * @date: 2020/12/29
     * @param
     * @return com.cloud.bssp.designpatterns.factorymethod.withoutdesign.factory.Car
     * @author weirx
     * @version 3.0
     */
    Ship getShip();
    /**
     * 获取飞机
     * @date: 2020/12/29
     * @param
     * @return com.cloud.bssp.designpatterns.factorymethod.withoutdesign.factory.Car
     * @author weirx
     * @version 3.0
     */
    Plane getPlane();
}
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.Car;
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.IShop;
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.Plane;
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.Ship;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
 * ShopImpl
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Slf4j
@Service
public class ShopImpl implements IShop {
    @Override
    public Car getCar() {
        log.info("this is car");
        return new Car();
    }
    @Override
    public Ship getShip() {
        log.info("this is Ship");
        return new Ship();
    }
    @Override
    public Plane getPlane() {
        log.info("this is Plane");
        return new Plane();
    }
}
创建一个测试类:
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.Car;
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.IShop;
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.Plane;
import com.cloud.bssp.designpatterns.factorymethod.withoutdesign.Ship;
import com.cloud.bssp.BsspUserApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
 * test
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BsspUserApplication.class)
public class TestDemo {
    @Autowired
    private IShop shop;
    @Test
    public void testWithout() {
        Car car = shop.getCar();
        car.run();
        Ship ship = shop.getShip();
        ship.run();
        Plane plane = shop.getPlane();
        plane.run();
    }
}
执行结果:
2020-12-29 14:54:24.078  INFO 16884 --- [           main] c.c.b.d.f.withoutdesign.impl.ShopImpl    : this is car
the car is running
2020-12-29 14:54:24.078  INFO 16884 --- [           main] c.c.b.d.f.withoutdesign.impl.ShopImpl    : this is Ship
the Ship is running
2020-12-29 14:54:24.078  INFO 16884 --- [           main] c.c.b.d.f.withoutdesign.impl.ShopImpl    : this is Plane
the Plane is running
3.2 使用工厂模式
创建一个Product接口,定义run()方法:
/**
 * Product
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
public interface Product {
    /**
     * 行驶
     * @date: 2020/12/29
     * @param
     * @return void
     * @author weirx
     * @version 3.0
     */
    void run();
}
定义三个实体类:
import lombok.Data;
/**
 * Car
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Data
public class Car implements Product{
    private String name;
    private String price;
    @Override
    public void run() {
        System.out.println("the car is running");
    }
}
import lombok.Data;
/**
 * Plane
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Data
public class Plane implements Product{
    private String name;
    private String price;
    @Override
    public void run() {
        System.out.println("the Plane is running");
    }
}
import lombok.Data;
/**
 * Ship
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Data
public class Ship implements Product{
    private String name;
    private String price;
    @Override
    public void run() {
        System.out.println("the Ship is running");
    }
}
接口与实现如下图所示:

一个接口返回Product,三个实现分别返回三个产品实体。
定义一个工厂方法接口:
/**
 * IShop
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
public interface IShop {
    /**
     * 获取商品
     * @date: 2020/12/29
     * @param
     * @return java.lang.Object
     * @author weirx
     * @version 3.0
     */
    Product getProduct();
}
定义工厂接口实现:
/**
 * CarImpl
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Slf4j
@Service("carImpl")
public class CarImpl implements IShop {
    @Override
    public Car getProduct() {
        log.info("this is car");
        return new Car();
    }
}
/**
 * CarImpl
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Slf4j
@Service("planeImpl")
public class PlaneImpl implements IShop {
    @Override
    public Plane getProduct() {
        log.info("this is plane");
        return new Plane();
    }
}
/**
 * CarImpl
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@Slf4j
@Service("shipImpl")
public class ShipImpl implements IShop {
    @Override
    public Ship getProduct() {
        log.info("this is Ship");
        return new Ship();
    }
}
定义一个测试类:
import com.cloud.bssp.BsspUserApplication;
import com.cloud.bssp.designpatterns.factorymethod.usedesign.IShop;
import com.cloud.bssp.designpatterns.factorymethod.usedesign.Product;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
 * test
 * @date: 2020/12/29
 * @author weirx
 * @version 3.0
 */
@RunWith(SpringRunner.class)
@SpringBootTest(classes = BsspUserApplication.class)
public class TestDemo {
    @Autowired
    private IShop shipImpl;
    @Autowired
    private IShop carImpl;
    @Autowired
    private IShop planeImpl;
    @Test
    public void testUsed() {
        //以下使用注入的方式,注意在工厂IShop的每个实现指定bean的名称
        Product car = carImpl.getProduct();
        Product ship = shipImpl.getProduct();
        Product plane = planeImpl.getProduct();
        //以下使用new的方式
//        IShop planeImpl = new PlaneImpl();
//        Product plane = planeImpl.getProduct();
//
//
//        IShop carImpl = new CarImpl();
//        Product car = carImpl.getProduct();
//        car.run();
//
//        IShop shipImpl = new ShipImpl();
//        Product ship = shipImpl.getProduct();
        car.run();
        plane.run();
        ship.run();
    }
}
运行结果:
2020-12-29 15:01:59.394  INFO 21132 --- [           main] c.c.b.d.f.usedesign.impl.CarImpl         : this is car
2020-12-29 15:01:59.394  INFO 21132 --- [           main] c.c.b.d.f.usedesign.impl.ShipImpl        : this is Ship
2020-12-29 15:01:59.394  INFO 21132 --- [           main] c.c.b.d.f.usedesign.impl.PlaneImpl       : this is plane
the car is running
the Plane is running
the Ship is running
4、分析
分析对比以上两种实现方式:
实体类:
使用工厂发的实体类,定义了一个接口,其内部可以定义一些通用的方法,实体类可以通过实现该接口重写该方法,方法名统一。
在没使用工厂的实体类中,虽然也可以自定义方法,但是没有对方法的名称有限制,可自定义。后续实体越来越多,可能通用的方法名会起的多种多样,造成代码混乱,不利于统一。
接口
实用工厂方式,提供一个统一接口,内部提供一个统一获取产品的方法,其具体返回那种产品由其实现方法进行指定。此接口永远不会被修改,只会在其实现类去扩展。
未使用的工厂方法的同样提供一个接口,但是其内部分别提供了获取三种产品的三个方法,后面随着产品越来越多,这个接口的方法也会越来越多。
实现类
使用工厂方法的针对不同的产品分别实现了各个产品的实现类,每个实现类返回对应的产品,相互之间没有任何耦合。
未使用工厂方法的在一个实现类内有三种产品的获取方法,随着产品种类增加,此类会越来越长,容易造成代码耦合和过长,不利于代码阅读和管理,扩展性很差,每次新增都需要修改这个实现类。
5、总结
最后总结下上面例子中使用工厂方法的优缺点:
优点:
1)符合开闭原则,对扩展开放,对修改关闭。
2)符合迪米特原则,类与类之间没有关联,降低耦合。
3)符合单一职责,一个类或方法只负责一件事。
缺点:
引入了很多的子类,代码变得复杂。









