工厂方法模式
uml类图
定义
Define an interface for creating an object,but let subclasses decide which class to instance.
Factory Method lets a class defer instantiation to subclasses.
定义一个用于创建对象的接口,让子类决定实例化哪一个类。工厂方法是一个类的实例化延迟到其子类。
抽象产品类:负责定义产品的共性,实现对事物最抽象的定义
具体产品类:
抽象工厂类:对客户暴露的入口,负责定义工厂的功能
具体工厂类:
实现
- 抽象产品类(接口)
Machine.java
/**
* @author heshiyuan
* @description <p></p>
* @path java-design-pattern/com.hsy.java.design.pattern.method
* @date 2018/1/30 15:58
* @github http://github.com/shiyuan2he
* @email shiyuan4work@sina.com
* Copyright (c) 2018 shiyuan4work@sina.com All rights reserved.
* @price ¥5 微信:hewei1109
*/
public interface Machine {
// 车发动起来
void start() ;
// 车加速度
void speed() ;
// 车制动
void stop() ;
}
- 具体产品类
Car.java
/**
* @author heshiyuan
* @description <p></p>
* @path java-design-pattern/com.hsy.java.design.pattern.method.product
* @date 2018/1/30 16:03
* @github http://github.com/shiyuan2he
* @email shiyuan4work@sina.com
* Copyright (c) 2018 shiyuan4work@sina.com All rights reserved.
* @price ¥5 微信:hewei1109
*/
public class Car implements Machine{
private final Logger _logger = LoggerFactory.getLogger(this.getClass()) ;
@Override
public void start() {
_logger.info("汽车启动啦");
}
@Override
public void speed() {
_logger.info("汽车加速啦");
}
public void blow() {
_logger.info("汽车按铃啦");
}
@Override
public void stop() {
_logger.info("汽车停车啦");
}
}
Truck.java
/**
* @author heshiyuan
* @description <p></p>
* @path java-design-pattern/com.hsy.java.design.pattern.method.product
* @date 2018/1/30 16:04
* @github http://github.com/shiyuan2he
* @email shiyuan4work@sina.com
* Copyright (c) 2018 shiyuan4work@sina.com All rights reserved.
* @price ¥5 微信:hewei1109
*/
public class Truck implements Machine{
private final Logger _logger = LoggerFactory.getLogger(this.getClass()) ;
@Override
public void start() {
_logger.info("卡车启动啦");
}
@Override
public void speed() {
_logger.info("卡车加速啦");
}
public void blow() {
_logger.info("卡车按铃啦");
}
@Override
public void stop() {
_logger.info("卡车停车啦");
}
}
Bike.java
/**
* @author heshiyuan
* @description <p></p>
* @path java-design-pattern/com.hsy.java.design.pattern.method.product
* @date 2018/1/30 16:04
* @github http://github.com/shiyuan2he
* @email shiyuan4work@sina.com
* Copyright (c) 2018 shiyuan4work@sina.com All rights reserved.
* @price ¥5 微信:hewei1109
*/
public class Bike implements Machine{
private final Logger _logger = LoggerFactory.getLogger(this.getClass()) ;
@Override
public void start() {
_logger.info("自行车启动啦");
}
@Override
public void speed() {
_logger.info("自行车加速啦");
}
@Override
public void stop() {
_logger.info("自行车停车啦");
}
}
- 抽象工厂类
Factory.java
/**
* @author heshiyuan
* @description <p></p>
* @path java-design-pattern/com.hsy.java.design.pattern.method
* @date 2018/1/30 15:57
* @github http://github.com/shiyuan2he
* @email shiyuan4work@sina.com
* Copyright (c) 2018 shiyuan4work@sina.com All rights reserved.
* @price ¥5 微信:hewei1109
*/
public abstract class Factory {
public abstract <T extends Machine> T createMachine(Class<T> c);
}
MachineFactory.java
/**
* @author heshiyuan
* @description <p></p>
* @path java-design-pattern/com.hsy.java.design.pattern.method
* @date 2018/1/30 16:01
* @github http://github.com/shiyuan2he
* @email shiyuan4work@sina.com
* Copyright (c) 2018 shiyuan4work@sina.com All rights reserved.
* @price ¥5 微信:hewei1109
*/
public class MachineFactory extends Factory{
@Override
public <T extends Machine> T createMachine(Class<T> c) {
Machine machine = null ;
try {
machine = (Machine) Class.forName(c.getName()).newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return (T) machine;
}
}
- 测试
/**
* @author heshiyuan
* @description <p></p>
* @path java-design-pattern/com.hsy.java.design.pattern.method.client
* @date 2018/1/30 16:13
* @github http://github.com/shiyuan2he
* @email shiyuan4work@sina.com
* Copyright (c) 2018 shiyuan4work@sina.com All rights reserved.
* @price ¥5 微信:hewei1109
*/
public class ClientTest {
@Test
public void createMachine(){
// 打包给客户的集装箱
List<Machine> list = new ArrayList<>() ;
/**
* 实例化一个造车工厂
*/
Factory factory = new MachineFactory() ;
/**
* 1.客户下订单,开始造车(2辆汽车,2两卡车,2两自行车)
*/
// 造汽车生产线
for(int i=0;i<2;i++){
list.add(factory.createMachine(Car.class));
}
// 造卡车生产线
for(int i=0;i<2;i++){
list.add(factory.createMachine(Truck.class));
}
// 造自行车生产线
for(int i=0;i<2;i++){
list.add(factory.createMachine(Bike.class));
}
//客户开始测验每辆车的性能
list.forEach(ele -> {
ele.start() ;
ele.speed();
ele.stop();
if(ele instanceof Car){
((Car) ele).blow();
}
if(ele instanceof Truck){
((Truck) ele).blow();
}
});
}
}
优点
- 良好的封装行,代码结构清晰
工厂方法模式的扩展性非常优秀。
增加产品类的情况下,只要适当地修改具体的工厂类或扩展一个工厂类,就可以完成“拥抱变化”
屏蔽产品类
产品类的实现如果变化,调用者不需要关心,它只需要关心产品的接口,只要接口保持不变,系统中的上层模块就不要发生变化。
因为产品中的实例化工作是由工厂类负责的。工厂方法模式是典型的解耦框架。
高层模块需要知道产品的抽象类,其他的实现类都不用关心,符合迪米特法则-我不需要的就不要去交流
也符合依赖倒置原则-只依赖产品类的抽象;
也符合里氏替换原则-使用产品子类替换产品父类;
使用场景
- 工厂方法模式是new一个对象的替代品,所以在所有需要生成对象的地方都可以使用,但是需要慎重的考虑是否要增加一个工厂类进行管理,增加代码的复杂度;
需要灵活的、可扩展的框架时,可以考虑采用工厂方法模式。
万物皆对象,那万物也就皆产品类,例如需要设计一个连接邮件服务器的框架,有三种网络协议可供选择:pop3、imap、http,我们就可以把这三种
连接方法作为产品类,定义一个接口,然后定义对邮件的操作方法,用不同的方法实现三个具体的产品类,再定义一个工厂方法,按照不同的传入条件,
选择不同的连接方式。如此设计,可以做到完美的扩展,如果某些邮件服务器提供了WebService接口,我们只要增加一个产品类就可以了。工厂方法模式可以用在异构项目中。
例如,通过webservice与一个非Java的项目交互,虽然WebService号称是可以做到异构系统的同构化,但是在实际的开发中,还是会碰到很多问题,
如类型问题、wsdl文件的支持问题等。从WSDL中产生的对象都认为是一个产品,然后由一个具体的工厂类进行管理,减少与外围系统的耦合。- 可以使用在测试驱动开发的框架下。