话接上回【让代码变美的第三天 - 简单工厂模式】
简单工厂其实还是不够完美,破坏了程序的开放封闭,那么看下工厂方法模式如何解决
丑陋的模样
简单工厂代码
public static Fruit getFruit(String name) {
if ("苹果".equals(name)) {
Apple apple = new Apple();
// 洗苹果 + 切苹果
// todo other init
return apple;
} else if ("菠萝".equals(name)) {
Pineapple pineapple = new Pineapple();
// 切菠萝
// todo other init
return pineapple;
} else {
throw new RuntimeException("没有找到该水果");
}
}
重构
抽象工厂
public interface FruitFacory {
Fruit getFruit();
}
定义具体工厂
public class AppleFacory implements FruitFacory {
@Override
public Fruit getFruit() {
Apple apple = new Apple();
// 洗苹果 + 切苹果
// todo other init
return apple;
}
}
public class PineappleFacory implements FruitFacory {
@Override
public Fruit getFruit() {
Pineapple pineapple = new Pineapple();
// 切菠萝
// todo other init
return pineapple;
}
}
使用
public void sendApple() {
AppleFacory appleFacory = new AppleFacory();
Fruit apple = appleFacory.getFruit();
// Fruit apple = FruitFacory.getFruit("苹果");
sendPeople(apple);
}
public void eatApple() {
AppleFacory appleFacory = new AppleFacory();
Fruit apple = appleFacory.getFruit();
// Fruit apple = FruitFacory.getFruit("苹果");
eatPeople(apple);
}
public void eatPineapple() {
AppleFacory appleFacory = new AppleFacory();
Fruit pineapple = appleFacory.getFruit();
// Fruit pineapple = FruitFacory.getFruit("菠萝");
eat(apple);
}
注释掉的是简单工厂的获取方式,可以简单进行对比,工厂方法模式无非就是对每个产品包装了一层,这一层就是具体的工厂
UML图(网上借用的)如下:
新问题
每次获取产品的时候,都要精确知道是哪个工厂生产的,不管要记住产品的名字,还要记住工厂的名字,这不是给工作增加负担么?
有没有一种办法,使得客户不用关心具体是哪个工厂生产的呢?就和简单工厂模式一样简单呢?
再次重构
只讨论JAVA场景,Spring项目,利用Spring优雅的解决上面的问题。
其实在【让代码变美的第一天 - 观察者模式】中也是使用类似这种办法。
重新定义产品名(简单工厂中使用的产品名)
public interface FruitFacory {
String getFruitName();
Fruit getFruit();
}
@Component
public class AppleFacory implements FruitFacory {
@Override
public Fruit getFruit() {
Apple apple = new Apple();
// 洗苹果 + 切苹果
// todo other init
return apple;
}
@Override
public String getFruitName() {
return "苹果";
}
}
@Component
public class PineappleFacory implements FruitFacory {
@Override
public Fruit getFruit() {
Pineapple pineapple = new Pineapple();
// 切菠萝
// todo other init
return pineapple;
}
@Override
public String getFruitName() {
return "菠萝";
}
}
- 产品名也可以使用【让代码变美的第一天 - 观察者模式】中的注解来实现,注解或许x格高一点。
构建产品名和工厂的映射关系
@Service
public class FruitService {
public static Map<String, FruitFacory> fruitFactoryMap = new HashMap<>();
@Autowired
private List<FruitFacory> fruitFactoryList;
@PostConstruct
private void init() {
for (FruitFacory fruitFacory : fruitFactoryList) {
fruitFactoryMap.put(fruitFacory.getFruitName(), fruitFacory);
}
}
public static Fruit getFruit(String fruitName) {
return fruitFactoryMap.get(fruitName).getFruit();
}
}
使用
public void sendApple() {
Fruit apple = FruitService.getFruit("苹果");
// AppleFacory appleFacory = new AppleFacory();
// Fruit apple = appleFacory.getFruit();
// Fruit apple = FruitFacory.getFruit("苹果");
// sendPeople(apple);
}
public void eatApple() {
Fruit apple = FruitService.getFruit("苹果");
// AppleFacory appleFacory = new AppleFacory();
// Fruit apple = appleFacory.getFruit();
// Fruit apple = FruitFacory.getFruit("苹果");
// eatPeople(apple);
}
看这里是不是又和简单工厂模式差不多了,外部不用关心产品内部细节,也不需要关心需要用哪个工厂,只要通过名称获取到产品即可
再来看有没有破坏开放封闭原则,我们新增一个产品,比如:香蕉。
- 只需要新增Banana(产品)、BananaFacory(工厂)。
Spring帮我们解决的是扫描所有工厂类(当然我们自己利用反射也能做),让我们无需在新增工厂的时候去加if else