抽象工厂开始之前,我不得不先说一句,关于工厂方法,我以为不太常用来着。而实际上,想到当年在学校,老师在教三层的时候告诉我们,service 与 dao 层必须都要继承自接口,并提供一个实现类,只是神秘的一笑告诉我们,这对代码有好处,然而一直到了学期结束也不知道有什么好处。
大概也不知道什么时间,代码越写越多,也是知道了是什么好处,service 是为了 AOP (代理模式的思想),dao 是为了可扩展性,如果中途换了数据库或者框架,我可以重新写一个实现 dao 的接口类,来保证之在 spring 中只需要更改实现类路径,就可以使程序正常运行。
然而,很长一段时间我并没有反映过来这就是工厂方法,直到今天,我自己想可能是因为 spring 一般会把 dao 作为要生产的组件来看待,而 dao 里面的操作也确实不怎么去能人一个工厂的感觉,所以,工厂方法还是很常用的,常用到我自己都不知道我在用,一般情况下的工厂方法会是这样
package dao;
public interface UserDao {
}
package dao.jdbc;
import dao.UserDao;
public class UserDaoImpl implements UserDao {
}
package dao.mybatis;
import dao.UserDao;
public class UserDaoImpl implements UserDao {
}
其中一些 get 方法看起来也算是“生产”了一些组件吧。不过这样的话基本上就是抛弃了对产品那方面应该是一个工厂去对应一个实体,一般情况下 UserDao 中只能获得 User 实体。
抽象工厂
首先,抽象工厂,大概就是可以去处理一组产品吧,一个抽象工厂中可以去生产多个组件,如果直接在工程方法里的代码改的话应该就是这样,感觉像吃了什么奇怪的东西一样难受。
public interface FoodFactory {
Food getFood(String name);
Juice getJuice(String name);
}
如果需要增加某个产品的话肯定需要去改工厂接口。
差不多就是这个样子,抽象工厂就是工厂方法可以生产多个类型,一般情况下的用法是,抽象工厂中是定义了一组接口,在接口定义好之后,就可以将这个接口拿去使用,或者是将一个实例化后的对象作为一个参数去传入所需要这个方法中,所以,抽象方法应该这样做。
首先,如果有一个负责生产一日三餐的工厂。
public interface EatFactory {
Food getBreakfast(MealFactory mealFactory);
Food getLunch(MealFactory mealFactory);
Food getDinner(MealFactory mealFactory);
}
(原谅我想不出来别的什么单词做类名了)
可以看到的是,吃饭的话自然需要一个吃饭的地点,这里就是 MealFactory 了
public interface MealFactory {
// 主食
StapleFood getStapleFood(String name);
// 果汁
Juice getJuice(String name);
// 汤
Soup getSoup(String name);
// 菜
Dish getDish(String name);
}
从某种程度上来说,抽象工厂已经设计好了,不去关心它的具体实现,直接某个人就要在家里吃饭了。
Human human = new Human();
MealFactory mealFactory = new HomeMealFactory();
human.eat(mealFactory.getStapleFood("馒头"));
human.eat(mealFactory.getStapleFood("馒头"));
human.eat(mealFactory.getDish("番茄鸡蛋"));
看起来和工厂方法差不多,只是可以获得不同的数据类型而已,然而这样,依旧是在调用部分来做了一些组合,并不太好,所以,就有了第一个复制一日三餐的菜单工厂类,下面简单做一下实现
public class NormalEatFactory implements EatFactory {
@Override
public Food getBreakfast(MealFactory mealFactory) {
Food food = new Food();
food.addSoup(mealFactory.getSoup("米汤"));
food.addStapleFood(mealFactory.getStapleFood("面包"));
return food;
}
@Override
public Food getLunch(MealFactory mealFactory) {
Food food = new Food();
food.addStapleFood(mealFactory.getStapleFood("面条"));
food.addJuice(mealFactory.getJuice("橘子汁"));
return food;
}
@Override
public Food getDinner(MealFactory mealFactory) {
Food food = new Food();
food.addStapleFood(mealFactory.getStapleFood("馒头"));
food.addStapleFood(mealFactory.getStapleFood("馒头"));
food.addDish(mealFactory.getDish("番茄鸡蛋"));
return food;
}
}
这样的话如果想要吃饭,只需要
Human human = new Human();
EatFactory eatFactory = new NormalEatFactory();
human.eat(eatFactory.getDinner(new HomeMealFactory()));
就可以了,如果说抽象方法的好处,就是可以在实现类没有做出来之前,使用接口去写一部分代码,或者自己继承接口去写测试代码,弊端就是,如果接口设计比较糟糕的话,绝对不会累死一批程序员的。
最后做下三种工厂的总结
简单工厂:不实现工厂接口,提供一个或多个产品类型
工厂方法:实现工厂接口,提供一个产品类型
抽象工厂:实现工厂接口,提供多个产品类型
The end