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");
}
}
定义接口和实现:
![](https://img.haomeiwen.com/i16830368/caa0043e72f54866.png)
如上图所示:一个接口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");
}
}
接口与实现如下图所示:
![](https://img.haomeiwen.com/i16830368/8398f97167e87cdf.png)
一个接口返回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();
}
定义工厂接口实现:
/**
* 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");
}
}
/**
* 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;
/**
* 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 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)符合单一职责,一个类或方法只负责一件事。
缺点:
引入了很多的子类,代码变得复杂。
网友评论